From a2874ee2353de83ef5f6c3c7c0ba98b74c491b55 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Tue, 9 Jun 2020 00:01:58 +0200 Subject: [PATCH 01/48] Add in memory version of command bus not handling commands --- src/Contexts/Shared/domain/Command.ts | 7 ++++++ src/Contexts/Shared/domain/CommandBus.ts | 5 ++++ .../CommandBus/InMemoryCommandBus.ts | 9 +++++++ .../CommandBus/NoHandlerForMessageError.ts | 7 ++++++ .../CommandBus/InMemoryCommandBus.test.ts | 24 +++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 src/Contexts/Shared/domain/Command.ts create mode 100644 src/Contexts/Shared/domain/CommandBus.ts create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts create mode 100644 tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts new file mode 100644 index 0000000..c88751b --- /dev/null +++ b/src/Contexts/Shared/domain/Command.ts @@ -0,0 +1,7 @@ +export abstract class Command { + readonly commandName: string; + + constructor(commandName: string) { + this.commandName = commandName; + } +} \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts new file mode 100644 index 0000000..82e8096 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -0,0 +1,5 @@ +import { Command } from './Command'; + +export interface CommandBus { + dispatch(command: Command): void; +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts new file mode 100644 index 0000000..35d84b9 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -0,0 +1,9 @@ +import { CommandBus } from './../../domain/CommandBus'; +import { Command } from '../../domain/Command'; +import { NoHandlerForMessageError } from './NoHandlerForMessageError'; + +export class InMemoryCommandBus implements CommandBus { + dispatch(command: Command): void { + throw new NoHandlerForMessageError(command); + } +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts new file mode 100644 index 0000000..76f34b7 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts @@ -0,0 +1,7 @@ +import { Command } from '../../domain/Command'; + +export class NoHandlerForMessageError extends Error { + constructor(command: Command) { + super(`There is not handler for command of type ${command.commandName}`); + } +} \ No newline at end of file diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts new file mode 100644 index 0000000..9a8a25e --- /dev/null +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -0,0 +1,24 @@ +import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; +import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; +import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; + +class UnhandledCommand extends Command { + constructor() { + super('UnhandledCommandName'); + } +} + +describe('InMemoryCommandBus', () => { + it('throws an error if dispatches a command without handler', (done) => { + const unhandledCommand = new UnhandledCommand(); + const commandBus = new InMemoryCommandBus(); + + try { + commandBus.dispatch(unhandledCommand); + } catch (error) { + expect(error).toBeInstanceOf(NoHandlerForMessageError); + expect(error.message).toBe('There is not handler for command of type UnhandledCommandName'); + done(); + } + }); +}); From 762b037c131258fcf623981d935eeefe7d9149f5 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Wed, 10 Jun 2020 17:27:28 +0200 Subject: [PATCH 02/48] WIP --- src/Contexts/Shared/domain/Command.ts | 6 +--- src/Contexts/Shared/domain/CommandHandler.ts | 7 ++++ .../CommandBus/InMemoryCommandBus.ts | 36 +++++++++++++++++-- .../CommandBus/InMemoryCommandBus.test.ts | 27 ++++++++++++-- 4 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 src/Contexts/Shared/domain/CommandHandler.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts index c88751b..f2c1ef2 100644 --- a/src/Contexts/Shared/domain/Command.ts +++ b/src/Contexts/Shared/domain/Command.ts @@ -1,7 +1,3 @@ export abstract class Command { - readonly commandName: string; - - constructor(commandName: string) { - this.commandName = commandName; - } + static COMMAND_NAME: string; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts new file mode 100644 index 0000000..4296bd7 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -0,0 +1,7 @@ +import { Command } from "./Command"; + +export interface CommandHandler { + subscribedTo(): T; + + handle(command: T): void; +} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 35d84b9..349e2be 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,9 +1,41 @@ import { CommandBus } from './../../domain/CommandBus'; import { Command } from '../../domain/Command'; import { NoHandlerForMessageError } from './NoHandlerForMessageError'; +import { CommandHandler } from '../../domain/CommandHandler'; + +export class CommandHandlersInformation { + private commandHandlersMap: Map>; + + constructor(commandHandlers: Array>) { + this.commandHandlersMap = this.formatHandlers(commandHandlers); + } + + private formatHandlers(commandHandlers: Array>) { + const handlersMap = new Map(); + + commandHandlers.forEach(commandHandler => { + handlersMap.set(commandHandler.subscribedTo(), commandHandler); + }); + + return handlersMap; + } + + public getHandler(command: Command) { + return this.commandHandlersMap.get(command.constructor.name); + } +} export class InMemoryCommandBus implements CommandBus { + constructor(private commandHandlersInformation: CommandHandlersInformation) { + } + dispatch(command: Command): void { - throw new NoHandlerForMessageError(command); + const handler = this.commandHandlersInformation.getHandler(command); + + if (!handler) { + throw new NoHandlerForMessageError(command); + } + + handler.handle(command); } -} \ No newline at end of file +} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 9a8a25e..bf55669 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -3,9 +3,19 @@ import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { - constructor() { - super('UnhandledCommandName'); - } + static COMMAND_NAME = 'unhandled.command'; +} + +class HandledCommand extends Command { + static COMMAND_NAME = 'handled.command'; +} + +interface CommandHandler { + handle(command: Command): void; +} + +class MyCommandHandler implements CommandHandler { + handle(command: HandledCommand): void {} } describe('InMemoryCommandBus', () => { @@ -21,4 +31,15 @@ describe('InMemoryCommandBus', () => { done(); } }); + + it('accepts a command with handler', done => { + const handledCommand = new HandledCommand(); + const myCommandHandler = new MyCommandHandler(); + const commandBus = new InMemoryCommandBus([myCommandHandler]); + + try { + commandBus.dispatch(handledCommand); + done(); + } catch (error) { } + }); }); From 71a896f8d1fcea6ae63b789a11f87eadce2b1d31 Mon Sep 17 00:00:00 2001 From: rsaladocid Date: Wed, 10 Jun 2020 18:05:56 +0200 Subject: [PATCH 03/48] wip: subscribe to symbol --- src/Contexts/Shared/domain/Command.ts | 4 +-- src/Contexts/Shared/domain/CommandHandler.ts | 9 +++--- .../CommandBus/CommandHandlersInformation.ts | 24 ++++++++++++++ .../CommandBus/InMemoryCommandBus.ts | 31 +++---------------- .../CommandBus/NoHandlerForMessageError.ts | 4 +-- .../CommandBus/InMemoryCommandBus.test.ts | 24 ++++++++------ 6 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts diff --git a/src/Contexts/Shared/domain/Command.ts b/src/Contexts/Shared/domain/Command.ts index f2c1ef2..9074445 100644 --- a/src/Contexts/Shared/domain/Command.ts +++ b/src/Contexts/Shared/domain/Command.ts @@ -1,3 +1 @@ -export abstract class Command { - static COMMAND_NAME: string; -} \ No newline at end of file +export abstract class Command {} diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts index 4296bd7..87d1c94 100644 --- a/src/Contexts/Shared/domain/CommandHandler.ts +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -1,7 +1,6 @@ -import { Command } from "./Command"; +import { Command } from './Command'; export interface CommandHandler { - subscribedTo(): T; - - handle(command: T): void; -} \ No newline at end of file + subscribedTo(): T; + handle(command: T): void; +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts new file mode 100644 index 0000000..531c872 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts @@ -0,0 +1,24 @@ +import { Command } from '../../domain/Command'; +import { CommandHandler } from '../../domain/CommandHandler'; + +export class CommandHandlersInformation { + private commandHandlersMap: Map>; + + constructor(commandHandlers: Array>) { + this.commandHandlersMap = this.formatHandlers(commandHandlers); + } + + private formatHandlers(commandHandlers: Array>): Map> { + const handlersMap = new Map(); + + commandHandlers.forEach(commandHandler => { + handlersMap.set(commandHandler.subscribedTo(), commandHandler); + }); + + return handlersMap; + } + + public getCommandHandler(command: Command): CommandHandler | undefined { + return this.commandHandlersMap.get(command.constructor); + } +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 349e2be..fc91594 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,36 +1,13 @@ -import { CommandBus } from './../../domain/CommandBus'; import { Command } from '../../domain/Command'; +import { CommandBus } from './../../domain/CommandBus'; +import { CommandHandlersInformation } from './CommandHandlersInformation'; import { NoHandlerForMessageError } from './NoHandlerForMessageError'; -import { CommandHandler } from '../../domain/CommandHandler'; - -export class CommandHandlersInformation { - private commandHandlersMap: Map>; - - constructor(commandHandlers: Array>) { - this.commandHandlersMap = this.formatHandlers(commandHandlers); - } - - private formatHandlers(commandHandlers: Array>) { - const handlersMap = new Map(); - - commandHandlers.forEach(commandHandler => { - handlersMap.set(commandHandler.subscribedTo(), commandHandler); - }); - - return handlersMap; - } - - public getHandler(command: Command) { - return this.commandHandlersMap.get(command.constructor.name); - } -} export class InMemoryCommandBus implements CommandBus { - constructor(private commandHandlersInformation: CommandHandlersInformation) { - } + constructor(private commandHandlersInformation: CommandHandlersInformation) {} dispatch(command: Command): void { - const handler = this.commandHandlersInformation.getHandler(command); + const handler = this.commandHandlersInformation.getCommandHandler(command); if (!handler) { throw new NoHandlerForMessageError(command); diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts index 76f34b7..4a3d932 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts @@ -2,6 +2,6 @@ import { Command } from '../../domain/Command'; export class NoHandlerForMessageError extends Error { constructor(command: Command) { - super(`There is not handler for command of type ${command.commandName}`); + super(`There is not handler for command of type ${command.constructor.name}`); } -} \ No newline at end of file +} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index bf55669..2adc25f 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -1,5 +1,7 @@ -import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; +import { CommandHandler } from '../../../../../src/Contexts/Shared/domain/CommandHandler'; +import { CommandHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation'; +import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { @@ -10,24 +12,25 @@ class HandledCommand extends Command { static COMMAND_NAME = 'handled.command'; } -interface CommandHandler { - handle(command: Command): void; -} +class MyCommandHandler implements CommandHandler { + subscribedTo(): HandledCommand { + return HandledCommand; + } -class MyCommandHandler implements CommandHandler { handle(command: HandledCommand): void {} } describe('InMemoryCommandBus', () => { - it('throws an error if dispatches a command without handler', (done) => { + it('throws an error if dispatches a command without handler', done => { const unhandledCommand = new UnhandledCommand(); - const commandBus = new InMemoryCommandBus(); + const commandHandlersInformation = new CommandHandlersInformation([]); + const commandBus = new InMemoryCommandBus(commandHandlersInformation); try { commandBus.dispatch(unhandledCommand); } catch (error) { expect(error).toBeInstanceOf(NoHandlerForMessageError); - expect(error.message).toBe('There is not handler for command of type UnhandledCommandName'); + expect(error.message).toBe('There is not handler for command of type UnhandledCommand'); done(); } }); @@ -35,11 +38,12 @@ describe('InMemoryCommandBus', () => { it('accepts a command with handler', done => { const handledCommand = new HandledCommand(); const myCommandHandler = new MyCommandHandler(); - const commandBus = new InMemoryCommandBus([myCommandHandler]); + const commandHandlersInformation = new CommandHandlersInformation([myCommandHandler]); + const commandBus = new InMemoryCommandBus(commandHandlersInformation); try { commandBus.dispatch(handledCommand); done(); - } catch (error) { } + } catch (error) {} }); }); From 2d3309cad85198ca0e1b67298d2d9c38ede42673 Mon Sep 17 00:00:00 2001 From: David Matas Date: Thu, 11 Jun 2020 19:41:18 +0200 Subject: [PATCH 04/48] Add frontend backoffice skeleton --- package-lock.json | 84 +++++++++++-------- package.json | 3 + src/apps/backoffice/frontend/app.ts | 24 ++++++ src/apps/backoffice/frontend/config/config.ts | 14 ++++ .../backoffice/frontend/config/default.json | 1 + .../dependency-injection/application.yaml | 7 ++ .../dependency-injection/application_dev.yaml | 2 + .../application_production.yaml | 2 + .../application_staging.yaml | 2 + .../application_test.yaml | 2 + .../config/dependency-injection/index.ts | 9 ++ .../backoffice/frontend/config/staging.json | 1 + src/apps/backoffice/frontend/config/test.json | 1 + .../frontend/controllers/HomeGetController.ts | 7 ++ .../backoffice/frontend/routes/home.route.ts | 8 ++ src/apps/backoffice/frontend/routes/index.ts | 12 +++ src/apps/backoffice/frontend/server.ts | 22 +++++ src/apps/backoffice/frontend/views/index.ejs | 1 + 18 files changed, 165 insertions(+), 37 deletions(-) create mode 100644 src/apps/backoffice/frontend/app.ts create mode 100644 src/apps/backoffice/frontend/config/config.ts create mode 100644 src/apps/backoffice/frontend/config/default.json create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/application.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/application_dev.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/application_production.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/application_staging.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/application_test.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/index.ts create mode 100644 src/apps/backoffice/frontend/config/staging.json create mode 100644 src/apps/backoffice/frontend/config/test.json create mode 100644 src/apps/backoffice/frontend/controllers/HomeGetController.ts create mode 100644 src/apps/backoffice/frontend/routes/home.route.ts create mode 100644 src/apps/backoffice/frontend/routes/index.ts create mode 100644 src/apps/backoffice/frontend/server.ts create mode 100644 src/apps/backoffice/frontend/views/index.ejs diff --git a/package-lock.json b/package-lock.json index cd2c9a2..3b25a29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -815,7 +815,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1402,7 +1401,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2181,6 +2179,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", + "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", + "requires": { + "jake": "^10.6.1" + } + }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -2316,8 +2322,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.12.0", @@ -2692,6 +2697,14 @@ "through2": "^2.0.1" } }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, "filewatcher": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", @@ -2847,8 +2860,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -2872,15 +2884,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2897,22 +2907,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3043,8 +3050,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3058,7 +3064,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3075,7 +3080,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3084,15 +3088,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3113,7 +3115,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3202,8 +3203,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3217,7 +3217,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3313,8 +3312,7 @@ "version": "5.1.2", "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3356,7 +3354,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3378,7 +3375,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3427,15 +3423,13 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, @@ -3632,8 +3626,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-glob": { "version": "0.1.1", @@ -4321,6 +4314,24 @@ "handlebars": "^4.1.2" } }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + } + } + }, "jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", @@ -7674,7 +7685,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } diff --git a/package.json b/package.json index 556e522..20b819c 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,12 @@ }, "scripts": { "dev": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/mooc_backend/server.ts", + "dev:backoffice:frontend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/backoffice/frontend/server.ts", "test": "npm run test:unit && npm run test:features", "test:unit": "NODE_ENV=test jest", "test:features": "NODE_ENV=test cucumber-js -p default", "start": "NODE_ENV=production node dist/src/apps/mooc_backend/server", + "start:backoffice:frontend": "NODE_ENV=production node dist/src/apps/backoffice/frontend/server", "build": "npm run build:clean && npm run build:tsc && npm run build:di", "build:tsc": "tsc -p tsconfig.prod.json", "build:di": "copy 'src/**/*.yaml' dist/src", @@ -34,6 +36,7 @@ "compression": "^1.7.4", "convict": "^5.1.0", "copy": "^0.3.2", + "ejs": "^3.1.3", "errorhandler": "^1.5.1", "express": "^4.17.1", "glob": "^7.1.6", diff --git a/src/apps/backoffice/frontend/app.ts b/src/apps/backoffice/frontend/app.ts new file mode 100644 index 0000000..2e67d71 --- /dev/null +++ b/src/apps/backoffice/frontend/app.ts @@ -0,0 +1,24 @@ +import bodyParser from 'body-parser'; +import express from 'express'; +import helmet from 'helmet'; +import compress from 'compression'; +import { registerRoutes } from './routes'; +import path from 'path'; + +const app: express.Express = express(); + +app.set('port', process.env.PORT || 8032); +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, '/views')); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(helmet.xssFilter()); +app.use(helmet.noSniff()); +app.use(helmet.hidePoweredBy()); +app.use(helmet.frameguard({ action: 'deny' })); +app.use(compress()); + +registerRoutes(app); + +export default app; diff --git a/src/apps/backoffice/frontend/config/config.ts b/src/apps/backoffice/frontend/config/config.ts new file mode 100644 index 0000000..4405a32 --- /dev/null +++ b/src/apps/backoffice/frontend/config/config.ts @@ -0,0 +1,14 @@ +import convict from 'convict'; + +const convictConfig = convict({ + env: { + doc: 'The application environment.', + format: ['production', 'development', 'staging', 'test'], + default: 'default', + env: 'NODE_ENV' + } +}); + +convictConfig.loadFile([__dirname + '/default.json', __dirname + '/' + convictConfig.get('env') + '.json']); + +export default convictConfig; diff --git a/src/apps/backoffice/frontend/config/default.json b/src/apps/backoffice/frontend/config/default.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/apps/backoffice/frontend/config/default.json @@ -0,0 +1 @@ +{} diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml new file mode 100644 index 0000000..22d0a1d --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -0,0 +1,7 @@ +services: + Contexts.shared.Logger: + class: ../../../../../Contexts/Shared/infrastructure/WinstonLogger + arguments: [] + + Apps.Backoffice.Frontend.controllers.HomeGetController: + class: ../../controllers/HomeGetController diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application_dev.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application_dev.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/application_dev.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application_production.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application_production.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/application_production.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application_staging.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application_staging.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/application_staging.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application_test.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application_test.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/application_test.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/index.ts b/src/apps/backoffice/frontend/config/dependency-injection/index.ts new file mode 100644 index 0000000..27d2a35 --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/index.ts @@ -0,0 +1,9 @@ +import { ContainerBuilder, YamlFileLoader } from 'node-dependency-injection'; + +const container = new ContainerBuilder(); +const loader = new YamlFileLoader(container); +const env = process.env.NODE_ENV || 'dev'; + +loader.load(`${__dirname}/application_${env}.yaml`); + +export default container; diff --git a/src/apps/backoffice/frontend/config/staging.json b/src/apps/backoffice/frontend/config/staging.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/apps/backoffice/frontend/config/staging.json @@ -0,0 +1 @@ +{} diff --git a/src/apps/backoffice/frontend/config/test.json b/src/apps/backoffice/frontend/config/test.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/apps/backoffice/frontend/config/test.json @@ -0,0 +1 @@ +{} diff --git a/src/apps/backoffice/frontend/controllers/HomeGetController.ts b/src/apps/backoffice/frontend/controllers/HomeGetController.ts new file mode 100644 index 0000000..af580c3 --- /dev/null +++ b/src/apps/backoffice/frontend/controllers/HomeGetController.ts @@ -0,0 +1,7 @@ +import { Request, Response } from 'express'; + +export class HomeGetController { + run(req: Request, res: Response) { + res.render('index', { foo: 'FOO' }); + } +} diff --git a/src/apps/backoffice/frontend/routes/home.route.ts b/src/apps/backoffice/frontend/routes/home.route.ts new file mode 100644 index 0000000..6eacd33 --- /dev/null +++ b/src/apps/backoffice/frontend/routes/home.route.ts @@ -0,0 +1,8 @@ +import { Express } from 'express'; +import container from '../config/dependency-injection'; +import { HomeGetController } from '../controllers/HomeGetController'; + +export const register = (app: Express) => { + const controller: HomeGetController = container.get('Apps.Backoffice.Frontend.controllers.HomeGetController'); + app.get('/', controller.run.bind(controller)); +}; diff --git a/src/apps/backoffice/frontend/routes/index.ts b/src/apps/backoffice/frontend/routes/index.ts new file mode 100644 index 0000000..56b36c7 --- /dev/null +++ b/src/apps/backoffice/frontend/routes/index.ts @@ -0,0 +1,12 @@ +import { Express } from 'express'; +import glob from 'glob'; + +export function registerRoutes(app: Express) { + const routes = glob.sync(__dirname + '/**/*.route.*'); + routes.map(route => register(route, app)); +} + +function register(routePath: string, app: Express) { + const route = require(routePath); + route.register(app); +} diff --git a/src/apps/backoffice/frontend/server.ts b/src/apps/backoffice/frontend/server.ts new file mode 100644 index 0000000..76b7b0a --- /dev/null +++ b/src/apps/backoffice/frontend/server.ts @@ -0,0 +1,22 @@ +import errorHandler from 'errorhandler'; +import app from './app'; +import container from './config/dependency-injection'; + +/** + * Error Handler. Provides full stack - remove for production + */ +app.use(errorHandler()); + +/** + * Start Express server. + */ +const server = app.listen(app.get('port'), () => { + const winstonLogger = container.get('Contexts.shared.Logger'); + + winstonLogger.info( + ` Backoffice frontend is running at http://localhost:${app.get('port')} in ${app.get('env')} mode` + ); + console.log(' Press CTRL-C to stop\n'); +}); + +export default server; diff --git a/src/apps/backoffice/frontend/views/index.ejs b/src/apps/backoffice/frontend/views/index.ejs new file mode 100644 index 0000000..e965047 --- /dev/null +++ b/src/apps/backoffice/frontend/views/index.ejs @@ -0,0 +1 @@ +Hello From 795e8d629473494eb946b7d50c3705533f0428e3 Mon Sep 17 00:00:00 2001 From: David Matas Date: Thu, 11 Jun 2020 20:21:46 +0200 Subject: [PATCH 05/48] Added home partially --- package-lock.json | 200 ++++++++++++++---- package.json | 3 +- src/apps/backoffice/frontend/app.ts | 12 +- .../frontend/controllers/HomeGetController.ts | 5 +- .../frontend/public/images/logo.png | Bin 0 -> 4186 bytes .../backoffice/frontend/templates/master.html | 34 +++ .../frontend/templates/pages/home.html | 7 + .../frontend/templates/partials/footer.html | 7 + .../frontend/templates/partials/header.html | 28 +++ src/apps/backoffice/frontend/views/index.ejs | 1 - 10 files changed, 256 insertions(+), 41 deletions(-) create mode 100644 src/apps/backoffice/frontend/public/images/logo.png create mode 100644 src/apps/backoffice/frontend/templates/master.html create mode 100644 src/apps/backoffice/frontend/templates/pages/home.html create mode 100644 src/apps/backoffice/frontend/templates/partials/footer.html create mode 100644 src/apps/backoffice/frontend/templates/partials/header.html delete mode 100644 src/apps/backoffice/frontend/views/index.ejs diff --git a/package-lock.json b/package-lock.json index 3b25a29..5aa7ea8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -655,6 +655,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.1.tgz", "integrity": "sha512-hx6zWtudh3Arsbl3cXay+JnkvVgCKzCWKv42C9J01N2T2np4h8w5X8u6Tpz5mj38kE3M9FM0Pazx8vKFFMnjLQ==" }, + "@types/nunjucks": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.3.tgz", + "integrity": "sha512-42IiIIBdoB7ZDwCVhCWYT4fMCj+4TeacuVgh7xyT2du5EhkpA+OFeeDdYTFCUt1MrHb8Aw7ZqFvr8s1bwP9l8w==", + "dev": true + }, "@types/range-parser": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", @@ -734,6 +740,11 @@ "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", "dev": true }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, "abab": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", @@ -815,6 +826,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -920,6 +932,11 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1144,6 +1161,12 @@ "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", "dev": true }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "optional": true + }, "bl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", @@ -1401,12 +1424,107 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -2179,14 +2297,6 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "ejs": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.3.tgz", - "integrity": "sha512-wmtrUGyfSC23GC/B1SMv2ogAUgbQEtDmTIhfqielrG5ExIM9TP4UoYdi90jLF1aTcsWCJNEO0UrgKzP0y3nTSg==", - "requires": { - "jake": "^10.6.1" - } - }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -2322,7 +2432,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "1.12.0", @@ -2697,14 +2808,6 @@ "through2": "^2.0.1" } }, - "filelist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", - "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", - "requires": { - "minimatch": "^3.0.4" - } - }, "filewatcher": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", @@ -3626,7 +3729,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-glob": { "version": "0.1.1", @@ -3944,6 +4048,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -4314,24 +4427,6 @@ "handlebars": "^4.1.2" } }, - "jake": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", - "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", - "requires": { - "async": "0.9.x", - "chalk": "^2.4.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - } - } - }, "jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", @@ -6093,6 +6188,24 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nunjucks": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.1.tgz", + "integrity": "sha512-LYlVuC1ZNSalQQkLNNPvcgPt2M9FTY9bs39mTCuFXtqh7jWbYzhDlmz2M6onPiXEhdZo+b9anRhc+uBGuJZ2bQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "chokidar": "^3.3.0", + "commander": "^3.0.2" + }, + "dependencies": { + "commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" + } + } + }, "nwsapi": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", @@ -6397,6 +6510,11 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -6622,6 +6740,15 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", @@ -7685,6 +7812,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } diff --git a/package.json b/package.json index 20b819c..919d2a3 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "compression": "^1.7.4", "convict": "^5.1.0", "copy": "^0.3.2", - "ejs": "^3.1.3", "errorhandler": "^1.5.1", "express": "^4.17.1", "glob": "^7.1.6", @@ -45,6 +44,7 @@ "mandrill-api": "^1.0.45", "mongodb": "^3.5.2", "node-dependency-injection": "^2.4.2", + "nunjucks": "^3.2.1", "ts-node": "^8.3.0", "typescript": "^3.7.2", "uuid": "^3.3.3", @@ -56,6 +56,7 @@ "@types/cucumber": "^4.0.7", "@types/faker": "^4.1.5", "@types/jest": "^24.0.18", + "@types/nunjucks": "^3.1.3", "@types/supertest": "^2.0.8", "cucumber": "^6.0.5", "faker": "^4.1.0", diff --git a/src/apps/backoffice/frontend/app.ts b/src/apps/backoffice/frontend/app.ts index 2e67d71..1fb376c 100644 --- a/src/apps/backoffice/frontend/app.ts +++ b/src/apps/backoffice/frontend/app.ts @@ -4,13 +4,21 @@ import helmet from 'helmet'; import compress from 'compression'; import { registerRoutes } from './routes'; import path from 'path'; +import nunjucks from 'nunjucks'; const app: express.Express = express(); app.set('port', process.env.PORT || 8032); -app.set('view engine', 'ejs'); -app.set('views', path.join(__dirname, '/views')); +// Templates +app.set('view engine', 'html'); +nunjucks.configure(path.join(__dirname, '/templates'), { + autoescape: true, + express: app, + watch: true +}); + +app.use(express.static(path.join(__dirname, '/public'))); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(helmet.xssFilter()); diff --git a/src/apps/backoffice/frontend/controllers/HomeGetController.ts b/src/apps/backoffice/frontend/controllers/HomeGetController.ts index af580c3..163d665 100644 --- a/src/apps/backoffice/frontend/controllers/HomeGetController.ts +++ b/src/apps/backoffice/frontend/controllers/HomeGetController.ts @@ -2,6 +2,9 @@ import { Request, Response } from 'express'; export class HomeGetController { run(req: Request, res: Response) { - res.render('index', { foo: 'FOO' }); + res.render('pages/home', { + title: 'Welcome', + description: 'CodelyTV - Backoffice' + }); } } diff --git a/src/apps/backoffice/frontend/public/images/logo.png b/src/apps/backoffice/frontend/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..759395922beee82928cbb06c3bfe0d415ceb70f5 GIT binary patch literal 4186 zcmV-g5T);lP){7FPXRCwC#T~CM{Ss8z`EP{BL2`GA4 z?PA0|NG~bLE(oIOVD>08i@M&N$t6(~(u;>(5H>yP!37~56k&5o=cMc^+X3~GNV>gv z*d3{62lte*hFt_tnFhs!i}Cx$uhz0s_5RiOs%j?R2anFAtLwdb^}gTxec$`u_f{S} zc%Vc?L`05*JSJTrA|fJyB_bjsB7h|#A|ik#A|fIpfF&X#BEln&yIlOtdsoiD$N9p4 z-+$|qKe*m>!=HZl zp?#^(ePorrSy{2^a004Vp`5A04*;#*rWy@kh3$B&;%jWjC0pR)0Ib5`R~(9PjDaeG z4+N*x!dFY^i?t54ZwMb(OTM}Z?b~h(ut3MK-)2yH4R^@_lo<5)kaVl6e^7Sdx)HN3 zW1o*p#tX_E^myRJu%IJP!N+YVr!<$HYPgei^&yzO2JL@x3ilWh=$%xFH8Xh^^aK)OjdEn63DFD>C!c06J&?FrBelLJk zE>laeniH_iN)#8BW7Fb3Lna2<219gkALvU^Q{QFZ^bt^l?bX9SC1)R zCC8|%2Reqr6x11NrBFu(4gjo7WT6q0@J~>lgz`QN>TB?M1r+!n9UTlhKf>Gxta37~ zFb$VB3PXUfNdq>j@G)c#SFCGV$C*zi)V86%tnH?HW)AhnROdid?<6>&db8)WQPL_U zTUPy^`k)AO&if+w_V)7oDn9^)e!uSnF2R8pK@Zr_foAe{W?00odNnMY#sKTdTwULT z@>LiJ-+`C@yAA@@M%@l|0IXX!3P+&=BV`v@*rRe3Sr5NOU)#_n){B4<>$3@xU8TBA zZLz^ZC*-rT^JeAo@AX`65Y zmO+0Oz^<&wKpQ?R?C{^hOaCR5Pjtwn(G8vh7@)NV1O5I5G%KN;g1a8Pot7SDAS}R}rl6edt#H=2bzWzJRqJ5G3$n zFm25rU{LjI-s^tNc1I1(TsFR=nc|JGU)2p70*)Jj9x2h(Ib#OM(-pI|&%3PxhlOop zz#NPe??Qy`Inei%gnn-xX5rBAj-5h!9y1{N`4YtsvC;VpPf3;5S zuR9ZR`vz=meH*YkAU}w0jMJ8eX-pm3w@_}G7peyS!~(3)&$p8+*DL^I=)#^grhdf0 zVxk&=;CjCW=OCoj##$@e#IAIJgj;Z&`r+{skjk=(B2Zl{`OWKr6!k+;z7OSjP^@pm zK>EGo@3CD!F8tqZcpl7#dX!zlkkqd!-++!?p<-P&fD{7SLymf4Eco4q!R(!sn1Vge zIwKmmLruMD$#a@O8G&h}@$U^(^=^b!+EhNXn2dA^$q{5aUqS!hGO&6Hls@$4=g%O};0hO%kJ|o-+VxV&R~PAdUNo3QRkCbwuiv(AHo`$t z%3Oe$MmAK4`+8Ge;8ENWF2^bPb2BWJJW8JhXn{-bXXSNetGAzK%80tOfYDj~_ zv&J>q6>cF)IRNWag?|Xa>Xn8R7jxEp6Uv`q@O;Suv|b_5x^MYhp{TpB?`liK1Z=|- zh7NT`8m6;wNEvL+V4zOj{>mNOSU1O2Ti28~5fhVPi~I*mVa;Gdhj3feSt%SD*p=6Q zN88moRQB_fF1bnhn88FUGiQA=q;??+S~shYY-6b$+2Fu1;6gB%O^!kA5USzxZX+hP zeVP$mOwSv_3h;8gQ|e=#-i~}D^2-3L%H%`8*6oG?>wPG{a=89ARh?;c-*YfDWe)0= z(I*YpiHbR8P|U;uB&Uwa3`dsl%;((>>P}u1-Of3tw1DMfUYUgMx%6n0?%f>J$tF<- zSZGo`3*}OYlAi$MhE%lVG#eql3iiD)=NO@5siZ1f*zBvBm~<@7zcLRuwteTZM#ZGaHY9Y2I{6D z)3N15Fs}kc#S=SK`KOj~lAKq^Khv1Sy8Iuq{tEd4O~y^ub#C^4TS$A0Bv+boPZCudu7 zTO_uiY;mx}sBU9yobLA<(AmF2`5FwiuXuu0opwhp4Fp>yabVKDr9TYm1?oQxG%Y)U$Y&ymnAkRFcGI{9sMHb*XMRlV%4Du@bF`<;6v=ob@+BB# ze{2~vgRT(fqQMi*DwuAyE_JBOg;CU2Ob=6*^XxrrC$41b4)O3Y?`p?R?d|Oim?uHq za~yD;#H?dS7Vl1H?%m{C#cTtx{t4xWP<{df?O&>WSqlYN1Xf9=)bKY(p-)Oe2Dr=z z7e>KRr*(aX)ywCo-Y8efkYYR@Bfb};h zAE$rgs^hm{u-sF}Lk2a?!byRF;8OesDDMH8FsXrgZ~|6h$##-J^=T&t>I-h=^H~Mu z$R1G%l>L668?c&iTO@`FpM48Xn}Br=%9o++cQnhW39x+G)ldTpJyo(1xq?w}w0G$c zl$kOpQdxbKeHriwFs_L}2SQvxsgs^3m^1-cDBBu#HRfzcaHl&A#&@9ntixcXo1xAc z!XB!?3SZz=%_U3nzbK#kYs}9LOY>~Nq?l0~N`(CjuZ0Yuf4!Qkna?F=`5KU>ru8ktCQGRh#GQ52L@zi7HdZ=Ed&uKMvU~Vn|>cU`B zP5Z4gFBCGPBJ;S5eO{7jG-75YS?1QtwxYA0a=0JYq^Ma23$`~lJgi2F@wxJ9SxuM3IfwL?IQ=$o`sS{b{ z#FVMQV^gtCSrKp&-HNE;P}<9M|FNYBIx{Gy&Xj+H`fg4jl`$$C-xv3moJE6r;+o1j z$l<_`JYXRZ(CIR!{OKQ@4x>?alPoJ((?SC{TV#$*3rfF6N@>bYI;xrhHNKH8ViHqT zzP%<=jtLJDB;cOQ3dP2DBm~Y=Vt^^twdODx*eJRwY-3`nn9d;@{eFMuF=o1Qo}x=# z7$iCk(Fh;FI!_yokG8!${qD>A(764kfd=ZutvIKkE%+zyBRpT@c0&(^@Bynqg@f5- zMy@Kq11az<#j(aryd0oT9fO%_?zu|sx$IHpLOr~I5qX+rQv&%O^ZWPR!O?|GBCd6f zLN&t1hgFut9S@GUP30bd$9jVur>MTSa&36kvfY&|OEE>_dVp1yK8Bm#FO)tj=UC}e zhlEhfVSqZ1SZj&N?3Ig(?z0)7ozCFWQqsOsaoARKi(?B;9l+YPxTaCSvaD~{<%Sf@ z`CNd3ioTA>V(EUHvF?Q`->Q;--ZYS(VbtKnP};YiN&E8cOvv2koJ6?I!KN9Ovh0sx z$Oaez>&+*2kf~f($4jnLyIoD0DGic2)60Dh?m4iDfnrh;1UyVL99!0j%fHB@QV40rbT8Kn~d9m_g zLC0L8G&AR^D5#s(I1*AQvj2?<$j(e1|`Zva+&bye7AS zIWC#KQ8|wGxVeIbjZoM^RHA$dDb0+zzBu=+c{$A0nK0LxJ>Q_II=Q3(Sg5d9%C778 z`zt>8uLvAH%+B+(8NZxDIj_nXY~m9n>WV)CSPj2$k&`6_z{1I+EZkuMfE7z`HDnTs zh&)PSnG)##1i)%dRmWz{q*n#7L`1lOD)U=reww+m->Rfr9TULfvWUba6q8Wo2&~Yd zT(-z@PUj~?{h@??cPlQzQ=TK~VIGmq&msa=|6P@>T9KVns-Uvpt&yYsuIBa=>0x15 z!mvcR0n3zO17occ^}kpoWL2h3&%&+*umrF~f`C@4`1cQ<^W5+(A||1Th*r3_lw1(7 z62KA>2_m#*=mNv7mIPK4=~V$N5s{Ffg&S$wZaWoJ^%m!07*qoM6N<$g25l>LjV8( literal 0 HcmV?d00001 diff --git a/src/apps/backoffice/frontend/templates/master.html b/src/apps/backoffice/frontend/templates/master.html new file mode 100644 index 0000000..fd1e760 --- /dev/null +++ b/src/apps/backoffice/frontend/templates/master.html @@ -0,0 +1,34 @@ + + + + + + + + + {{title}} + {{description}} + + + {% include 'partials/header.html' %} + + {% if flash.message %} + + {% endif %} + +
+

{% block page_title %}{% endblock %}

+ {% block main %}{% endblock %} +
+ +
+ + {% include 'partials/footer.html' %} + + diff --git a/src/apps/backoffice/frontend/templates/pages/home.html b/src/apps/backoffice/frontend/templates/pages/home.html new file mode 100644 index 0000000..30a2e95 --- /dev/null +++ b/src/apps/backoffice/frontend/templates/pages/home.html @@ -0,0 +1,7 @@ +{% extends '../master.html' %} + +{% block page_title %}HOME{% endblock %} + +{% block main %} + HOLIII HOME +{% endblock %} diff --git a/src/apps/backoffice/frontend/templates/partials/footer.html b/src/apps/backoffice/frontend/templates/partials/footer.html new file mode 100644 index 0000000..1c7693f --- /dev/null +++ b/src/apps/backoffice/frontend/templates/partials/footer.html @@ -0,0 +1,7 @@ +
+
+

+ 🤙 CodelyTV - El mejor backoffice de la historia +

+
+
diff --git a/src/apps/backoffice/frontend/templates/partials/header.html b/src/apps/backoffice/frontend/templates/partials/header.html new file mode 100644 index 0000000..30fe251 --- /dev/null +++ b/src/apps/backoffice/frontend/templates/partials/header.html @@ -0,0 +1,28 @@ +
+ +
diff --git a/src/apps/backoffice/frontend/views/index.ejs b/src/apps/backoffice/frontend/views/index.ejs deleted file mode 100644 index e965047..0000000 --- a/src/apps/backoffice/frontend/views/index.ejs +++ /dev/null @@ -1 +0,0 @@ -Hello From 3c9c97f9106e91ef30285d399fb35bdcec4bed81 Mon Sep 17 00:00:00 2001 From: rsaladocid Date: Fri, 12 Jun 2020 16:10:18 +0200 Subject: [PATCH 06/48] Refactor command handlers information --- .../Shared/domain/CommandNotRegisteredError.ts | 7 +++++++ .../CommandBus/CommandHandlersInformation.ts | 11 +++++++++-- .../infrastructure/CommandBus/InMemoryCommandBus.ts | 7 +------ .../CommandBus/NoHandlerForMessageError.ts | 7 ------- .../CommandBus/InMemoryCommandBus.test.ts | 6 +++--- 5 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 src/Contexts/Shared/domain/CommandNotRegisteredError.ts delete mode 100644 src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts diff --git a/src/Contexts/Shared/domain/CommandNotRegisteredError.ts b/src/Contexts/Shared/domain/CommandNotRegisteredError.ts new file mode 100644 index 0000000..1da7052 --- /dev/null +++ b/src/Contexts/Shared/domain/CommandNotRegisteredError.ts @@ -0,0 +1,7 @@ +import { Command } from './Command'; + +export class CommandNotRegisteredError extends Error { + constructor(command: Command) { + super(`The command <${command.constructor.name}> hasn't a command handler associated`); + } +} diff --git a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts index 531c872..d157ed6 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation.ts @@ -1,5 +1,6 @@ import { Command } from '../../domain/Command'; import { CommandHandler } from '../../domain/CommandHandler'; +import { CommandNotRegisteredError } from '../../domain/CommandNotRegisteredError'; export class CommandHandlersInformation { private commandHandlersMap: Map>; @@ -18,7 +19,13 @@ export class CommandHandlersInformation { return handlersMap; } - public getCommandHandler(command: Command): CommandHandler | undefined { - return this.commandHandlersMap.get(command.constructor); + public search(command: Command): CommandHandler { + const commandHandler = this.commandHandlersMap.get(command.constructor); + + if (!commandHandler) { + throw new CommandNotRegisteredError(command); + } + + return commandHandler; } } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index fc91594..949000a 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -1,17 +1,12 @@ import { Command } from '../../domain/Command'; import { CommandBus } from './../../domain/CommandBus'; import { CommandHandlersInformation } from './CommandHandlersInformation'; -import { NoHandlerForMessageError } from './NoHandlerForMessageError'; export class InMemoryCommandBus implements CommandBus { constructor(private commandHandlersInformation: CommandHandlersInformation) {} dispatch(command: Command): void { - const handler = this.commandHandlersInformation.getCommandHandler(command); - - if (!handler) { - throw new NoHandlerForMessageError(command); - } + const handler = this.commandHandlersInformation.search(command); handler.handle(command); } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts b/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts deleted file mode 100644 index 4a3d932..0000000 --- a/src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Command } from '../../domain/Command'; - -export class NoHandlerForMessageError extends Error { - constructor(command: Command) { - super(`There is not handler for command of type ${command.constructor.name}`); - } -} diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 2adc25f..1de8598 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -1,8 +1,8 @@ import { Command } from '../../../../../src/Contexts/Shared/domain/Command'; import { CommandHandler } from '../../../../../src/Contexts/Shared/domain/CommandHandler'; +import { CommandNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/CommandNotRegisteredError'; import { CommandHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation'; import { InMemoryCommandBus } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus'; -import { NoHandlerForMessageError } from '../../../../../src/Contexts/Shared/infrastructure/CommandBus/NoHandlerForMessageError'; class UnhandledCommand extends Command { static COMMAND_NAME = 'unhandled.command'; @@ -29,8 +29,8 @@ describe('InMemoryCommandBus', () => { try { commandBus.dispatch(unhandledCommand); } catch (error) { - expect(error).toBeInstanceOf(NoHandlerForMessageError); - expect(error.message).toBe('There is not handler for command of type UnhandledCommand'); + expect(error).toBeInstanceOf(CommandNotRegisteredError); + expect(error.message).toBe(`The command hasn't a command handler associated`); done(); } }); From 784ab218b3f90212f34031b62da3e36e12b8211a Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Fri, 12 Jun 2020 17:29:01 +0200 Subject: [PATCH 07/48] Command handler to create course MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: Iván Portillo --- .../Mooc/Courses/application/CourseCreator.ts | 14 ++++++++---- .../application/CreateCourseCommand.ts | 20 +++++++++++++++++ .../application/CreateCourseCommandHandler.ts | 22 +++++++++++++++++++ src/Contexts/Shared/domain/CommandBus.ts | 2 +- src/Contexts/Shared/domain/CommandHandler.ts | 4 ++-- .../CommandBus/InMemoryCommandBus.ts | 4 ++-- .../Courses/application.yaml | 6 +++++ .../Shared/application.yaml | 8 +++++++ .../apps/application.yaml | 2 +- .../controllers/CoursePutController.ts | 8 ++++--- 10 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts create mode 100644 src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts diff --git a/src/Contexts/Mooc/Courses/application/CourseCreator.ts b/src/Contexts/Mooc/Courses/application/CourseCreator.ts index 67acc99..0880cdf 100644 --- a/src/Contexts/Mooc/Courses/application/CourseCreator.ts +++ b/src/Contexts/Mooc/Courses/application/CourseCreator.ts @@ -6,6 +6,12 @@ import { CourseName } from '../domain/CourseName'; import { CourseDuration } from '../domain/CourseDuration'; import { EventBus } from '../../../Shared/domain/EventBus'; +type Params = { + courseId: CourseId; + courseName: CourseName; + courseDuration: CourseDuration; +}; + export class CourseCreator { private repository: CourseRepository; private eventBus: EventBus; @@ -15,11 +21,11 @@ export class CourseCreator { this.eventBus = eventBus; } - async run(request: CreateCourseRequest): Promise { + async run({ courseId, courseName, courseDuration }: Params): Promise { const course = Course.create( - new CourseId(request.id), - new CourseName(request.name), - new CourseDuration(request.duration) + courseId, + courseName, + courseDuration ); await this.repository.save(course); diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts new file mode 100644 index 0000000..199f777 --- /dev/null +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts @@ -0,0 +1,20 @@ +import { Command } from '../../../Shared/domain/Command'; + +type Params = { + id: string; + name: string; + duration: string; +}; + +export class CreateCourseCommand extends Command { + id: string; + name: string; + duration: string; + + constructor({ id, name, duration }: Params) { + super(); + this.id = id; + this.name = name; + this.duration = duration; + } +} \ No newline at end of file diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts new file mode 100644 index 0000000..2c8c60e --- /dev/null +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler.ts @@ -0,0 +1,22 @@ +import { CreateCourseCommand } from './CreateCourseCommand'; +import { CommandHandler } from '../../../Shared/domain/CommandHandler'; +import { CourseCreator } from './CourseCreator'; +import { Command } from '../../../Shared/domain/Command'; +import { CourseId } from '../../Shared/domain/Courses/CourseId'; +import { CourseName } from '../domain/CourseName'; +import { CourseDuration } from '../domain/CourseDuration'; + +export class CreateCourseCommandHandler implements CommandHandler { + constructor(private courseCreator: CourseCreator) {} + + subscribedTo(): Command { + return CreateCourseCommand; + } + + async handle(command: CreateCourseCommand): Promise { + const courseId = new CourseId(command.id); + const courseName = new CourseName(command.name); + const courseDuration = new CourseDuration(command.duration); + await this.courseCreator.run({ courseId, courseName, courseDuration }); + } +} diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts index 82e8096..a4aebbc 100644 --- a/src/Contexts/Shared/domain/CommandBus.ts +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -1,5 +1,5 @@ import { Command } from './Command'; export interface CommandBus { - dispatch(command: Command): void; + dispatch(command: Command): Promise; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/CommandHandler.ts b/src/Contexts/Shared/domain/CommandHandler.ts index 87d1c94..65be088 100644 --- a/src/Contexts/Shared/domain/CommandHandler.ts +++ b/src/Contexts/Shared/domain/CommandHandler.ts @@ -1,6 +1,6 @@ import { Command } from './Command'; export interface CommandHandler { - subscribedTo(): T; - handle(command: T): void; + subscribedTo(): Command; + handle(command: T): Promise; } diff --git a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts index 949000a..011bdb2 100644 --- a/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts +++ b/src/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.ts @@ -5,9 +5,9 @@ import { CommandHandlersInformation } from './CommandHandlersInformation'; export class InMemoryCommandBus implements CommandBus { constructor(private commandHandlersInformation: CommandHandlersInformation) {} - dispatch(command: Command): void { + async dispatch(command: Command): Promise { const handler = this.commandHandlersInformation.search(command); - handler.handle(command); + await handler.handle(command); } } diff --git a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml index c4abfad..8acb39e 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml @@ -7,3 +7,9 @@ services: Mooc.courses.CourseCreator: class: ../../../../../Contexts/Mooc/Courses/application/CourseCreator arguments: ['@Mooc.courses.CourseRepository', '@Mooc.shared.EventBus'] + + Mooc.courses.CreateCourseCommandHandler: + class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler + arguments: ['@Mooc.courses.CourseCreator'] + tags: + - { name: 'commandHandler' } \ No newline at end of file diff --git a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml index 62137a3..51165b3 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml @@ -13,6 +13,14 @@ services: class: ../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus arguments: [] + Mooc.shared.CommandHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation + arguments: ['!tagged commandHandler'] + + Mooc.shared.CommandBus: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus + arguments: ['@Mooc.shared.CommandHandlersInformation'] + Mooc.shared.EventBus.DomainEventMapping: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventMapping arguments: ['!tagged domainEventSubscriber'] diff --git a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml index 308a6f9..3e4dff9 100644 --- a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml @@ -2,7 +2,7 @@ services: Apps.mooc.controllers.CoursePutController: class: ../../../controllers/CoursePutController - arguments: ["@Mooc.courses.CourseCreator"] + arguments: ["@Mooc.shared.CommandBus"] Apps.mooc.controllers.StatusGetController: class: ../../../controllers/StatusGetController diff --git a/src/apps/mooc_backend/controllers/CoursePutController.ts b/src/apps/mooc_backend/controllers/CoursePutController.ts index 4b1724e..1863ab7 100644 --- a/src/apps/mooc_backend/controllers/CoursePutController.ts +++ b/src/apps/mooc_backend/controllers/CoursePutController.ts @@ -1,19 +1,21 @@ import { Request, Response } from 'express'; -import { CourseCreator } from '../../../Contexts/Mooc/Courses/application/CourseCreator'; import httpStatus from 'http-status'; import { Controller } from './Controller'; import { CourseAlreadyExists } from '../../../Contexts/Mooc/Courses/domain/CourseAlreadyExists'; +import { CommandBus } from '../../../Contexts/Shared/domain/CommandBus'; +import { CreateCourseCommand } from '../../../Contexts/Mooc/Courses/application/CreateCourseCommand'; export class CoursePutController implements Controller { - constructor(private courseCreator: CourseCreator) {} + constructor(private commandBus: CommandBus) {} async run(req: Request, res: Response) { const id: string = req.params.id; const name: string = req.body.name; const duration: string = req.body.duration; + const createCourseCommand = new CreateCourseCommand({ id, name, duration }); try { - await this.courseCreator.run({ id, name, duration }); + await this.commandBus.dispatch(createCourseCommand); } catch (error) { if (error instanceof CourseAlreadyExists) { res.status(httpStatus.BAD_REQUEST).send(error.message); From ba63a67761a551282ed59e736850356b4a1043f1 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Sun, 14 Jun 2020 12:29:00 +0200 Subject: [PATCH 08/48] Makefile: Add support to start backoffice frontend application --- Makefile | 14 ++++++++++---- docker-compose.yml | 29 +++++++++++++++++++++-------- package.json | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 23f0fa4..d8c05ad 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ -.PHONY = default deps build test start clean start-database +.PHONY = default deps build test start-mooc-backend clean start-database start-backoffice-frontend IMAGE_NAME := codelytv/typescript-ddd-skeleton SERVICE_NAME := app +MOOC_APP_NAME := mooc +BACKOFFICE_APP_NAME := backoffice # Test if the dependencies we need to run this Makefile are installed DOCKER := $(shell command -v docker) @@ -26,9 +28,13 @@ build: test: build docker-compose run --rm $(SERVICE_NAME) bash -c 'npm run build && npm run test' -# Start the application -start: build - docker-compose up $(SERVICE_NAME) && docker-compose down +# Start mooc backend app +start-mooc-backend: build + docker-compose up $(MOOC_APP_NAME)-backend && docker-compose down + +# Start backoffice frontend app +start-backoffice-frontend: build + docker-compose up $(BACKOFFICE_APP_NAME)-frontend && docker-compose down # Clean containers clean: diff --git a/docker-compose.yml b/docker-compose.yml index f23e67e..11634f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,31 @@ version: '3.8' +x-app-service: &default-app + build: . + command: '' + environment: + - MONGO_URL=mongodb://mongo:27017/dev + depends_on: + - mongo + volumes: + - .:/code + - node_modules:/code/node_modules + services: app: - build: . + <<: *default-app + + mooc-backend: + <<: *default-app command: bash -c "npm run build && npm run start" ports: - 3000:3000 - environment: - - MONGO_URL=mongodb://mongo:27017/dev - depends_on: - - mongo - volumes: - - .:/code - - node_modules:/code/node_modules + + backoffice-frontend: + <<: *default-app + command: bash -c "npm run build && npm run start:backoffice:frontend" + ports: + - 8032:8032 mongo: image: mongo:3.4.6 diff --git a/package.json b/package.json index b897276..f6edd9e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "start:backoffice:frontend": "NODE_ENV=production node dist/src/apps/backoffice/frontend/server", "build": "npm run build:clean && npm run build:tsc && npm run build:di", "build:tsc": "tsc -p tsconfig.prod.json", - "build:di": "copy 'src/**/*.{json,yaml}' dist/src", + "build:di": "copy 'src/**/*.{json,yaml,html,png}' dist/src", "build:clean": "rm -r dist; exit 0" }, "dependencies": { From 2543df4202d570d3edadf907d601ed2873de8912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 17:26:56 +0200 Subject: [PATCH 09/48] Refactor CourseCreator test to use command handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- .../Courses/application/CourseCreator.test.ts | 16 ++++++++-------- .../application/CreateCourseCommandMother.ts | 14 ++++++++++++++ .../application/CreateCourseRequestMother.ts | 17 ----------------- .../Contexts/Mooc/Courses/domain/Course.test.ts | 12 ++++++------ .../Mooc/Courses/domain/CourseMother.ts | 9 +++++---- 5 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts delete mode 100644 tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts diff --git a/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts b/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts index 3609c2c..a406009 100644 --- a/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts +++ b/tests/Contexts/Mooc/Courses/application/CourseCreator.test.ts @@ -1,25 +1,25 @@ import { CourseCreator } from '../../../../../src/Contexts/Mooc/Courses/application/CourseCreator'; import { CourseMother } from '../domain/CourseMother'; import { CourseRepositoryMock } from '../__mocks__/CourseRepositoryMock'; -import { CreateCourseRequestMother } from './CreateCourseRequestMother'; +import { CreateCourseCommandMother } from './CreateCourseCommandMother'; import EventBusMock from '../__mocks__/EventBusMock'; +import { CreateCourseCommandHandler } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommandHandler'; let repository: CourseRepositoryMock; -let creator: CourseCreator; +let handler: CreateCourseCommandHandler; const eventBus = new EventBusMock(); beforeEach(() => { repository = new CourseRepositoryMock(); - creator = new CourseCreator(repository, eventBus); + const creator = new CourseCreator(repository, eventBus); + handler = new CreateCourseCommandHandler(creator); }); it('should create a valid course', async () => { - const request = CreateCourseRequestMother.random(); - - const course = CourseMother.fromRequest(request); - - await creator.run(request); + const command = CreateCourseCommandMother.random(); + await handler.handle(command); + const course = CourseMother.fromCommand(command); repository.assertLastSavedCourseIs(course); }); diff --git a/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts b/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts new file mode 100644 index 0000000..02b796d --- /dev/null +++ b/tests/Contexts/Mooc/Courses/application/CreateCourseCommandMother.ts @@ -0,0 +1,14 @@ +import { CourseDurationMother } from '../domain/CourseDurationMother'; +import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; +import { CourseNameMother } from '../domain/CourseNameMother'; +import { CreateCourseCommand } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommand'; + +export class CreateCourseCommandMother { + static create(id: string, name: string, duration: string): CreateCourseCommand { + return new CreateCourseCommand({ id, name, duration }); + } + + static random(): CreateCourseCommand { + return this.create(CourseIdMother.random().value, CourseNameMother.random().value, CourseDurationMother.random().value); + } +} diff --git a/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts b/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts deleted file mode 100644 index d780d22..0000000 --- a/tests/Contexts/Mooc/Courses/application/CreateCourseRequestMother.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CreateCourseRequest } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseRequest'; -import { CourseDuration } from '../../../../../src/Contexts/Mooc/Courses/domain/CourseDuration'; -import { CourseId } from '../../../../../src/Contexts/Mooc/Shared/domain/Courses/CourseId'; -import { CourseName } from '../../../../../src/Contexts/Mooc/Courses/domain/CourseName'; -import { CourseDurationMother } from '../domain/CourseDurationMother'; -import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; -import { CourseNameMother } from '../domain/CourseNameMother'; - -export class CreateCourseRequestMother { - static create(id: CourseId, name: CourseName, duration: CourseDuration): CreateCourseRequest { - return { id: id.value, name: name.value, duration: duration.value }; - } - - static random(): CreateCourseRequest { - return this.create(CourseIdMother.random(), CourseNameMother.random(), CourseDurationMother.random()); - } -} diff --git a/tests/Contexts/Mooc/Courses/domain/Course.test.ts b/tests/Contexts/Mooc/Courses/domain/Course.test.ts index f6f65fd..cdd67c1 100644 --- a/tests/Contexts/Mooc/Courses/domain/Course.test.ts +++ b/tests/Contexts/Mooc/Courses/domain/Course.test.ts @@ -1,4 +1,4 @@ -import { CreateCourseRequestMother } from '../application/CreateCourseRequestMother'; +import { CreateCourseCommandMother } from '../application/CreateCourseCommandMother'; import { CourseMother } from './CourseMother'; import { Course } from '../../../../../src/Contexts/Mooc/Courses/domain/Course'; import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; @@ -8,13 +8,13 @@ import { CourseDurationMother } from './CourseDurationMother'; describe('Course', () => { it('should return a new course instance', () => { - const request = CreateCourseRequestMother.random(); + const command = CreateCourseCommandMother.random(); - const course = CourseMother.fromRequest(request); + const course = CourseMother.fromCommand(command); - expect(course.id.value).toBe(request.id); - expect(course.name.value).toBe(request.name); - expect(course.duration.value).toBe(request.duration); + expect(course.id.value).toBe(command.id); + expect(course.name.value).toBe(command.name); + expect(course.duration.value).toBe(command.duration); }); it('should record a CourseCreatedDomainEvent after its creation', () => { diff --git a/tests/Contexts/Mooc/Courses/domain/CourseMother.ts b/tests/Contexts/Mooc/Courses/domain/CourseMother.ts index 5d53285..4c388be 100644 --- a/tests/Contexts/Mooc/Courses/domain/CourseMother.ts +++ b/tests/Contexts/Mooc/Courses/domain/CourseMother.ts @@ -6,17 +6,18 @@ import { CreateCourseRequest } from '../../../../../src/Contexts/Mooc/Courses/ap import { CourseIdMother } from '../../Shared/domain/Courses/CourseIdMother'; import { CourseNameMother } from './CourseNameMother'; import { CourseDurationMother } from './CourseDurationMother'; +import { CreateCourseCommand } from '../../../../../src/Contexts/Mooc/Courses/application/CreateCourseCommand'; export class CourseMother { static create(id: CourseId, name: CourseName, duration: CourseDuration): Course { return new Course(id, name, duration); } - static fromRequest(request: CreateCourseRequest): Course { + static fromCommand(command: CreateCourseCommand): Course { return this.create( - CourseIdMother.create(request.id), - CourseNameMother.create(request.name), - CourseDurationMother.create(request.duration) + CourseIdMother.create(command.id), + CourseNameMother.create(command.name), + CourseDurationMother.create(command.duration) ); } From 3ad53bc9ee22cff6bfbcce52b5351c2b5df89e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 17:36:58 +0200 Subject: [PATCH 10/48] Fix InMemoryCommandBus test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- .../CommandBus/InMemoryCommandBus.test.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts index 1de8598..141bfa3 100644 --- a/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus.test.ts @@ -17,33 +17,33 @@ class MyCommandHandler implements CommandHandler { return HandledCommand; } - handle(command: HandledCommand): void {} + async handle(command: HandledCommand): Promise {} } describe('InMemoryCommandBus', () => { - it('throws an error if dispatches a command without handler', done => { + it('throws an error if dispatches a command without handler', async () => { const unhandledCommand = new UnhandledCommand(); const commandHandlersInformation = new CommandHandlersInformation([]); const commandBus = new InMemoryCommandBus(commandHandlersInformation); + let exception = null; + try { - commandBus.dispatch(unhandledCommand); + await commandBus.dispatch(unhandledCommand); } catch (error) { - expect(error).toBeInstanceOf(CommandNotRegisteredError); - expect(error.message).toBe(`The command hasn't a command handler associated`); - done(); + exception = error; } + + expect(exception).toBeInstanceOf(CommandNotRegisteredError); + expect(exception.message).toBe(`The command hasn't a command handler associated`); }); - it('accepts a command with handler', done => { + it('accepts a command with handler', async () => { const handledCommand = new HandledCommand(); const myCommandHandler = new MyCommandHandler(); const commandHandlersInformation = new CommandHandlersInformation([myCommandHandler]); const commandBus = new InMemoryCommandBus(commandHandlersInformation); - try { - commandBus.dispatch(handledCommand); - done(); - } catch (error) {} + await commandBus.dispatch(handledCommand); }); }); From a69946dbf1d96990cb6f3724fdf5afc662118f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Thu, 18 Jun 2020 18:26:46 +0200 Subject: [PATCH 11/48] Add query bus (WIP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aliondev Co-authored-by: Rubén Salado --- src/Contexts/Shared/domain/Query.ts | 1 + src/Contexts/Shared/domain/QueryBus.ts | 5 ++ src/Contexts/Shared/domain/QueryHandler.ts | 7 +++ .../Shared/domain/QueryNotRegisteredError.ts | 7 +++ src/Contexts/Shared/domain/Response.ts | 1 + .../QueryBus/QueryHandlersInformation.ts | 32 ++++++++++++ .../CoursesCounterGetController.ts | 3 ++ .../QueryBus/InMemoryQueryBus.test.ts | 49 +++++++++++++++++++ 8 files changed, 105 insertions(+) create mode 100644 src/Contexts/Shared/domain/Query.ts create mode 100644 src/Contexts/Shared/domain/QueryBus.ts create mode 100644 src/Contexts/Shared/domain/QueryHandler.ts create mode 100644 src/Contexts/Shared/domain/QueryNotRegisteredError.ts create mode 100644 src/Contexts/Shared/domain/Response.ts create mode 100644 src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts create mode 100644 tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts diff --git a/src/Contexts/Shared/domain/Query.ts b/src/Contexts/Shared/domain/Query.ts new file mode 100644 index 0000000..1acca03 --- /dev/null +++ b/src/Contexts/Shared/domain/Query.ts @@ -0,0 +1 @@ +export abstract class Query {} diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts new file mode 100644 index 0000000..54b7b57 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -0,0 +1,5 @@ +import { Query } from './Query'; + +export interface QueryBus { + ask(query: Query): Promise; +} \ No newline at end of file diff --git a/src/Contexts/Shared/domain/QueryHandler.ts b/src/Contexts/Shared/domain/QueryHandler.ts new file mode 100644 index 0000000..49574f8 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryHandler.ts @@ -0,0 +1,7 @@ +import { Query } from './Query'; +import { Response } from './Response'; + +export interface QueryHandler { + subscribedTo(): Query; + handle(query: T): Promise; +} diff --git a/src/Contexts/Shared/domain/QueryNotRegisteredError.ts b/src/Contexts/Shared/domain/QueryNotRegisteredError.ts new file mode 100644 index 0000000..aa26fc4 --- /dev/null +++ b/src/Contexts/Shared/domain/QueryNotRegisteredError.ts @@ -0,0 +1,7 @@ +import { Query } from './Query'; + +export class QueryNotRegisteredError extends Error { + constructor(query: Query) { + super(`The query <${query.constructor.name}> hasn't a query handler associated`); + } +} diff --git a/src/Contexts/Shared/domain/Response.ts b/src/Contexts/Shared/domain/Response.ts new file mode 100644 index 0000000..c1db157 --- /dev/null +++ b/src/Contexts/Shared/domain/Response.ts @@ -0,0 +1 @@ +export interface Response {} \ No newline at end of file diff --git a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts new file mode 100644 index 0000000..e751eca --- /dev/null +++ b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts @@ -0,0 +1,32 @@ +import { Query } from '../../domain/Query'; +import { QueryHandler } from '../../domain/QueryHandler'; +import { Response } from '../../domain/Response'; +import { QueryNotRegisteredError } from '../../domain/QueryNotRegisteredError'; + +export class QueryHandlersInformation { + private queryHandlersMap: Map>; + + constructor(queryHandlers: Array>) { + this.queryHandlersMap = this.formatHandlers(queryHandlers); + } + + private formatHandlers(queryHandlers: Array>): Map> { + const handlersMap = new Map(); + + queryHandlers.forEach(queryHandler => { + handlersMap.set(queryHandler.subscribedTo(), queryHandler); + }); + + return handlersMap; + } + + public search(query: Query): QueryHandler { + const queryHandler = this.queryHandlersMap.get(query.constructor); + + if (!queryHandler) { + throw new QueryNotRegisteredError(query); + } + + return queryHandler; + } +} diff --git a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts index 088faeb..175364b 100644 --- a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts +++ b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts @@ -8,6 +8,9 @@ export class CoursesCounterGetController implements Controller { constructor(private coursesCounterFinder: CoursesCounterFinder) {} async run(req: Request, res: Response): Promise { try { + const query = new FindCourseCounterQuery(); + const count = await this.queryBus.query(query); + const counter = await this.coursesCounterFinder.run(); res.status(httpStatus.OK).send(counter); } catch (e) { diff --git a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts new file mode 100644 index 0000000..eb96306 --- /dev/null +++ b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts @@ -0,0 +1,49 @@ +import { Query } from '../../../../../src/Contexts/Shared/domain/Query'; +import { QueryHandlersInformation } from '../../../../../src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation'; +import { QueryNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/QueryNotRegisteredError'; +import { QueryHandler } from '../../../../../src/Contexts/Shared/domain/QueryHandler'; +import { Response } from '../../../../../src/Contexts/Shared/domain/Response'; + +class UnhandledQuery extends Query { + static QUERY_NAME = 'unhandled.query'; +} + +class HandledQuery extends Query { + static QUERY_NAME = 'handled.query'; +} + +class MyQueryHandler implements QueryHandler { + subscribedTo(): HandledQuery { + return HandledQuery; + } + + async handle(query: HandledQuery): Promise {return {};} +} + +describe('InMemoryQueryBus', () => { + it('throws an error if dispatches a query without handler', async () => { + const unhandledQuery = new UnhandledQuery(); + const queryHandlersInformation = new QueryHandlersInformation([]); + const queryBus = new InMemoryQueryBus(queryHandlersInformation); + + let exception = null; + + try { + await queryBus.ask(unhandledQuery); + } catch (error) { + exception = error; + } + + expect(exception).toBeInstanceOf(QueryNotRegisteredError); + expect(exception.message).toBe(`The query hasn't a query handler associated`); + }); + + it('accepts a query with handler', async () => { + const handledQuery = new HandledQuery(); + const myQueryHandler = new MyQueryHandler(); + const queryHandlersInformation = new QueryHandlersInformation([myQueryHandler]); + const queryBus = new InMemoryQueryBus(queryHandlersInformation); + + await queryBus.ask(handledQuery); + }); +}); From 420be6e73190e1a20a0934cef1a1fa7fe22a4f92 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 19 Jun 2020 18:06:07 +0200 Subject: [PATCH 12/48] Added courses template Co-authored-by: Francisco Ortiz --- package.json | 2 +- .../controllers/CoursesGetController.ts | 17 +++++++++++++++++ .../backoffice/frontend/routes/courses.route.ts | 8 ++++++++ .../frontend/templates/pages/courses.html | 17 +++++++++++++++++ src/apps/mooc_backend/config/dev.json | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/apps/backoffice/frontend/controllers/CoursesGetController.ts create mode 100644 src/apps/backoffice/frontend/routes/courses.route.ts create mode 100644 src/apps/backoffice/frontend/templates/pages/courses.html create mode 100644 src/apps/mooc_backend/config/dev.json diff --git a/package.json b/package.json index f6edd9e..4547d92 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "scripts": { "dev": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/mooc_backend/server.ts", - "dev:backoffice:frontend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/backoffice/frontend/server.ts", + "dev:backoffice:frontend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules ./src/apps/backoffice/frontend/server.ts", "test": "npm run test:unit && npm run test:features", "test:unit": "NODE_ENV=test jest", "test:features": "NODE_ENV=test cucumber-js -p default", diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts new file mode 100644 index 0000000..60d20e1 --- /dev/null +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -0,0 +1,17 @@ +import { Request, Response } from 'express'; +import { CoursesCounterFinder } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder'; + +export class CoursesGetController { + constructor(private coursesCounterFinder: CoursesCounterFinder) {} + + async run(req: Request, res: Response) { + // const courses = await this.coursesCounterFinder.run(); + + res.render('pages/courses', { + title: 'Welcome', + description: 'CodelyTV - Backoffice', + courses_counter: 0, + new_course_id: 'xxxx' + }); + } +} diff --git a/src/apps/backoffice/frontend/routes/courses.route.ts b/src/apps/backoffice/frontend/routes/courses.route.ts new file mode 100644 index 0000000..23f7303 --- /dev/null +++ b/src/apps/backoffice/frontend/routes/courses.route.ts @@ -0,0 +1,8 @@ +import { Express } from 'express'; +import container from '../config/dependency-injection'; +import { CoursesGetController } from '../controllers/CoursesGetController'; + +export const register = (app: Express) => { + const controller: CoursesGetController = container.get('Apps.Backoffice.Frontend.controllers.CoursesGetController'); + app.get('/courses', controller.run.bind(controller)); +}; diff --git a/src/apps/backoffice/frontend/templates/pages/courses.html b/src/apps/backoffice/frontend/templates/pages/courses.html new file mode 100644 index 0000000..a96ab2f --- /dev/null +++ b/src/apps/backoffice/frontend/templates/pages/courses.html @@ -0,0 +1,17 @@ +{% extends '../master.html' %} + +{% block page_title %}Cursos{% endblock %} + +{% block main %} +
+ Sunset in the mountains +
+
Cursos
+

+ Actualmente CodelyTV Pro cuenta con {{ courses_counter }} cursos. +

+
+
+
+
+{% endblock %} diff --git a/src/apps/mooc_backend/config/dev.json b/src/apps/mooc_backend/config/dev.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/apps/mooc_backend/config/dev.json @@ -0,0 +1 @@ +{} From 4d254a186668c072faae05116ca63d586d06f140 Mon Sep 17 00:00:00 2001 From: David Matas Date: Mon, 22 Jun 2020 16:56:58 +0200 Subject: [PATCH 13/48] WIP --- .../config/dependency-injection/application.yaml | 7 +++++++ .../frontend/controllers/CoursesGetController.ts | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index 22d0a1d..425cffa 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -1,3 +1,6 @@ +// TODO: +Instance CoursesCounterFinder application servicer + services: Contexts.shared.Logger: class: ../../../../../Contexts/Shared/infrastructure/WinstonLogger @@ -5,3 +8,7 @@ services: Apps.Backoffice.Frontend.controllers.HomeGetController: class: ../../controllers/HomeGetController + + Apps.Backoffice.Frontend.controllers.CoursesGetController: + class: ../../controllers/CoursesGetController + arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 60d20e1..8200eea 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -5,12 +5,18 @@ export class CoursesGetController { constructor(private coursesCounterFinder: CoursesCounterFinder) {} async run(req: Request, res: Response) { - // const courses = await this.coursesCounterFinder.run(); + let coursesNumber; + try { + const courses = await this.coursesCounterFinder.run(); + coursesNumber = courses.total; + } catch (error) { + coursesNumber = 0; + } res.render('pages/courses', { title: 'Welcome', description: 'CodelyTV - Backoffice', - courses_counter: 0, + courses_counter: coursesNumber, new_course_id: 'xxxx' }); } From 69f44d604f5d6cfdfb69ad9582689783b924d28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Wed, 24 Jun 2020 17:22:23 +0200 Subject: [PATCH 14/48] Implement InMemoryQueryBus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: aliondev --- src/Contexts/Shared/domain/QueryBus.ts | 3 ++- src/Contexts/Shared/domain/QueryHandler.ts | 4 ++-- .../infrastructure/QueryBus/InMemoryQueryBus.ts | 14 ++++++++++++++ .../QueryBus/InMemoryQueryBus.test.ts | 3 ++- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts index 54b7b57..2a04f03 100644 --- a/src/Contexts/Shared/domain/QueryBus.ts +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -1,5 +1,6 @@ import { Query } from './Query'; +import { Response } from './Response'; export interface QueryBus { - ask(query: Query): Promise; + ask(query: Query): Promise; } \ No newline at end of file diff --git a/src/Contexts/Shared/domain/QueryHandler.ts b/src/Contexts/Shared/domain/QueryHandler.ts index 49574f8..a988d70 100644 --- a/src/Contexts/Shared/domain/QueryHandler.ts +++ b/src/Contexts/Shared/domain/QueryHandler.ts @@ -1,7 +1,7 @@ import { Query } from './Query'; import { Response } from './Response'; -export interface QueryHandler { +export interface QueryHandler { subscribedTo(): Query; - handle(query: T): Promise; + handle(query: Q): Promise; } diff --git a/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts b/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts new file mode 100644 index 0000000..ad7cbe6 --- /dev/null +++ b/src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.ts @@ -0,0 +1,14 @@ +import { Query } from '../../domain/Query'; +import { Response } from '../../domain/Response'; +import { QueryBus } from './../../domain/QueryBus'; +import { QueryHandlersInformation } from './QueryHandlersInformation'; + +export class InMemoryQueryBus implements QueryBus { + constructor(private queryHandlersInformation: QueryHandlersInformation) {} + + async ask(query: Query): Promise { + const handler = this.queryHandlersInformation.search(query); + + return handler.handle(query) as Promise; + } +} diff --git a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts index eb96306..829e1c5 100644 --- a/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts +++ b/tests/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus.test.ts @@ -3,6 +3,7 @@ import { QueryHandlersInformation } from '../../../../../src/Contexts/Shared/inf import { QueryNotRegisteredError } from '../../../../../src/Contexts/Shared/domain/QueryNotRegisteredError'; import { QueryHandler } from '../../../../../src/Contexts/Shared/domain/QueryHandler'; import { Response } from '../../../../../src/Contexts/Shared/domain/Response'; +import { InMemoryQueryBus } from '../../../../../src/Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus'; class UnhandledQuery extends Query { static QUERY_NAME = 'unhandled.query'; @@ -35,7 +36,7 @@ describe('InMemoryQueryBus', () => { } expect(exception).toBeInstanceOf(QueryNotRegisteredError); - expect(exception.message).toBe(`The query hasn't a query handler associated`); + expect(exception.message).toBe(`The query hasn't a query handler associated`); }); it('accepts a query with handler', async () => { From d6b7729e96f7ef64681101151e794f38913ed9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Wed, 24 Jun 2020 17:54:52 +0200 Subject: [PATCH 15/48] Refactor FindCourseCounter to use query bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén Salado Co-authored-by: aliondev --- .../application/Find/CoursesCounterFinder.ts | 4 +-- .../Find/FindCoursesCounterQuery.ts | 3 +++ .../Find/FindCoursesCounterQueryHandler.ts | 17 +++++++++++++ ...ponse.ts => FindCoursesCounterResponse.ts} | 2 +- .../CoursesCounter/application.yaml | 7 +++++- .../Shared/application.yaml | 8 ++++++ .../apps/application.yaml | 2 +- .../CoursesCounterGetController.ts | 13 +++++----- .../__mocks__/CoursesCounterRepositoryMock.ts | 1 - ... => FindCourseCounterQueryHandler.test.ts} | 25 +++++++++++++------ .../domain/CoursesCounterResponseMother.ts | 4 +-- 11 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts create mode 100644 src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts rename src/Contexts/Mooc/CoursesCounter/application/Find/{CoursesCounterResponse.ts => FindCoursesCounterResponse.ts} (67%) rename tests/Contexts/Mooc/CoursesCounter/application/Find/{CoursesCounterFinder.test.ts => FindCourseCounterQueryHandler.test.ts} (55%) diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts index 6f95bcb..445fde7 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.ts @@ -1,6 +1,6 @@ import { CoursesCounterRepository } from '../../domain/CoursesCounterRepository'; import { CoursesCounterNotExist } from '../../domain/CoursesCounterNotExist'; -import { CoursesCounterResponse } from './CoursesCounterResponse'; +import { FindCoursesCounterResponse } from './FindCoursesCounterResponse'; export class CoursesCounterFinder { constructor(private repository: CoursesCounterRepository) {} @@ -11,6 +11,6 @@ export class CoursesCounterFinder { throw new CoursesCounterNotExist(); } - return new CoursesCounterResponse(counter.total.value); + return new FindCoursesCounterResponse(counter.total.value); } } diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts new file mode 100644 index 0000000..312dd47 --- /dev/null +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts @@ -0,0 +1,3 @@ +import { Query } from "../../../../Shared/domain/Query"; + +export class FindCoursesCounterQuery implements Query {} \ No newline at end of file diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts new file mode 100644 index 0000000..c8e85ab --- /dev/null +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts @@ -0,0 +1,17 @@ +import { QueryHandler } from "../../../../Shared/domain/QueryHandler"; +import { FindCoursesCounterQuery } from "./FindCoursesCounterQuery"; +import { FindCoursesCounterResponse } from "./FindCoursesCounterResponse"; +import { Query } from "../../../../Shared/domain/Query"; +import { CoursesCounterFinder } from "./CoursesCounterFinder"; + +export class FindCoursesCounterQueryHandler implements QueryHandler { + constructor(private finder: CoursesCounterFinder) {} + + subscribedTo(): Query { + return FindCoursesCounterQuery; + } + handle(_query: FindCoursesCounterQuery): Promise { + return this.finder.run(); + } + +} \ No newline at end of file diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts similarity index 67% rename from src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts rename to src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts index af89a06..fa5bab2 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse.ts @@ -1,4 +1,4 @@ -export class CoursesCounterResponse { +export class FindCoursesCounterResponse { readonly total: number; constructor(total: number) { diff --git a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml index 8314388..493864c 100644 --- a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml @@ -1,5 +1,4 @@ services: - Mooc.coursesCounter.CoursesCounterRepository: class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository arguments: ["@Mooc.shared.ConnectionManager"] @@ -21,4 +20,10 @@ services: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder arguments: ["@Mooc.coursesCounter.CoursesCounterRepository"] + + Mooc.coursesCounter.FindCoursesCounterQueryHandler: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler + arguments: ["@Mooc.coursesCounter.CoursesCounterFinder"] + tags: + - { name: 'queryHandler' } diff --git a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml index 51165b3..624a233 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml @@ -28,3 +28,11 @@ services: Mooc.shared.EventBus.DomainEventJsonDeserializer: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventJsonDeserializer arguments: ['@Mooc.shared.EventBus.DomainEventMapping'] + + Mooc.shared.QueryHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation + arguments: ['!tagged queryHandler'] + + Mooc.shared.QueryBus: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus + arguments: ['@Mooc.shared.QueryHandlersInformation'] diff --git a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml index 3e4dff9..131ee62 100644 --- a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml @@ -10,4 +10,4 @@ services: Apps.mooc.controllers.CoursesCounterGetController: class: ../../../controllers/CoursesCounterGetController - arguments: ["@Mooc.coursesCounter.CoursesCounterFinder"] + arguments: ["@Mooc.shared.QueryBus"] diff --git a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts index 175364b..9f97752 100644 --- a/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts +++ b/src/apps/mooc_backend/controllers/CoursesCounterGetController.ts @@ -1,18 +1,19 @@ import { Controller } from './Controller'; import { Request, Response } from 'express'; -import { CoursesCounterFinder } from '../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder'; import httpStatus = require('http-status'); import { CoursesCounterNotExist } from '../../../Contexts/Mooc/CoursesCounter/domain/CoursesCounterNotExist'; +import { FindCoursesCounterQuery } from '../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; +import { QueryBus } from '../../../Contexts/Shared/domain/QueryBus'; +import { FindCoursesCounterResponse } from '../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; export class CoursesCounterGetController implements Controller { - constructor(private coursesCounterFinder: CoursesCounterFinder) {} + constructor(private queryBus: QueryBus) {} async run(req: Request, res: Response): Promise { try { - const query = new FindCourseCounterQuery(); - const count = await this.queryBus.query(query); + const query = new FindCoursesCounterQuery(); + const count = await this.queryBus.ask(query); - const counter = await this.coursesCounterFinder.run(); - res.status(httpStatus.OK).send(counter); + res.status(httpStatus.OK).send(count); } catch (e) { if (e instanceof CoursesCounterNotExist) { res.status(httpStatus.NOT_FOUND).send(); diff --git a/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts b/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts index cd88fe5..15bf9a0 100644 --- a/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts +++ b/tests/Contexts/Mooc/CoursesCounter/__mocks__/CoursesCounterRepositoryMock.ts @@ -1,7 +1,6 @@ import { CoursesCounterRepository } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterRepository'; import { CoursesCounter } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounter'; import { Nullable } from '../../../../../src/Contexts/Shared/domain/Nullable'; -import { CourseId } from '../../../../../src/Contexts/Mooc/Shared/domain/Courses/CourseId'; export class CoursesCounterRepositoryMock implements CoursesCounterRepository { private mockSave = jest.fn(); diff --git a/tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts b/tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts similarity index 55% rename from tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts rename to tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts index a52fd9a..260fa00 100644 --- a/tests/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder.test.ts +++ b/tests/Contexts/Mooc/CoursesCounter/application/Find/FindCourseCounterQueryHandler.test.ts @@ -3,28 +3,37 @@ import { CoursesCounterMother } from '../../domain/CoursesCounterMother'; import { CoursesCounterRepositoryMock } from '../../__mocks__/CoursesCounterRepositoryMock'; import { CoursesCounterResponseMother } from '../../domain/CoursesCounterResponseMother'; import { CoursesCounterNotExist } from '../../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterNotExist'; +import { FindCoursesCounterQueryHandler } from '../../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler'; +import { FindCoursesCounterQuery } from '../../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; -describe('CoursesCounter Finder', () => { - let finder: CoursesCounterFinder; +describe('FindCourseCounter QueryHandler', () => { let repository: CoursesCounterRepositoryMock; beforeEach(() => { repository = new CoursesCounterRepositoryMock(); - finder = new CoursesCounterFinder(repository); }); + it('should find an existing courses counter', async () => { const counter = CoursesCounterMother.random(); - const expected = CoursesCounterResponseMother.create(counter.total); repository.returnOnSearch(counter); - const actual = await finder.run(); - + const handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder(repository)); + + const query = new FindCoursesCounterQuery(); + const response = await handler.handle(query); + repository.assertSearch(); - expect(expected).toEqual(actual); + + const expected = CoursesCounterResponseMother.create(counter.total); + expect(expected).toEqual(response); }); it('should throw an exception when courses counter does not exists', async () => { - await expect(finder.run()).rejects.toBeInstanceOf(CoursesCounterNotExist); + const handler = new FindCoursesCounterQueryHandler(new CoursesCounterFinder(repository)); + + const query = new FindCoursesCounterQuery(); + + await expect(handler.handle(query)).rejects.toBeInstanceOf(CoursesCounterNotExist); }); }); diff --git a/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts b/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts index d383200..677b388 100644 --- a/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts +++ b/tests/Contexts/Mooc/CoursesCounter/domain/CoursesCounterResponseMother.ts @@ -1,8 +1,8 @@ import { CoursesCounterTotal } from '../../../../../src/Contexts/Mooc/CoursesCounter/domain/CoursesCounterTotal'; -import { CoursesCounterResponse } from '../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterResponse'; +import { FindCoursesCounterResponse } from '../../../../../src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; export class CoursesCounterResponseMother { static create(total: CoursesCounterTotal) { - return new CoursesCounterResponse(total.value); + return new FindCoursesCounterResponse(total.value); } } From 68da68f07fad1e117a1e0672d17be439f83b5edd Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Fri, 3 Jul 2020 17:17:56 +0200 Subject: [PATCH 16/48] Use localhost as default mongo host --- src/apps/mooc_backend/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/mooc_backend/config/config.ts b/src/apps/mooc_backend/config/config.ts index 11b1dba..b679f61 100644 --- a/src/apps/mooc_backend/config/config.ts +++ b/src/apps/mooc_backend/config/config.ts @@ -12,7 +12,7 @@ const convictConfig = convict({ doc: 'The Mongo connection URL', format: String, env: 'MONGO_URL', - default: 'mongodb://mongo:27017/dev' + default: 'mongodb://localhost:27017/dev' } } }); From b5e43ff03988adb39be9ad3e41821541e6aec049 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 3 Jul 2020 16:09:30 +0200 Subject: [PATCH 17/48] Instantitate CoursesCounterFinder for backoffice frontend --- src/apps/backoffice/frontend/config/config.ts | 8 ++++++++ .../dependency-injection/application.yaml | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/apps/backoffice/frontend/config/config.ts b/src/apps/backoffice/frontend/config/config.ts index 4405a32..11b1dba 100644 --- a/src/apps/backoffice/frontend/config/config.ts +++ b/src/apps/backoffice/frontend/config/config.ts @@ -6,6 +6,14 @@ const convictConfig = convict({ format: ['production', 'development', 'staging', 'test'], default: 'default', env: 'NODE_ENV' + }, + mongo: { + url: { + doc: 'The Mongo connection URL', + format: String, + env: 'MONGO_URL', + default: 'mongodb://mongo:27017/dev' + } } }); diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index 425cffa..c43a05a 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -1,6 +1,3 @@ -// TODO: -Instance CoursesCounterFinder application servicer - services: Contexts.shared.Logger: class: ../../../../../Contexts/Shared/infrastructure/WinstonLogger @@ -9,6 +6,20 @@ services: Apps.Backoffice.Frontend.controllers.HomeGetController: class: ../../controllers/HomeGetController + Mooc.coursesCounter.CoursesCounterFinder: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder + arguments: ["@Mooc.coursesCounter.CoursesCounterRepository"] + + Mooc.coursesCounter.CoursesCounterRepository: + class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository + arguments: ["@Mooc.shared.ConnectionManager"] + + Mooc.shared.ConnectionManager: + factory: + class: ../../../../../Contexts/Shared/infrastructure/persistence/mongo/MongoClientFactory + method: 'createClient' + arguments: ['mooc'] + Apps.Backoffice.Frontend.controllers.CoursesGetController: class: ../../controllers/CoursesGetController arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] From cbeeead263c524c01898ee4ebd9e173f9b76a3b1 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 3 Jul 2020 16:44:31 +0200 Subject: [PATCH 18/48] Delete collections after all tests --- .../features/step_definitions/controller.steps.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/apps/mooc_backend/features/step_definitions/controller.steps.ts b/tests/apps/mooc_backend/features/step_definitions/controller.steps.ts index 00e2757..a70221f 100644 --- a/tests/apps/mooc_backend/features/step_definitions/controller.steps.ts +++ b/tests/apps/mooc_backend/features/step_definitions/controller.steps.ts @@ -13,9 +13,7 @@ Given('I send a GET request to {string}', (route: string) => { }); Given('I send a PUT request to {string} with body:', (route: string, body: string) => { - _request = request(app) - .put(route) - .send(JSON.parse(body)); + _request = request(app).put(route).send(JSON.parse(body)); }); Then('the response status code should be {int}', async (status: number) => { @@ -37,5 +35,6 @@ Before(async () => { AfterAll(async () => { const environmentArranger: Promise = container.get('Mooc.EnvironmentArranger'); + await (await environmentArranger).arrange(); await (await environmentArranger).close(); }); From 297eb3dae9d478eb2304145decb571c9beffb5ad Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 3 Jul 2020 16:51:58 +0200 Subject: [PATCH 19/48] Eliminate 0 case from controller --- .../frontend/controllers/CoursesGetController.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 8200eea..60332ba 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -5,18 +5,12 @@ export class CoursesGetController { constructor(private coursesCounterFinder: CoursesCounterFinder) {} async run(req: Request, res: Response) { - let coursesNumber; - try { - const courses = await this.coursesCounterFinder.run(); - coursesNumber = courses.total; - } catch (error) { - coursesNumber = 0; - } + const courses = await this.coursesCounterFinder.run(); res.render('pages/courses', { title: 'Welcome', description: 'CodelyTV - Backoffice', - courses_counter: coursesNumber, + courses_counter: courses.total, new_course_id: 'xxxx' }); } From c4d63edcb61f1cf9d24c3f29668c9006bedfd009 Mon Sep 17 00:00:00 2001 From: David Matas Date: Sun, 5 Jul 2020 08:52:44 +0200 Subject: [PATCH 20/48] Add courses folder in pages --- .../backoffice/frontend/controllers/CoursesGetController.ts | 2 +- .../frontend/templates/pages/{ => courses}/courses.html | 3 ++- .../templates/pages/courses/partials/new_course_form.html | 1 + src/apps/backoffice/frontend/templates/pages/home.html | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) rename src/apps/backoffice/frontend/templates/pages/{ => courses}/courses.html (86%) create mode 100644 src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 60332ba..953da5a 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -7,7 +7,7 @@ export class CoursesGetController { async run(req: Request, res: Response) { const courses = await this.coursesCounterFinder.run(); - res.render('pages/courses', { + res.render('pages/courses/courses', { title: 'Welcome', description: 'CodelyTV - Backoffice', courses_counter: courses.total, diff --git a/src/apps/backoffice/frontend/templates/pages/courses.html b/src/apps/backoffice/frontend/templates/pages/courses/courses.html similarity index 86% rename from src/apps/backoffice/frontend/templates/pages/courses.html rename to src/apps/backoffice/frontend/templates/pages/courses/courses.html index a96ab2f..b954147 100644 --- a/src/apps/backoffice/frontend/templates/pages/courses.html +++ b/src/apps/backoffice/frontend/templates/pages/courses/courses.html @@ -1,4 +1,4 @@ -{% extends '../master.html' %} +{% extends 'master.html' %} {% block page_title %}Cursos{% endblock %} @@ -12,6 +12,7 @@

+ {% include 'pages/courses/partials/new_course_form.html' %}

{% endblock %} diff --git a/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html @@ -0,0 +1 @@ +hello diff --git a/src/apps/backoffice/frontend/templates/pages/home.html b/src/apps/backoffice/frontend/templates/pages/home.html index 30a2e95..aad6614 100644 --- a/src/apps/backoffice/frontend/templates/pages/home.html +++ b/src/apps/backoffice/frontend/templates/pages/home.html @@ -1,4 +1,4 @@ -{% extends '../master.html' %} +{% extends 'master.html' %} {% block page_title %}HOME{% endblock %} From 17f757f6f5806445e17c75c0f8eedfb508efacaf Mon Sep 17 00:00:00 2001 From: David Matas Date: Sun, 5 Jul 2020 21:42:34 +0200 Subject: [PATCH 21/48] Add create course form --- package-lock.json | 91 ++++++++++++++++++- package.json | 8 +- src/apps/backoffice/frontend/app.ts | 15 +++ .../dependency-injection/application.yaml | 16 ++++ .../controllers/CoursesGetController.ts | 4 +- .../controllers/CoursesPostController.ts | 20 ++++ .../frontend/routes/courses.route.ts | 12 ++- .../courses/partials/new_course_form.html | 49 +++++++++- 8 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 src/apps/backoffice/frontend/controllers/CoursesPostController.ts diff --git a/package-lock.json b/package-lock.json index d7aa252..496e635 100644 --- a/package-lock.json +++ b/package-lock.json @@ -901,6 +901,15 @@ "@types/node": "*" } }, + "@types/connect-flash": { + "version": "0.0.35", + "resolved": "https://registry.npmjs.org/@types/connect-flash/-/connect-flash-0.0.35.tgz", + "integrity": "sha512-pwEiJI1gYhH+s6x4+st/TBgf1CPuATADPJi1+2It4Gnckw1pVVdKycRNDyoHwkZGCb/J38vEdOrxnZ4FsOlvWg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/convict": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/@types/convict/-/convict-5.2.1.tgz", @@ -909,6 +918,25 @@ "@types/node": "*" } }, + "@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/cookie-session": { + "version": "2.0.41", + "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.41.tgz", + "integrity": "sha512-Ytd7zWY3WvC7TP9rKVjlnYHus8Hq+lJuioHOtKJ7dXg2Nt+O+9UpelygYy5Eb67QQN92HPO5qEG0vnmAlqRLLw==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/keygrip": "*" + } + }, "@types/cookiejar": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz", @@ -1023,6 +1051,12 @@ "pretty-format": "^25.2.1" } }, + "@types/keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==", + "dev": true + }, "@types/mime": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", @@ -1828,12 +1862,14 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "optional": true }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "optional": true, "requires": { "is-extglob": "^2.1.1" } @@ -2126,6 +2162,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, "console.table": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", @@ -2175,6 +2216,25 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, + "cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + } + }, + "cookie-session": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz", + "integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==", + "requires": { + "cookies": "0.8.0", + "debug": "2.6.9", + "on-headers": "~1.0.2" + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -2186,6 +2246,22 @@ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", "dev": true }, + "cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "requires": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, "copy": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/copy/-/copy-0.3.2.tgz", @@ -5043,6 +5119,14 @@ } } }, + "keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "requires": { + "tsscmp": "1.0.6" + } + }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", @@ -8180,6 +8264,11 @@ } } }, + "tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==" + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", diff --git a/package.json b/package.json index 4547d92..3391bc7 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,10 @@ "body-parser": "^1.19.0", "bson": "^4.0.4", "compression": "^1.7.4", + "connect-flash": "^0.1.1", "convict": "^6.0.0", + "cookie-parser": "^1.4.5", + "cookie-session": "^1.4.0", "copy": "^0.3.2", "errorhandler": "^1.5.1", "express": "^4.17.1", @@ -56,11 +59,14 @@ "winston": "^3.2.1" }, "devDependencies": { - "@types/nunjucks": "^3.1.3", "@types/aws-lambda": "^8.10.51", + "@types/connect-flash": "0.0.35", + "@types/cookie-parser": "^1.4.2", + "@types/cookie-session": "^2.0.41", "@types/cucumber": "^6.0.1", "@types/faker": "^4.1.12", "@types/jest": "^25.2.3", + "@types/nunjucks": "^3.1.3", "@types/supertest": "^2.0.9", "cucumber": "^6.0.5", "faker": "^4.1.0", diff --git a/src/apps/backoffice/frontend/app.ts b/src/apps/backoffice/frontend/app.ts index 1fb376c..e306c85 100644 --- a/src/apps/backoffice/frontend/app.ts +++ b/src/apps/backoffice/frontend/app.ts @@ -4,12 +4,27 @@ import helmet from 'helmet'; import compress from 'compression'; import { registerRoutes } from './routes'; import path from 'path'; +import cookieSession from 'cookie-session'; +import cookieParser from 'cookie-parser'; +import flash from 'connect-flash'; import nunjucks from 'nunjucks'; const app: express.Express = express(); app.set('port', process.env.PORT || 8032); +app.use(cookieParser()); +app.use( + cookieSession({ + name: 'Backoffice Frontend Codely session', + keys: ['Codely'], + + // Cookie Options + maxAge: 24 * 60 * 60 * 1000 // 24 hours + }) +); +app.use(flash()); + // Templates app.set('view engine', 'html'); nunjucks.configure(path.join(__dirname, '/templates'), { diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index c43a05a..510476e 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -20,6 +20,22 @@ services: method: 'createClient' arguments: ['mooc'] + Mooc.courses.CourseRepository: + class: ../../../../../Contexts/Mooc/Courses/infrastructure/persistence/MongoCourseRepository + arguments: ['@Mooc.shared.ConnectionManager'] + + Mooc.shared.EventBus: + class: ../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus + arguments: [] + + Mooc.courses.CourseCreator: + class: ../../../../../Contexts/Mooc/Courses/application/CourseCreator + arguments: ['@Mooc.courses.CourseRepository', '@Mooc.shared.EventBus'] + Apps.Backoffice.Frontend.controllers.CoursesGetController: class: ../../controllers/CoursesGetController arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] + + Apps.Backoffice.Frontend.controllers.CoursesPostController: + class: ../../controllers/CoursesPostController + arguments: ['@Mooc.courses.CourseCreator'] diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 953da5a..8cd931d 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -1,5 +1,6 @@ import { Request, Response } from 'express'; import { CoursesCounterFinder } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder'; +import { Uuid } from '../../../../Contexts/Shared/domain/value-object/Uuid'; export class CoursesGetController { constructor(private coursesCounterFinder: CoursesCounterFinder) {} @@ -11,7 +12,8 @@ export class CoursesGetController { title: 'Welcome', description: 'CodelyTV - Backoffice', courses_counter: courses.total, - new_course_id: 'xxxx' + new_course_id: Uuid.random(), + flash: req.flash() }); } } diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts new file mode 100644 index 0000000..d7c630b --- /dev/null +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -0,0 +1,20 @@ +import { Request, Response } from 'express'; +import { CreateCourseRequest } from '../../../../Contexts/Mooc/Courses/application/CreateCourseRequest'; +import { CourseCreator } from '../../../../Contexts/Mooc/Courses/application/CourseCreator'; + +export class CoursesPostController { + constructor(private courseCreator: CourseCreator) {} + + async run(req: Request, res: Response) { + // TODO: validation + // req.flash('errors.id', 'Flash Message Added'); + + await this.createCourse(req, res); + } + + private async createCourse(req: Request, res: Response) { + await this.courseCreator.run({ id: req.body.id, name: req.body.name, duration: req.body.duration }); + req.flash('message', `Felicidades, el curso ${req.body.name} ha sido creado!`); + res.redirect('/courses'); + } +} diff --git a/src/apps/backoffice/frontend/routes/courses.route.ts b/src/apps/backoffice/frontend/routes/courses.route.ts index 23f7303..1bf8aa9 100644 --- a/src/apps/backoffice/frontend/routes/courses.route.ts +++ b/src/apps/backoffice/frontend/routes/courses.route.ts @@ -1,8 +1,16 @@ import { Express } from 'express'; import container from '../config/dependency-injection'; import { CoursesGetController } from '../controllers/CoursesGetController'; +import { CoursesPostController } from '../controllers/CoursesPostController'; export const register = (app: Express) => { - const controller: CoursesGetController = container.get('Apps.Backoffice.Frontend.controllers.CoursesGetController'); - app.get('/courses', controller.run.bind(controller)); + const coursesGetController: CoursesGetController = container.get( + 'Apps.Backoffice.Frontend.controllers.CoursesGetController' + ); + const coursesPostController: CoursesPostController = container.get( + 'Apps.Backoffice.Frontend.controllers.CoursesPostController' + ); + + app.get('/courses', coursesGetController.run.bind(coursesGetController)); + app.post('/courses', coursesPostController.run.bind(coursesPostController)); }; diff --git a/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html index ce01362..e151e98 100644 --- a/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html +++ b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html @@ -1 +1,48 @@ -hello +
+

Crear curso

+
+
+ + + + {% if flash['errors.id'] %} +

{{ flash['errors.id'] }}

+ {% endif %} +
+
+
+
+ + + + {% if flash['errors.name'] %} +

{{ flash['errors.name'] }}

+ {% endif %} +
+
+ + + {% if flash['errors.duration'] %} +

{{ flash['errors.duration'] }}

+ {% endif %} +
+
+
+ +
+
From 3f8ca2ad6742ad63a27819041d38703ec33db9c7 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Tue, 7 Jul 2020 19:36:25 +0200 Subject: [PATCH 22/48] Use localhost as mongo host by default --- src/apps/backoffice/frontend/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/backoffice/frontend/config/config.ts b/src/apps/backoffice/frontend/config/config.ts index 11b1dba..b679f61 100644 --- a/src/apps/backoffice/frontend/config/config.ts +++ b/src/apps/backoffice/frontend/config/config.ts @@ -12,7 +12,7 @@ const convictConfig = convict({ doc: 'The Mongo connection URL', format: String, env: 'MONGO_URL', - default: 'mongodb://mongo:27017/dev' + default: 'mongodb://localhost:27017/dev' } } }); From bda1674b0414e04f7411c5b69884fe820eca25f8 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Fri, 10 Jul 2020 16:13:00 +0200 Subject: [PATCH 23/48] Add courses navigation --- src/apps/backoffice/frontend/templates/partials/header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/backoffice/frontend/templates/partials/header.html b/src/apps/backoffice/frontend/templates/partials/header.html index 30fe251..1fccf60 100644 --- a/src/apps/backoffice/frontend/templates/partials/header.html +++ b/src/apps/backoffice/frontend/templates/partials/header.html @@ -11,7 +11,7 @@ class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4"> Usuarios - Cursos From 80cb15f8a2806b421d962928f206d182208298be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Fri, 10 Jul 2020 17:52:43 +0200 Subject: [PATCH 24/48] Add lint step and activate eoline lint rule --- .github/workflows/nodejs.yml | 1 + package.json | 1 + tslint.json | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index c148dee..82a6447 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -25,6 +25,7 @@ jobs: run: | npm install npm run build --if-present + npm run lint npm test env: CI: true diff --git a/package.json b/package.json index 59a19f4..45a50f1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test": "npm run test:unit && npm run test:features", "test:unit": "NODE_ENV=test jest", "test:features": "NODE_ENV=test cucumber-js -p default", + "lint": "tslint src/**/*.ts{,x}", "start": "NODE_ENV=production node dist/src/apps/mooc_backend/server", "build": "npm run build:clean && npm run build:tsc && npm run build:di", "build:tsc": "tsc -p tsconfig.prod.json", diff --git a/tslint.json b/tslint.json index b29bf01..7f72323 100644 --- a/tslint.json +++ b/tslint.json @@ -4,7 +4,7 @@ "rules": { "adjacent-overload-signatures": true, "curly": true, - "eofline": false, + "eofline": true, "align": [true, "parameters"], "class-name": true, "indent": [true, "spaces"], From 9c16582103d398592a43e08ea25afb5396d599d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Portillo?= Date: Fri, 10 Jul 2020 17:56:54 +0200 Subject: [PATCH 25/48] Fix linter issues --- .../application/CreateCourseCommand.ts | 2 +- .../Find/FindCoursesCounterQuery.ts | 4 +-- .../Find/FindCoursesCounterQueryHandler.ts | 30 +++++++++---------- src/Contexts/Shared/domain/CommandBus.ts | 2 +- src/Contexts/Shared/domain/QueryBus.ts | 2 +- src/Contexts/Shared/domain/Response.ts | 2 +- .../QueryBus/QueryHandlersInformation.ts | 4 ++- tslint.json | 2 +- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts index 199f777..884cf7d 100644 --- a/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts +++ b/src/Contexts/Mooc/Courses/application/CreateCourseCommand.ts @@ -17,4 +17,4 @@ export class CreateCourseCommand extends Command { this.name = name; this.duration = duration; } -} \ No newline at end of file +} diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts index 312dd47..bdbf087 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery.ts @@ -1,3 +1,3 @@ -import { Query } from "../../../../Shared/domain/Query"; +import { Query } from '../../../../Shared/domain/Query'; -export class FindCoursesCounterQuery implements Query {} \ No newline at end of file +export class FindCoursesCounterQuery implements Query {} diff --git a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts index c8e85ab..b456d6f 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler.ts @@ -1,17 +1,17 @@ -import { QueryHandler } from "../../../../Shared/domain/QueryHandler"; -import { FindCoursesCounterQuery } from "./FindCoursesCounterQuery"; -import { FindCoursesCounterResponse } from "./FindCoursesCounterResponse"; -import { Query } from "../../../../Shared/domain/Query"; -import { CoursesCounterFinder } from "./CoursesCounterFinder"; +import { QueryHandler } from '../../../../Shared/domain/QueryHandler'; +import { FindCoursesCounterQuery } from './FindCoursesCounterQuery'; +import { FindCoursesCounterResponse } from './FindCoursesCounterResponse'; +import { Query } from '../../../../Shared/domain/Query'; +import { CoursesCounterFinder } from './CoursesCounterFinder'; -export class FindCoursesCounterQueryHandler implements QueryHandler { - constructor(private finder: CoursesCounterFinder) {} +export class FindCoursesCounterQueryHandler + implements QueryHandler { + constructor(private finder: CoursesCounterFinder) {} - subscribedTo(): Query { - return FindCoursesCounterQuery; - } - handle(_query: FindCoursesCounterQuery): Promise { - return this.finder.run(); - } - -} \ No newline at end of file + subscribedTo(): Query { + return FindCoursesCounterQuery; + } + handle(_query: FindCoursesCounterQuery): Promise { + return this.finder.run(); + } +} diff --git a/src/Contexts/Shared/domain/CommandBus.ts b/src/Contexts/Shared/domain/CommandBus.ts index a4aebbc..ac32da3 100644 --- a/src/Contexts/Shared/domain/CommandBus.ts +++ b/src/Contexts/Shared/domain/CommandBus.ts @@ -2,4 +2,4 @@ import { Command } from './Command'; export interface CommandBus { dispatch(command: Command): Promise; -} \ No newline at end of file +} diff --git a/src/Contexts/Shared/domain/QueryBus.ts b/src/Contexts/Shared/domain/QueryBus.ts index 2a04f03..38c5b49 100644 --- a/src/Contexts/Shared/domain/QueryBus.ts +++ b/src/Contexts/Shared/domain/QueryBus.ts @@ -3,4 +3,4 @@ import { Response } from './Response'; export interface QueryBus { ask(query: Query): Promise; -} \ No newline at end of file +} diff --git a/src/Contexts/Shared/domain/Response.ts b/src/Contexts/Shared/domain/Response.ts index c1db157..158c809 100644 --- a/src/Contexts/Shared/domain/Response.ts +++ b/src/Contexts/Shared/domain/Response.ts @@ -1 +1 @@ -export interface Response {} \ No newline at end of file +export interface Response {} diff --git a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts index e751eca..dd34e48 100644 --- a/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts +++ b/src/Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation.ts @@ -10,7 +10,9 @@ export class QueryHandlersInformation { this.queryHandlersMap = this.formatHandlers(queryHandlers); } - private formatHandlers(queryHandlers: Array>): Map> { + private formatHandlers( + queryHandlers: Array> + ): Map> { const handlersMap = new Map(); queryHandlers.forEach(queryHandler => { diff --git a/tslint.json b/tslint.json index 7f72323..b43960e 100644 --- a/tslint.json +++ b/tslint.json @@ -26,7 +26,7 @@ "no-bitwise": true, "no-debugger": true, "prefer-const": true, - "no-empty-interface": true, + "no-empty-interface": false, "no-string-throw": true, "unified-signatures": true, "space-before-function-paren": [ From fced599e646c50831f0786b0ca7fd8d0d45bc305 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Sat, 11 Jul 2020 12:10:46 +0200 Subject: [PATCH 26/48] Fix build --- .../frontend/controllers/CoursesPostController.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts index d7c630b..f3274ff 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -13,7 +13,11 @@ export class CoursesPostController { } private async createCourse(req: Request, res: Response) { - await this.courseCreator.run({ id: req.body.id, name: req.body.name, duration: req.body.duration }); + await this.courseCreator.run({ + courseId: req.body.id, + courseName: req.body.name, + courseDuration: req.body.duration + }); req.flash('message', `Felicidades, el curso ${req.body.name} ha sido creado!`); res.redirect('/courses'); } From 331b8dc35d126f5125833418370d4a25e5477794 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Sat, 11 Jul 2020 12:33:37 +0200 Subject: [PATCH 27/48] Use command and query bus in backoffice frontend app --- .../dependency-injection/application.yaml | 34 +++++++++++++++++-- .../controllers/CoursesGetController.ts | 8 +++-- .../controllers/CoursesPostController.ts | 16 +++++---- .../CoursesCounter/application.yaml | 2 -- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index 510476e..16fcec8 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -8,11 +8,11 @@ services: Mooc.coursesCounter.CoursesCounterFinder: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder - arguments: ["@Mooc.coursesCounter.CoursesCounterRepository"] + arguments: ['@Mooc.coursesCounter.CoursesCounterRepository'] Mooc.coursesCounter.CoursesCounterRepository: class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository - arguments: ["@Mooc.shared.ConnectionManager"] + arguments: ['@Mooc.shared.ConnectionManager'] Mooc.shared.ConnectionManager: factory: @@ -34,8 +34,36 @@ services: Apps.Backoffice.Frontend.controllers.CoursesGetController: class: ../../controllers/CoursesGetController - arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] + arguments: ['@Mooc.shared.QueryBus'] Apps.Backoffice.Frontend.controllers.CoursesPostController: class: ../../controllers/CoursesPostController + arguments: ['@Mooc.shared.CommandBus'] + + Mooc.coursesCounter.FindCoursesCounterQueryHandler: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler + arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] + tags: + - { name: 'queryHandler' } + + Mooc.courses.CreateCourseCommandHandler: + class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler arguments: ['@Mooc.courses.CourseCreator'] + tags: + - { name: 'commandHandler' } + + Mooc.shared.QueryBus: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus + arguments: ['@Mooc.shared.QueryHandlersInformation'] + + Mooc.shared.CommandBus: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus + arguments: ['@Mooc.shared.CommandHandlersInformation'] + + Mooc.shared.QueryHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation + arguments: ['!tagged queryHandler'] + + Mooc.shared.CommandHandlersInformation: + class: ../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation + arguments: ['!tagged commandHandler'] diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 8cd931d..0900f3d 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -1,12 +1,14 @@ import { Request, Response } from 'express'; -import { CoursesCounterFinder } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder'; import { Uuid } from '../../../../Contexts/Shared/domain/value-object/Uuid'; +import { QueryBus } from '../../../../Contexts/Shared/domain/QueryBus'; +import { FindCoursesCounterResponse } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; +import { FindCoursesCounterQuery } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; export class CoursesGetController { - constructor(private coursesCounterFinder: CoursesCounterFinder) {} + constructor(private queryBus: QueryBus) {} async run(req: Request, res: Response) { - const courses = await this.coursesCounterFinder.run(); + const courses = await this.queryBus.ask(new FindCoursesCounterQuery()); res.render('pages/courses/courses', { title: 'Welcome', diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts index f3274ff..f99a1bb 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -1,9 +1,9 @@ import { Request, Response } from 'express'; -import { CreateCourseRequest } from '../../../../Contexts/Mooc/Courses/application/CreateCourseRequest'; -import { CourseCreator } from '../../../../Contexts/Mooc/Courses/application/CourseCreator'; +import { CommandBus } from '../../../../Contexts/Shared/domain/CommandBus'; +import { CreateCourseCommand } from '../../../../Contexts/Mooc/Courses/application/CreateCourseCommand'; export class CoursesPostController { - constructor(private courseCreator: CourseCreator) {} + constructor(private commandBus: CommandBus) {} async run(req: Request, res: Response) { // TODO: validation @@ -13,11 +13,13 @@ export class CoursesPostController { } private async createCourse(req: Request, res: Response) { - await this.courseCreator.run({ - courseId: req.body.id, - courseName: req.body.name, - courseDuration: req.body.duration + const createCourseCommand = new CreateCourseCommand({ + id: req.body.id, + name: req.body.name, + duration: req.body.duration }); + await this.commandBus.dispatch(createCourseCommand); + req.flash('message', `Felicidades, el curso ${req.body.name} ha sido creado!`); res.redirect('/courses'); } diff --git a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml index 493864c..d4457d9 100644 --- a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml @@ -20,10 +20,8 @@ services: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder arguments: ["@Mooc.coursesCounter.CoursesCounterRepository"] - Mooc.coursesCounter.FindCoursesCounterQueryHandler: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler arguments: ["@Mooc.coursesCounter.CoursesCounterFinder"] tags: - { name: 'queryHandler' } - From ab59e53b9390127564887b05af523f0daebb2543 Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Tue, 14 Jul 2020 16:07:27 +0200 Subject: [PATCH 28/48] Initialize CouserCounter collection if it's empty --- src/apps/backoffice/frontend/config/config.ts | 2 +- src/apps/backoffice/frontend/config/test.json | 6 +++++- src/apps/backoffice/frontend/seed.ts | 18 ++++++++++++++++++ src/apps/backoffice/frontend/server.ts | 5 ++++- src/apps/mooc_backend/config/config.ts | 2 +- src/apps/mooc_backend/config/test.json | 2 +- 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/apps/backoffice/frontend/seed.ts diff --git a/src/apps/backoffice/frontend/config/config.ts b/src/apps/backoffice/frontend/config/config.ts index b679f61..de28f95 100644 --- a/src/apps/backoffice/frontend/config/config.ts +++ b/src/apps/backoffice/frontend/config/config.ts @@ -12,7 +12,7 @@ const convictConfig = convict({ doc: 'The Mongo connection URL', format: String, env: 'MONGO_URL', - default: 'mongodb://localhost:27017/dev' + default: 'mongodb://localhost:27017/backoffice-frontend-dev' } } }); diff --git a/src/apps/backoffice/frontend/config/test.json b/src/apps/backoffice/frontend/config/test.json index 0967ef4..6a0d2f4 100644 --- a/src/apps/backoffice/frontend/config/test.json +++ b/src/apps/backoffice/frontend/config/test.json @@ -1 +1,5 @@ -{} +{ + "mongo": { + "url": "mongodb://localhost:27017/backoffice-frontend-test" + } +} diff --git a/src/apps/backoffice/frontend/seed.ts b/src/apps/backoffice/frontend/seed.ts new file mode 100644 index 0000000..a1abc60 --- /dev/null +++ b/src/apps/backoffice/frontend/seed.ts @@ -0,0 +1,18 @@ +import { CoursesCounterRepository } from '../../../Contexts/Mooc/CoursesCounter/domain/CoursesCounterRepository'; +import { CoursesCounter } from '../../../Contexts/Mooc/CoursesCounter/domain/CoursesCounter'; +import { CoursesCounterId } from '../../../Contexts/Mooc/CoursesCounter/domain/CoursesCounterId'; +import container from './config/dependency-injection'; + +export async function seed() { + const repository: CoursesCounterRepository = container.get('Mooc.coursesCounter.CoursesCounterRepository'); + const logger = container.get('Contexts.shared.Logger'); + + const alreadyExists = await repository.search(); + const isTestEnvironment = process.env.NODE_ENV === 'test'; + + if (!alreadyExists && !isTestEnvironment) { + logger.info('[Seed] Initializing CourseCounter'); + const courseCounter = CoursesCounter.initialize(CoursesCounterId.random()); + await repository.save(courseCounter); + } +} diff --git a/src/apps/backoffice/frontend/server.ts b/src/apps/backoffice/frontend/server.ts index 76b7b0a..576a1c5 100644 --- a/src/apps/backoffice/frontend/server.ts +++ b/src/apps/backoffice/frontend/server.ts @@ -1,6 +1,7 @@ import errorHandler from 'errorhandler'; import app from './app'; import container from './config/dependency-injection'; +import { seed } from './seed'; /** * Error Handler. Provides full stack - remove for production @@ -10,9 +11,11 @@ app.use(errorHandler()); /** * Start Express server. */ -const server = app.listen(app.get('port'), () => { +const server = app.listen(app.get('port'), async () => { const winstonLogger = container.get('Contexts.shared.Logger'); + await seed(); + winstonLogger.info( ` Backoffice frontend is running at http://localhost:${app.get('port')} in ${app.get('env')} mode` ); diff --git a/src/apps/mooc_backend/config/config.ts b/src/apps/mooc_backend/config/config.ts index b679f61..bd561bb 100644 --- a/src/apps/mooc_backend/config/config.ts +++ b/src/apps/mooc_backend/config/config.ts @@ -12,7 +12,7 @@ const convictConfig = convict({ doc: 'The Mongo connection URL', format: String, env: 'MONGO_URL', - default: 'mongodb://localhost:27017/dev' + default: 'mongodb://localhost:27017/mooc-backend-dev' } } }); diff --git a/src/apps/mooc_backend/config/test.json b/src/apps/mooc_backend/config/test.json index fcef01b..c18fdd4 100644 --- a/src/apps/mooc_backend/config/test.json +++ b/src/apps/mooc_backend/config/test.json @@ -1,3 +1,3 @@ { - "mongo": { "url": "mongodb://localhost:27017/test" } + "mongo": { "url": "mongodb://localhost:27017/mooc-backend-test" } } From 8c010529019a28dde3ffcdc1034566273aa6af7d Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Fri, 4 Sep 2020 17:30:35 +0200 Subject: [PATCH 29/48] Add notifications module with application service and domain event subscriber --- .../SendWelcomeUserEmail.ts | 17 +++++ .../SendWelcomeUserEmailOnUserRegistered.ts | 18 +++++ .../Mooc/Notifications/domain/Email.ts | 37 +++++++++++ .../Mooc/Notifications/domain/EmailAddress.ts | 3 + .../Mooc/Notifications/domain/EmailId.ts | 3 + .../Mooc/Notifications/domain/EmailSender.ts | 5 ++ .../domain/UserRegisteredDomainEvent.ts | 37 +++++++++++ .../Notifications/domain/WelcomeUserEmail.ts | 13 ++++ .../domain/WelcomeUserEmailError.ts | 7 ++ .../__mocks__/EmailSenderMock.ts | 22 +++++++ ...ndWelcomeUserEmailOnUserRegistered.test.ts | 66 +++++++++++++++++++ 11 files changed, 228 insertions(+) create mode 100644 src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts create mode 100644 src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/Email.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/EmailAddress.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/EmailId.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/EmailSender.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/UserRegisteredDomainEvent.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/WelcomeUserEmail.ts create mode 100644 src/Contexts/Mooc/Notifications/domain/WelcomeUserEmailError.ts create mode 100644 tests/Contexts/Mooc/Notifications/__mocks__/EmailSenderMock.ts create mode 100644 tests/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.test.ts diff --git a/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts b/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts new file mode 100644 index 0000000..140e081 --- /dev/null +++ b/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts @@ -0,0 +1,17 @@ +import { EmailSender } from '../../domain/EmailSender'; +import { EmailAddress } from '../../domain/EmailAddress'; +import { WelcomeUserEmail } from '../../domain/WelcomeUserEmail'; +import { WelcomeUserEmailError } from '../../domain/WelcomeUserEmailError'; + +export default class SendWelcomeUserEmail { + constructor(private emailSender: EmailSender) {} + + async run(userEmailAddress: EmailAddress): Promise { + const welcomeUserEmail = new WelcomeUserEmail(userEmailAddress); + try { + await this.emailSender.send(welcomeUserEmail); + } catch (error) { + throw new WelcomeUserEmailError(userEmailAddress); + } + } +} diff --git a/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts b/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts new file mode 100644 index 0000000..785e76b --- /dev/null +++ b/src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts @@ -0,0 +1,18 @@ +import { DomainEventSubscriber } from '../../../../Shared/domain/DomainEventSubscriber'; +import { UserRegisteredDomainEvent } from '../../domain/UserRegisteredDomainEvent'; +import { DomainEventClass } from '../../../../Shared/domain/DomainEvent'; +import SendWelcomeUserEmail from './SendWelcomeUserEmail'; +import { EmailAddress } from '../../domain/EmailAddress'; + +export default class SendWelcomeUserEmailOnUserRegistered implements DomainEventSubscriber { + constructor(private sendWelcomeUserEmail: SendWelcomeUserEmail) {} + + subscribedTo(): DomainEventClass[] { + return [UserRegisteredDomainEvent]; + } + + async on(domainEvent: UserRegisteredDomainEvent): Promise { + const userEmailAddress = new EmailAddress(domainEvent.userEmailAddress); + await this.sendWelcomeUserEmail.run(userEmailAddress); + } +} diff --git a/src/Contexts/Mooc/Notifications/domain/Email.ts b/src/Contexts/Mooc/Notifications/domain/Email.ts new file mode 100644 index 0000000..673dc3c --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/Email.ts @@ -0,0 +1,37 @@ +import { EmailAddress } from './EmailAddress'; +import { EmailId } from './EmailId'; +import { Uuid } from '../../../Shared/domain/value-object/Uuid'; + +type ConstructorParams = { + id?: EmailId; + from: EmailAddress; + to: EmailAddress; + subject: string; + body: string; +}; + +export class Email { + readonly id: EmailId; + readonly from: EmailAddress; + readonly to: EmailAddress; + readonly subject: string; + readonly body: string; + + constructor(params: ConstructorParams) { + this.id = params.id || new EmailId(Uuid.random().value); + this.from = params.from; + this.to = params.to; + this.subject = params.subject; + this.body = params.body; + } + + equals(otherEmail: Email): boolean { + return ( + this.id.value === otherEmail.id.value && + this.from.value === otherEmail.from.value && + this.to.value === otherEmail.to.value && + this.subject === otherEmail.subject && + this.body === otherEmail.body + ); + } +} diff --git a/src/Contexts/Mooc/Notifications/domain/EmailAddress.ts b/src/Contexts/Mooc/Notifications/domain/EmailAddress.ts new file mode 100644 index 0000000..f014743 --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/EmailAddress.ts @@ -0,0 +1,3 @@ +import { StringValueObject } from '../../../Shared/domain/value-object/StringValueObject'; + +export class EmailAddress extends StringValueObject {} diff --git a/src/Contexts/Mooc/Notifications/domain/EmailId.ts b/src/Contexts/Mooc/Notifications/domain/EmailId.ts new file mode 100644 index 0000000..3ba583c --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/EmailId.ts @@ -0,0 +1,3 @@ +import { Uuid } from '../../../Shared/domain/value-object/Uuid'; + +export class EmailId extends Uuid {} diff --git a/src/Contexts/Mooc/Notifications/domain/EmailSender.ts b/src/Contexts/Mooc/Notifications/domain/EmailSender.ts new file mode 100644 index 0000000..5a96e50 --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/EmailSender.ts @@ -0,0 +1,5 @@ +import { Email } from './Email'; + +export interface EmailSender { + send(email: Email): Promise; +} diff --git a/src/Contexts/Mooc/Notifications/domain/UserRegisteredDomainEvent.ts b/src/Contexts/Mooc/Notifications/domain/UserRegisteredDomainEvent.ts new file mode 100644 index 0000000..e23cd4e --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/UserRegisteredDomainEvent.ts @@ -0,0 +1,37 @@ +import { DomainEvent } from '../../../Shared/domain/DomainEvent'; + +type UserRegisteredDomainEventBody = { userEmailAddress: string }; + +export class UserRegisteredDomainEvent extends DomainEvent { + static readonly EVENT_NAME = 'user.registered'; + readonly userEmailAddress: string; + + constructor(data: { + id: string; + userEmailAddress: string; + eventId?: string; + occurredOn?: Date; + }) { + const { id, eventId, occurredOn, userEmailAddress } = data; + super(UserRegisteredDomainEvent.EVENT_NAME, id, eventId, occurredOn); + this.userEmailAddress = userEmailAddress; + } + + toPrimitive(): Object { + return { userEmailAddress: this.userEmailAddress }; + } + + static fromPrimitives( + aggregateId: string, + body: UserRegisteredDomainEventBody, + eventId: string, + occurredOn: Date + ): DomainEvent { + return new UserRegisteredDomainEvent({ + id: aggregateId, + userEmailAddress: body.userEmailAddress, + eventId, + occurredOn + }); + } +} diff --git a/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmail.ts b/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmail.ts new file mode 100644 index 0000000..22a2fd9 --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmail.ts @@ -0,0 +1,13 @@ +import { Email } from './Email'; +import { EmailAddress } from './EmailAddress'; + +export class WelcomeUserEmail extends Email { + constructor(to: EmailAddress) { + super({ + from: new EmailAddress('welcome@foo.com'), + to, + subject: 'Welcome', + body: 'Welcome to our platform' + }); + } +} diff --git a/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmailError.ts b/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmailError.ts new file mode 100644 index 0000000..ef6eddc --- /dev/null +++ b/src/Contexts/Mooc/Notifications/domain/WelcomeUserEmailError.ts @@ -0,0 +1,7 @@ +import { EmailAddress } from './EmailAddress'; + +export class WelcomeUserEmailError extends Error { + constructor(userEmailAddress: EmailAddress) { + super(`Error sending WelcomeUser email to ${userEmailAddress.value}`); + } +} diff --git a/tests/Contexts/Mooc/Notifications/__mocks__/EmailSenderMock.ts b/tests/Contexts/Mooc/Notifications/__mocks__/EmailSenderMock.ts new file mode 100644 index 0000000..083d204 --- /dev/null +++ b/tests/Contexts/Mooc/Notifications/__mocks__/EmailSenderMock.ts @@ -0,0 +1,22 @@ +import { EmailSender } from '../../../../../src/Contexts/Mooc/Notifications/domain/EmailSender'; +import { Email } from '../../../../../src/Contexts/Mooc/Notifications/domain/Email'; + +export class EmailSenderMock implements EmailSender { + private sendSpy = jest.fn(); + + async send(email: Email): Promise { + this.sendSpy(email); + } + + assertSentTimes(times: number): void { + expect(this.sendSpy.mock.calls.length).toBe(times); + } + + lastEmailSent(): Email { + const sendCalls = this.sendSpy.mock.calls; + const lastSendCall = sendCalls[sendCalls.length - 1] || []; + const lastEmailSent = lastSendCall[0] as Email; + + return lastEmailSent; + } +} diff --git a/tests/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.test.ts b/tests/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.test.ts new file mode 100644 index 0000000..1755526 --- /dev/null +++ b/tests/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.test.ts @@ -0,0 +1,66 @@ +import faker from 'faker'; +import { Email } from '../../../../../../src/Contexts/Mooc/Notifications/domain/Email'; +import { EmailAddress } from '../../../../../../src/Contexts/Mooc/Notifications/domain/EmailAddress'; +import { EmailSender } from '../../../../../../src/Contexts/Mooc/Notifications/domain/EmailSender'; +import { EmailSenderMock } from '../../__mocks__/EmailSenderMock'; +import SendWelcomeUserEmail from '../../../../../../src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail'; +import SendWelcomeUserEmailOnUserRegistered from '../../../../../../src/Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered'; +import { UserRegisteredDomainEvent } from '../../../../../../src/Contexts/Mooc/Notifications/domain/UserRegisteredDomainEvent'; +import { UuidMother } from '../../../../../Contexts/Shared/domain/UuidMother'; +import { WelcomeUserEmail } from '../../../../../../src/Contexts/Mooc/Notifications/domain/WelcomeUserEmail'; +import { WelcomeUserEmailError } from '../../../../../../src/Contexts/Mooc/Notifications/domain/WelcomeUserEmailError'; + +describe('SendWelcomeUserEmailOnUserRegistered event handler', () => { + it('sends a welcome email to the user', async () => { + const emailSenderMock = new EmailSenderMock(); + const sendWelcomeUserEmail = new SendWelcomeUserEmail(emailSenderMock); + const sendWelcomeUserEmailOnUserRegistered = new SendWelcomeUserEmailOnUserRegistered(sendWelcomeUserEmail); + const userEmailAddress = anEmailAddress(); + const domainEvent = aDomainEventWithEmailAddress(userEmailAddress); + + await sendWelcomeUserEmailOnUserRegistered.on(domainEvent); + + const lastEmailSent = emailSenderMock.lastEmailSent(); + emailSenderMock.assertSentTimes(1); + expect(lastEmailSent).toBeInstanceOf(WelcomeUserEmail); + expect(lastEmailSent.to).toEqual(userEmailAddress); + }); + + it('throws a WelcomeUserEmailError if the emailSender fails', async () => { + const failingEmailSender = aFailingEmailSender(); + const sendWelcomeUserEmail = new SendWelcomeUserEmail(failingEmailSender); + const sendWelcomeUserEmailOnUserRegistered = new SendWelcomeUserEmailOnUserRegistered(sendWelcomeUserEmail); + + const domainEvent = aDomainEventWithEmailAddress(anEmailAddress()); + let error; + + try { + await sendWelcomeUserEmailOnUserRegistered.on(domainEvent); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error).toBeInstanceOf(WelcomeUserEmailError); + expect(error.message).toBe(`Error sending WelcomeUser email to ${domainEvent.userEmailAddress}`); + }); +}); + +function aFailingEmailSender() { + return { + async send(email: Email) { + throw new Error('some error'); + } + } as EmailSender; +} + +function anEmailAddress(): EmailAddress { + return new EmailAddress(faker.internet.email()); +} + +function aDomainEventWithEmailAddress(emailAddress: EmailAddress) { + return new UserRegisteredDomainEvent({ + id: UuidMother.random(), + userEmailAddress: emailAddress.value + }); +} From d164cc1f4c57185ec4da736e26f5d5905d021f90 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Fri, 4 Sep 2020 17:41:36 +0200 Subject: [PATCH 30/48] Add DI and fake email sender --- .../infrastructure/FakeEmailSender.ts | 8 ++++++++ .../Notifications/application.yaml | 14 ++++++++++++++ .../config/dependency-injection/application.yaml | 1 + 3 files changed, 23 insertions(+) create mode 100644 src/Contexts/Mooc/Notifications/infrastructure/FakeEmailSender.ts create mode 100644 src/apps/mooc_backend/config/dependency-injection/Notifications/application.yaml diff --git a/src/Contexts/Mooc/Notifications/infrastructure/FakeEmailSender.ts b/src/Contexts/Mooc/Notifications/infrastructure/FakeEmailSender.ts new file mode 100644 index 0000000..e148b9f --- /dev/null +++ b/src/Contexts/Mooc/Notifications/infrastructure/FakeEmailSender.ts @@ -0,0 +1,8 @@ +import { EmailSender } from '../domain/EmailSender'; +import { Email } from '../domain/Email'; + +export default class FakeEmailSender implements EmailSender { + async send(email: Email): Promise { + // do nothing + } +} diff --git a/src/apps/mooc_backend/config/dependency-injection/Notifications/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Notifications/application.yaml new file mode 100644 index 0000000..ec4ae95 --- /dev/null +++ b/src/apps/mooc_backend/config/dependency-injection/Notifications/application.yaml @@ -0,0 +1,14 @@ +services: + Mooc.notifications.EmailSender: + class: ../../../../../Contexts/Mooc/Notifications/infrastructure/FakeEmailSender + arguments: [] + + Mooc.notifications.SendWelcomeUserEmail: + class: ../../../../../Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmail + arguments: ["@Mooc.notifications.EmailSender"] + + Mooc.notifications.SendWelcomeUserEmailOnUserRegistered: + class: ../../../../../Contexts/Mooc/Notifications/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered + arguments: ["@Mooc.notifications.SendWelcomeUserEmail"] + tags: + - { name: 'domainEventSubscriber' } \ No newline at end of file diff --git a/src/apps/mooc_backend/config/dependency-injection/application.yaml b/src/apps/mooc_backend/config/dependency-injection/application.yaml index 1bd153d..ba8eb1f 100644 --- a/src/apps/mooc_backend/config/dependency-injection/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/application.yaml @@ -2,4 +2,5 @@ imports: - { resource: ./Shared/application.yaml } - { resource: ./Courses/application.yaml } - { resource: ./CoursesCounter/application.yaml } + - { resource: ./Notifications/application.yaml } - { resource: ./apps/application.yaml } From 0a26b7d9b7df73cb7e12755dd56c66c8746c9099 Mon Sep 17 00:00:00 2001 From: David Matas Date: Tue, 15 Sep 2020 20:52:09 +0200 Subject: [PATCH 31/48] Validate courses request --- package-lock.json | 21 +++++++ package.json | 1 + .../controllers/CoursesGetController.ts | 12 ++-- .../controllers/CoursesPostController.ts | 30 ++++++--- .../frontend/controllers/WebController.ts | 61 +++++++++++++++++++ .../frontend/routes/courses.route.ts | 2 +- .../courses/partials/new_course_form.html | 6 +- 7 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 src/apps/backoffice/frontend/controllers/WebController.ts diff --git a/package-lock.json b/package-lock.json index 496e635..f220c86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2961,6 +2961,22 @@ "vary": "~1.1.2" } }, + "express-validator": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.6.1.tgz", + "integrity": "sha512-+MrZKJ3eGYXkNF9p9Zf7MS7NkPJFg9MDYATU5c80Cf4F62JdLBIjWxy6481tRC0y1NnC9cgOw8FuN364bWaGhA==", + "requires": { + "lodash": "^4.17.19", + "validator": "^13.1.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -8493,6 +8509,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz", + "integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index d3456e2..9a9d490 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "copy": "^0.3.2", "errorhandler": "^1.5.1", "express": "^4.17.1", + "express-validator": "^6.6.1", "glob": "^7.1.6", "helmet": "^3.22.0", "http-status": "^1.4.2", diff --git a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts index 0900f3d..1c56d5c 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesGetController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesGetController.ts @@ -3,19 +3,21 @@ import { Uuid } from '../../../../Contexts/Shared/domain/value-object/Uuid'; import { QueryBus } from '../../../../Contexts/Shared/domain/QueryBus'; import { FindCoursesCounterResponse } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterResponse'; import { FindCoursesCounterQuery } from '../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQuery'; +import { WebController } from './WebController'; -export class CoursesGetController { - constructor(private queryBus: QueryBus) {} +export class CoursesGetController extends WebController { + constructor(private queryBus: QueryBus) { + super(); + } async run(req: Request, res: Response) { const courses = await this.queryBus.ask(new FindCoursesCounterQuery()); - res.render('pages/courses/courses', { + this.render(req, res, 'pages/courses/courses', { title: 'Welcome', description: 'CodelyTV - Backoffice', courses_counter: courses.total, - new_course_id: Uuid.random(), - flash: req.flash() + id: Uuid.random().value }); } } diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts index f99a1bb..f7cd3ff 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -1,15 +1,29 @@ import { Request, Response } from 'express'; import { CommandBus } from '../../../../Contexts/Shared/domain/CommandBus'; import { CreateCourseCommand } from '../../../../Contexts/Mooc/Courses/application/CreateCourseCommand'; +import { body, ValidationChain } from 'express-validator'; +import { WebController } from './WebController'; -export class CoursesPostController { - constructor(private commandBus: CommandBus) {} +export class CoursesPostController extends WebController { + constructor(private commandBus: CommandBus) { + super(); + } - async run(req: Request, res: Response) { - // TODO: validation - // req.flash('errors.id', 'Flash Message Added'); + static validator(): ValidationChain[] { + return [ + body('id').isUUID(), + body('name').isLength({ min: 1, max: 255 }), + body('duration').isLength({ min: 4, max: 100 }) + ]; + } - await this.createCourse(req, res); + async run(req: Request, res: Response) { + const errors = this.validateRequest(req); + if (errors.length === 0) { + await this.createCourse(req, res); + } else { + this.redirectWithErrors(req, res, errors); + } } private async createCourse(req: Request, res: Response) { @@ -18,9 +32,9 @@ export class CoursesPostController { name: req.body.name, duration: req.body.duration }); + await this.commandBus.dispatch(createCourseCommand); - req.flash('message', `Felicidades, el curso ${req.body.name} ha sido creado!`); - res.redirect('/courses'); + this.redirectWithMessage(req, res, '/courses', `Felicidades, el curso ${req.body.name} ha sido creado!`); } } diff --git a/src/apps/backoffice/frontend/controllers/WebController.ts b/src/apps/backoffice/frontend/controllers/WebController.ts new file mode 100644 index 0000000..ae64abb --- /dev/null +++ b/src/apps/backoffice/frontend/controllers/WebController.ts @@ -0,0 +1,61 @@ +import { Request, Response } from 'express'; +import { validationResult, ValidationError, matchedData } from 'express-validator'; + +export abstract class WebController { + protected validateRequest(req: Request): Array { + const errors = validationResult(req); + return errors.array(); + } + + private setFlashMessage(req: Request, message: { key: string; value: string }) { + req.flash(message.key, message.value); + } + + protected redirectWithErrors(req: Request, res: Response, errors: ValidationError[]) { + const validFields = this.getValidFields(req); + + errors.forEach(e => { + this.setFlashMessage(req, { key: `errors.${e.param}`, value: e.msg }); + }); + + validFields.forEach(field => { + this.setFlashMessage(req, { key: `inputs.${field.param}`, value: field.value }); + }); + + res.redirect(req.originalUrl); + } + + protected redirectWithMessage(req: Request, res: Response, route: string, message: string) { + this.setFlashMessage(req, { key: 'message', value: message }); + res.redirect(route); + } + + private getValidFields(req: Request): Array<{ value: any; param: string }> { + const validData = matchedData(req, { onlyValidData: false }); + return Object.keys(validData).map(key => ({ + param: key, + value: validData[key] + })); + } + + protected render(req: Request, res: Response, template: string, data: { [key: string]: any }) { + const flash = this.feedFlash(req, data); + res.render('pages/courses/courses', { + ...data, + ...flash + }); + } + + private feedFlash(req: Request, data: { [key: string]: any }) { + const rawFlash = req.flash(); + const flashResponse = Object.keys(data).reduce((flash, key) => { + flash[`inputs.${key}`] = flash[`inputs.${key}`] || data[key]; + return flash; + }, rawFlash); + return { + flash: flashResponse + }; + } + + abstract run(req: Request, res: Response): Promise; +} diff --git a/src/apps/backoffice/frontend/routes/courses.route.ts b/src/apps/backoffice/frontend/routes/courses.route.ts index 1bf8aa9..c936658 100644 --- a/src/apps/backoffice/frontend/routes/courses.route.ts +++ b/src/apps/backoffice/frontend/routes/courses.route.ts @@ -12,5 +12,5 @@ export const register = (app: Express) => { ); app.get('/courses', coursesGetController.run.bind(coursesGetController)); - app.post('/courses', coursesPostController.run.bind(coursesPostController)); + app.post('/courses', CoursesPostController.validator(), coursesPostController.run.bind(coursesPostController)); }; diff --git a/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html index e151e98..167a3fd 100644 --- a/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html +++ b/src/apps/backoffice/frontend/templates/pages/courses/partials/new_course_form.html @@ -7,7 +7,7 @@

+ value="{{flash['inputs.id']}}"> {% if flash['errors.id'] %}

{{ flash['errors.id'] }}

@@ -21,7 +21,7 @@

+ value="{{flash['inputs.name']}}"> {% if flash['errors.name'] %}

{{ flash['errors.name'] }}

@@ -33,7 +33,7 @@

+ value="{{flash['inputs.duration']}}"> {% if flash['errors.duration'] %}

{{ flash['errors.duration'] }}

{% endif %} From c38834663cc21aef467cd3c935ba25cc31efd584 Mon Sep 17 00:00:00 2001 From: Fran Ortiz Date: Wed, 16 Sep 2020 17:33:25 +0200 Subject: [PATCH 32/48] Add increment courses counter subscriber to backoffice frontend --- .../infrastructure/EventBus/EventEmitterBus.ts | 2 +- src/apps/backoffice/frontend/app.ts | 2 ++ .../config/dependency-injection/application.yaml | 13 +++++++++++++ src/apps/backoffice/frontend/subscribers.ts | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/apps/backoffice/frontend/subscribers.ts diff --git a/src/Contexts/Shared/infrastructure/EventBus/EventEmitterBus.ts b/src/Contexts/Shared/infrastructure/EventBus/EventEmitterBus.ts index 4cf309f..d528909 100644 --- a/src/Contexts/Shared/infrastructure/EventBus/EventEmitterBus.ts +++ b/src/Contexts/Shared/infrastructure/EventBus/EventEmitterBus.ts @@ -17,7 +17,7 @@ export class EventEmitterBus extends EventEmitter { private registerSubscriber(subscriber: DomainEventSubscriber) { subscriber.subscribedTo().map(event => { - this.on(event.EVENT_NAME, subscriber.on); + this.on(event.EVENT_NAME, subscriber.on.bind(subscriber)); }); } diff --git a/src/apps/backoffice/frontend/app.ts b/src/apps/backoffice/frontend/app.ts index e306c85..5004021 100644 --- a/src/apps/backoffice/frontend/app.ts +++ b/src/apps/backoffice/frontend/app.ts @@ -8,6 +8,7 @@ import cookieSession from 'cookie-session'; import cookieParser from 'cookie-parser'; import flash from 'connect-flash'; import nunjucks from 'nunjucks'; +import { registerSubscribers } from './subscribers'; const app: express.Express = express(); @@ -43,5 +44,6 @@ app.use(helmet.frameguard({ action: 'deny' })); app.use(compress()); registerRoutes(app); +registerSubscribers(); export default app; diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index 16fcec8..960ee96 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -46,6 +46,19 @@ services: tags: - { name: 'queryHandler' } + Mooc.coursesCounter.CoursesCounterIncrementer: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer + arguments: [ + "@Mooc.coursesCounter.CoursesCounterRepository", + "@Mooc.shared.EventBus" + ] + + Mooc.coursesCounter.IncrementCoursesCounterOnCourseCreated: + class: ../../../../../Contexts/Mooc/CoursesCounter/application/Increment/IncrementCoursesCounterOnCourseCreated + arguments: ["@Mooc.coursesCounter.CoursesCounterIncrementer"] + tags: + - { name: 'domainEventSubscriber' } + Mooc.courses.CreateCourseCommandHandler: class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler arguments: ['@Mooc.courses.CourseCreator'] diff --git a/src/apps/backoffice/frontend/subscribers.ts b/src/apps/backoffice/frontend/subscribers.ts new file mode 100644 index 0000000..4c723a7 --- /dev/null +++ b/src/apps/backoffice/frontend/subscribers.ts @@ -0,0 +1,14 @@ +import container from './config/dependency-injection'; +import { InMemoryAsyncEventBus } from '../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus'; +import { Definition } from 'node-dependency-injection'; +import { DomainEventSubscriber } from '../../../Contexts/Shared/domain/DomainEventSubscriber'; +import { DomainEvent } from '../../../Contexts/Shared/domain/DomainEvent'; + +export function registerSubscribers() { + const eventBus = container.get('Mooc.shared.EventBus') as InMemoryAsyncEventBus; + const subscriberDefinitions = container.findTaggedServiceIds('domainEventSubscriber') as Map; + const subscribers: Array> = []; + + subscriberDefinitions.forEach((value: any, key: any) => subscribers.push(container.get(key))); + eventBus.addSubscribers(subscribers); +} From 8a6455e993148cb85de60ceae5fb71bc81cc2fef Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Wed, 16 Sep 2020 18:09:22 +0200 Subject: [PATCH 33/48] Bump required nodejs version to 12.x --- .nvmrc | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..83f2a92 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v12.18.3 diff --git a/package.json b/package.json index 9a9d490..47c7fa2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "license": "", "engines": { - "node": ">=10.15.0", + "node": ">=12.0.0", "npm": ">=6.7.0" }, "scripts": { From aae93bab45357c804dfd09758efb7afca761fe4f Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Wed, 16 Sep 2020 18:10:10 +0200 Subject: [PATCH 34/48] Refactor backoffice fronted dependency inyection --- package-lock.json | 34 ++++++-- package.json | 2 +- .../Courses/application.yaml | 14 +++ .../CoursesCounter/application.yaml | 24 +++++ .../Shared/application.yaml | 30 +++++++ .../dependency-injection/application.yaml | 87 ++----------------- .../apps/application.yaml | 11 +++ src/apps/backoffice/frontend/seed.ts | 2 +- src/apps/backoffice/frontend/server.ts | 2 +- src/apps/backoffice/frontend/subscribers.ts | 2 +- .../Courses/application.yaml | 6 +- .../CoursesCounter/application.yaml | 4 +- .../Shared/application.yaml | 24 ++--- .../application_test.yaml | 4 +- .../apps/application.yaml | 4 +- src/apps/mooc_backend/server.ts | 2 +- src/apps/mooc_backend/subscribers.ts | 2 +- .../step_definitions/evenBus.steps.ts | 4 +- 18 files changed, 141 insertions(+), 117 deletions(-) create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/Courses/application.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/CoursesCounter/application.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml create mode 100644 src/apps/backoffice/frontend/config/dependency-injection/apps/application.yaml diff --git a/package-lock.json b/package-lock.json index f220c86..7ef54e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1823,6 +1823,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5031,6 +5032,7 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5920,15 +5922,35 @@ "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, "node-dependency-injection": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/node-dependency-injection/-/node-dependency-injection-2.6.3.tgz", - "integrity": "sha512-dUDqVaV6DjZb5dXPkkrC2E1YGf5U7r0y/ZRMak5dz/Rhr8B2j5Gcvcn9XazLnd422WECPcAJbVhwmt52xKlhQw==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-dependency-injection/-/node-dependency-injection-2.6.8.tgz", + "integrity": "sha512-IYHPG2b5sH5Wh+xB2YGYD1UtjmmVgjrMvOTXEhQcifFKCcJwDu5Op5jVafCZ0iBw1VjiLpnU1m8lormamZCsIw==", "requires": { - "chalk": "^4.0.0", - "commander": "^5.0.0", + "chalk": "^4.1.0", + "commander": "^5.1.0", "console.table": "^0.10.0", "fs": "^0.0.2", - "js-yaml": "^3.13.1" + "js-yaml": "^3.14.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } } }, "node-int64": { diff --git a/package.json b/package.json index 47c7fa2..fda7251 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "http-status": "^1.4.2", "mandrill-api": "^1.0.45", "mongodb": "^3.5.7", - "node-dependency-injection": "^2.6.3", + "node-dependency-injection": "^2.6.8", "nunjucks": "^3.2.1", "ts-node": "^8.10.1", "typescript": "^3.9.2", diff --git a/src/apps/backoffice/frontend/config/dependency-injection/Courses/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/Courses/application.yaml new file mode 100644 index 0000000..40fddc3 --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/Courses/application.yaml @@ -0,0 +1,14 @@ +services: + Mooc.courses.CourseRepository: + class: ../../../../../../Contexts/Mooc/Courses/infrastructure/persistence/MongoCourseRepository + arguments: ['@Shared.ConnectionManager'] + + Mooc.courses.CourseCreator: + class: ../../../../../../Contexts/Mooc/Courses/application/CourseCreator + arguments: ['@Mooc.courses.CourseRepository', '@Shared.EventBus'] + + Mooc.courses.CreateCourseCommandHandler: + class: ../../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler + arguments: ['@Mooc.courses.CourseCreator'] + tags: + - { name: 'commandHandler' } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/CoursesCounter/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/CoursesCounter/application.yaml new file mode 100644 index 0000000..3f1f0ba --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/CoursesCounter/application.yaml @@ -0,0 +1,24 @@ +services: + Mooc.coursesCounter.CoursesCounterFinder: + class: ../../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder + arguments: ['@Mooc.coursesCounter.CoursesCounterRepository'] + + Mooc.coursesCounter.CoursesCounterRepository: + class: ../../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository + arguments: ['@Shared.ConnectionManager'] + + Mooc.coursesCounter.FindCoursesCounterQueryHandler: + class: ../../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler + arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] + tags: + - { name: 'queryHandler' } + + Mooc.coursesCounter.CoursesCounterIncrementer: + class: ../../../../../../Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer + arguments: ['@Mooc.coursesCounter.CoursesCounterRepository', '@Shared.EventBus'] + + Mooc.coursesCounter.IncrementCoursesCounterOnCourseCreated: + class: ../../../../../../Contexts/Mooc/CoursesCounter/application/Increment/IncrementCoursesCounterOnCourseCreated + arguments: ['@Mooc.coursesCounter.CoursesCounterIncrementer'] + tags: + - { name: 'domainEventSubscriber' } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml new file mode 100644 index 0000000..813e2c5 --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml @@ -0,0 +1,30 @@ +services: + Shared.Logger: + class: ../../../../../../Contexts/Shared/infrastructure/WinstonLogger + arguments: [] + + Shared.ConnectionManager: + factory: + class: ../../../../../../Contexts/Shared/infrastructure/persistence/mongo/MongoClientFactory + method: 'createClient' + arguments: ['mooc'] + + Shared.EventBus: + class: ../../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus + arguments: [] + + Shared.QueryBus: + class: ../../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus + arguments: ['@Shared.QueryHandlersInformation'] + + Shared.CommandBus: + class: ../../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus + arguments: ['@Shared.CommandHandlersInformation'] + + Shared.QueryHandlersInformation: + class: ../../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation + arguments: ['!tagged queryHandler'] + + Shared.CommandHandlersInformation: + class: ../../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation + arguments: ['!tagged commandHandler'] diff --git a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml index 960ee96..3bd1a26 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/application.yaml @@ -1,82 +1,5 @@ -services: - Contexts.shared.Logger: - class: ../../../../../Contexts/Shared/infrastructure/WinstonLogger - arguments: [] - - Apps.Backoffice.Frontend.controllers.HomeGetController: - class: ../../controllers/HomeGetController - - Mooc.coursesCounter.CoursesCounterFinder: - class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/CoursesCounterFinder - arguments: ['@Mooc.coursesCounter.CoursesCounterRepository'] - - Mooc.coursesCounter.CoursesCounterRepository: - class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository - arguments: ['@Mooc.shared.ConnectionManager'] - - Mooc.shared.ConnectionManager: - factory: - class: ../../../../../Contexts/Shared/infrastructure/persistence/mongo/MongoClientFactory - method: 'createClient' - arguments: ['mooc'] - - Mooc.courses.CourseRepository: - class: ../../../../../Contexts/Mooc/Courses/infrastructure/persistence/MongoCourseRepository - arguments: ['@Mooc.shared.ConnectionManager'] - - Mooc.shared.EventBus: - class: ../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus - arguments: [] - - Mooc.courses.CourseCreator: - class: ../../../../../Contexts/Mooc/Courses/application/CourseCreator - arguments: ['@Mooc.courses.CourseRepository', '@Mooc.shared.EventBus'] - - Apps.Backoffice.Frontend.controllers.CoursesGetController: - class: ../../controllers/CoursesGetController - arguments: ['@Mooc.shared.QueryBus'] - - Apps.Backoffice.Frontend.controllers.CoursesPostController: - class: ../../controllers/CoursesPostController - arguments: ['@Mooc.shared.CommandBus'] - - Mooc.coursesCounter.FindCoursesCounterQueryHandler: - class: ../../../../../Contexts/Mooc/CoursesCounter/application/Find/FindCoursesCounterQueryHandler - arguments: ['@Mooc.coursesCounter.CoursesCounterFinder'] - tags: - - { name: 'queryHandler' } - - Mooc.coursesCounter.CoursesCounterIncrementer: - class: ../../../../../Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer - arguments: [ - "@Mooc.coursesCounter.CoursesCounterRepository", - "@Mooc.shared.EventBus" - ] - - Mooc.coursesCounter.IncrementCoursesCounterOnCourseCreated: - class: ../../../../../Contexts/Mooc/CoursesCounter/application/Increment/IncrementCoursesCounterOnCourseCreated - arguments: ["@Mooc.coursesCounter.CoursesCounterIncrementer"] - tags: - - { name: 'domainEventSubscriber' } - - Mooc.courses.CreateCourseCommandHandler: - class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler - arguments: ['@Mooc.courses.CourseCreator'] - tags: - - { name: 'commandHandler' } - - Mooc.shared.QueryBus: - class: ../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus - arguments: ['@Mooc.shared.QueryHandlersInformation'] - - Mooc.shared.CommandBus: - class: ../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus - arguments: ['@Mooc.shared.CommandHandlersInformation'] - - Mooc.shared.QueryHandlersInformation: - class: ../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation - arguments: ['!tagged queryHandler'] - - Mooc.shared.CommandHandlersInformation: - class: ../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation - arguments: ['!tagged commandHandler'] +imports: + - { resource: ./apps/application.yaml } + - { resource: ./Shared/application.yaml } + - { resource: ./CoursesCounter/application.yaml } + - { resource: ./Courses/application.yaml } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/apps/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/apps/application.yaml new file mode 100644 index 0000000..3a37a59 --- /dev/null +++ b/src/apps/backoffice/frontend/config/dependency-injection/apps/application.yaml @@ -0,0 +1,11 @@ +services: + Apps.Backoffice.Frontend.controllers.HomeGetController: + class: ../../../controllers/HomeGetController + + Apps.Backoffice.Frontend.controllers.CoursesGetController: + class: ../../../controllers/CoursesGetController + arguments: ['@Shared.QueryBus'] + + Apps.Backoffice.Frontend.controllers.CoursesPostController: + class: ../../../controllers/CoursesPostController + arguments: ['@Shared.CommandBus'] diff --git a/src/apps/backoffice/frontend/seed.ts b/src/apps/backoffice/frontend/seed.ts index a1abc60..03f871f 100644 --- a/src/apps/backoffice/frontend/seed.ts +++ b/src/apps/backoffice/frontend/seed.ts @@ -5,7 +5,7 @@ import container from './config/dependency-injection'; export async function seed() { const repository: CoursesCounterRepository = container.get('Mooc.coursesCounter.CoursesCounterRepository'); - const logger = container.get('Contexts.shared.Logger'); + const logger = container.get('Shared.Logger'); const alreadyExists = await repository.search(); const isTestEnvironment = process.env.NODE_ENV === 'test'; diff --git a/src/apps/backoffice/frontend/server.ts b/src/apps/backoffice/frontend/server.ts index 576a1c5..f79e1c0 100644 --- a/src/apps/backoffice/frontend/server.ts +++ b/src/apps/backoffice/frontend/server.ts @@ -12,7 +12,7 @@ app.use(errorHandler()); * Start Express server. */ const server = app.listen(app.get('port'), async () => { - const winstonLogger = container.get('Contexts.shared.Logger'); + const winstonLogger = container.get('Shared.Logger'); await seed(); diff --git a/src/apps/backoffice/frontend/subscribers.ts b/src/apps/backoffice/frontend/subscribers.ts index 4c723a7..16cbe51 100644 --- a/src/apps/backoffice/frontend/subscribers.ts +++ b/src/apps/backoffice/frontend/subscribers.ts @@ -5,7 +5,7 @@ import { DomainEventSubscriber } from '../../../Contexts/Shared/domain/DomainEve import { DomainEvent } from '../../../Contexts/Shared/domain/DomainEvent'; export function registerSubscribers() { - const eventBus = container.get('Mooc.shared.EventBus') as InMemoryAsyncEventBus; + const eventBus = container.get('Shared.EventBus') as InMemoryAsyncEventBus; const subscriberDefinitions = container.findTaggedServiceIds('domainEventSubscriber') as Map; const subscribers: Array> = []; diff --git a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml index 8acb39e..b0ab7c8 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Courses/application.yaml @@ -2,14 +2,14 @@ services: Mooc.courses.CourseRepository: class: ../../../../../Contexts/Mooc/Courses/infrastructure/persistence/MongoCourseRepository - arguments: ['@Mooc.shared.ConnectionManager'] + arguments: ['@Shared.ConnectionManager'] Mooc.courses.CourseCreator: class: ../../../../../Contexts/Mooc/Courses/application/CourseCreator - arguments: ['@Mooc.courses.CourseRepository', '@Mooc.shared.EventBus'] + arguments: ['@Mooc.courses.CourseRepository', '@Shared.EventBus'] Mooc.courses.CreateCourseCommandHandler: class: ../../../../../Contexts/Mooc/Courses/application/CreateCourseCommandHandler arguments: ['@Mooc.courses.CourseCreator'] tags: - - { name: 'commandHandler' } \ No newline at end of file + - { name: 'commandHandler' } diff --git a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml index d4457d9..6501720 100644 --- a/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/CoursesCounter/application.yaml @@ -1,13 +1,13 @@ services: Mooc.coursesCounter.CoursesCounterRepository: class: ../../../../../Contexts/Mooc/CoursesCounter/infrastructure/persistence/mongo/MongoCoursesCounterRepository - arguments: ["@Mooc.shared.ConnectionManager"] + arguments: ["@Shared.ConnectionManager"] Mooc.coursesCounter.CoursesCounterIncrementer: class: ../../../../../Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer arguments: [ "@Mooc.coursesCounter.CoursesCounterRepository", - "@Mooc.shared.EventBus" + "@Shared.EventBus" ] Mooc.coursesCounter.IncrementCoursesCounterOnCourseCreated: diff --git a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml index 624a233..b3f8a05 100644 --- a/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/Shared/application.yaml @@ -1,38 +1,38 @@ services: - Mooc.shared.ConnectionManager: + Shared.ConnectionManager: factory: class: ../../../../../Contexts/Shared/infrastructure/persistence/mongo/MongoClientFactory method: 'createClient' arguments: ['mooc'] - Mooc.shared.Logger: + Shared.Logger: class: ../../../../../Contexts/Shared/infrastructure/WinstonLogger arguments: [] - Mooc.shared.EventBus: + Shared.EventBus: class: ../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus arguments: [] - Mooc.shared.CommandHandlersInformation: + Shared.CommandHandlersInformation: class: ../../../../../Contexts/Shared/infrastructure/CommandBus/CommandHandlersInformation arguments: ['!tagged commandHandler'] - Mooc.shared.CommandBus: + Shared.CommandBus: class: ../../../../../Contexts/Shared/infrastructure/CommandBus/InMemoryCommandBus - arguments: ['@Mooc.shared.CommandHandlersInformation'] + arguments: ['@Shared.CommandHandlersInformation'] - Mooc.shared.EventBus.DomainEventMapping: + Shared.EventBus.DomainEventMapping: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventMapping arguments: ['!tagged domainEventSubscriber'] - Mooc.shared.EventBus.DomainEventJsonDeserializer: + Shared.EventBus.DomainEventJsonDeserializer: class: ../../../../../Contexts/Shared/infrastructure/EventBus/DomainEventJsonDeserializer - arguments: ['@Mooc.shared.EventBus.DomainEventMapping'] + arguments: ['@Shared.EventBus.DomainEventMapping'] - Mooc.shared.QueryHandlersInformation: + Shared.QueryHandlersInformation: class: ../../../../../Contexts/Shared/infrastructure/QueryBus/QueryHandlersInformation arguments: ['!tagged queryHandler'] - Mooc.shared.QueryBus: + Shared.QueryBus: class: ../../../../../Contexts/Shared/infrastructure/QueryBus/InMemoryQueryBus - arguments: ['@Mooc.shared.QueryHandlersInformation'] + arguments: ['@Shared.QueryHandlersInformation'] diff --git a/src/apps/mooc_backend/config/dependency-injection/application_test.yaml b/src/apps/mooc_backend/config/dependency-injection/application_test.yaml index ad69269..7b87ced 100644 --- a/src/apps/mooc_backend/config/dependency-injection/application_test.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/application_test.yaml @@ -4,8 +4,8 @@ imports: services: Mooc.EnvironmentArranger: class: ../../../../../tests/Contexts/Shared/infrastructure/mongo/MongoEnvironmentArranger - arguments: ['@Mooc.shared.ConnectionManager'] + arguments: ['@Shared.ConnectionManager'] - Mooc.shared.EventBus: + Shared.EventBus: class: ../../../../Contexts/Shared/infrastructure/EventBus/InMemorySyncEventBus arguments: [] diff --git a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml index 131ee62..05e0acd 100644 --- a/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml +++ b/src/apps/mooc_backend/config/dependency-injection/apps/application.yaml @@ -2,7 +2,7 @@ services: Apps.mooc.controllers.CoursePutController: class: ../../../controllers/CoursePutController - arguments: ["@Mooc.shared.CommandBus"] + arguments: ["@Shared.CommandBus"] Apps.mooc.controllers.StatusGetController: class: ../../../controllers/StatusGetController @@ -10,4 +10,4 @@ services: Apps.mooc.controllers.CoursesCounterGetController: class: ../../../controllers/CoursesCounterGetController - arguments: ["@Mooc.shared.QueryBus"] + arguments: ["@Shared.QueryBus"] diff --git a/src/apps/mooc_backend/server.ts b/src/apps/mooc_backend/server.ts index e980480..fc072a8 100644 --- a/src/apps/mooc_backend/server.ts +++ b/src/apps/mooc_backend/server.ts @@ -12,7 +12,7 @@ app.use(errorHandler()); */ const server = app.listen(app.get('port'), () => { // tslint:disable: no-console - const logger = container.get('Mooc.shared.Logger'); + const logger = container.get('Shared.Logger'); logger.info(` App is running at http://localhost:${app.get('port')} in ${app.get('env')} mode`); console.log(' Press CTRL-C to stop\n'); diff --git a/src/apps/mooc_backend/subscribers.ts b/src/apps/mooc_backend/subscribers.ts index 290fcf3..6a7667c 100644 --- a/src/apps/mooc_backend/subscribers.ts +++ b/src/apps/mooc_backend/subscribers.ts @@ -5,7 +5,7 @@ import { DomainEventSubscriber } from '../../Contexts/Shared/domain/DomainEventS import { DomainEvent } from '../../Contexts/Shared/domain/DomainEvent'; export function registerSubscribers() { - const eventBus = container.get('Mooc.shared.EventBus') as InMemoryAsyncEventBus; + const eventBus = container.get('Shared.EventBus') as InMemoryAsyncEventBus; const subscriberDefinitions = container.findTaggedServiceIds('domainEventSubscriber') as Map; const subscribers: Array> = []; diff --git a/tests/apps/mooc_backend/features/step_definitions/evenBus.steps.ts b/tests/apps/mooc_backend/features/step_definitions/evenBus.steps.ts index 751ff59..22d10bb 100644 --- a/tests/apps/mooc_backend/features/step_definitions/evenBus.steps.ts +++ b/tests/apps/mooc_backend/features/step_definitions/evenBus.steps.ts @@ -3,8 +3,8 @@ import container from '../../../../../src/apps/mooc_backend/config/dependency-in import { EventBus } from '../../../../../src/Contexts/Shared/domain/EventBus'; import { DomainEventJsonDeserializer } from '../../../../../src/Contexts/Shared/infrastructure/EventBus/DomainEventJsonDeserializer'; -const eventBus = container.get('Mooc.shared.EventBus') as EventBus; -const deserializer = container.get('Mooc.shared.EventBus.DomainEventJsonDeserializer') as DomainEventJsonDeserializer; +const eventBus = container.get('Shared.EventBus') as EventBus; +const deserializer = container.get('Shared.EventBus.DomainEventJsonDeserializer') as DomainEventJsonDeserializer; Given('I send an event to the event bus:', async (event: any) => { const domainEvent = deserializer.deserialize(event); From 2bdb81f930982c3af9d6aae22510e9929a9065f5 Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Wed, 16 Sep 2020 18:33:13 +0200 Subject: [PATCH 35/48] Update dependencies --- package-lock.json | 2882 ++++++++++++++++++++++++++------------------- package.json | 52 +- 2 files changed, 1698 insertions(+), 1236 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ef54e9..8b6ac16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,33 +5,33 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", + "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.6", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.5", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.5", + "@babel/types": "^7.11.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" @@ -46,6 +46,12 @@ "ms": "^2.1.1" } }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -61,14 +67,13 @@ } }, "@babel/generator": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", - "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", "dev": true, "requires": { - "@babel/types": "^7.9.6", + "@babel/types": "^7.11.5", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { @@ -81,128 +86,136 @@ } }, "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.11.0" } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + } } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true }, "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -266,9 +279,9 @@ } }, "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -290,12 +303,21 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-json-strings": { @@ -308,12 +330,12 @@ } }, "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-nullish-coalescing-operator": { @@ -326,12 +348,12 @@ } }, "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-object-rest-spread": { @@ -372,31 +394,31 @@ } }, "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/traverse": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", - "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" }, "dependencies": { "debug": { @@ -408,6 +430,12 @@ "ms": "^2.1.1" } }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -417,14 +445,22 @@ } }, "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.5", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + } } }, "@bcoe/v8-coverage": { @@ -443,14 +479,25 @@ "minimist": "^1.2.0" } }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", + "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, @@ -470,60 +517,82 @@ "dev": true }, "@jest/console": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.0.1.tgz", - "integrity": "sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.3.0.tgz", + "integrity": "sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", + "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.0.1", - "jest-util": "^26.0.1", + "jest-message-util": "^26.3.0", + "jest-util": "^26.3.0", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/core": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", - "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.4.2.tgz", + "integrity": "sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg==", "dev": true, "requires": { - "@jest/console": "^26.0.1", - "@jest/reporters": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/console": "^26.3.0", + "@jest/reporters": "^26.4.1", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.0.1", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", + "jest-changed-files": "^26.3.0", + "jest-config": "^26.4.2", + "jest-haste-map": "^26.3.0", + "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-resolve-dependencies": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "jest-watcher": "^26.0.1", + "jest-resolve": "^26.4.0", + "jest-resolve-dependencies": "^26.4.2", + "jest-runner": "^26.4.2", + "jest-runtime": "^26.4.2", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", + "jest-watcher": "^26.3.0", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "rimraf": "^3.0.0", @@ -532,17 +601,37 @@ }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -555,133 +644,215 @@ } }, "@jest/environment": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.0.1.tgz", - "integrity": "sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz", + "integrity": "sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA==", "dev": true, "requires": { - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1" + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", + "jest-mock": "^26.3.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/fake-timers": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.0.1.tgz", - "integrity": "sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.3.0.tgz", + "integrity": "sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "@sinonjs/fake-timers": "^6.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" + "@types/node": "*", + "jest-message-util": "^26.3.0", + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/globals": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", - "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.4.2.tgz", + "integrity": "sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow==", "dev": true, "requires": { - "@jest/environment": "^26.0.1", - "@jest/types": "^26.0.1", - "expect": "^26.0.1" + "@jest/environment": "^26.3.0", + "@jest/types": "^26.3.0", + "expect": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/reporters": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", - "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", + "version": "26.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.1.tgz", + "integrity": "sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/console": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", "graceful-fs": "^4.2.4", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-instrument": "^4.0.3", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "node-notifier": "^7.0.0", + "jest-haste-map": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", + "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" + "v8-to-istanbul": "^5.0.1" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/source-map": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", - "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.3.0.tgz", + "integrity": "sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -690,60 +861,80 @@ } }, "@jest/test-result": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.0.1.tgz", - "integrity": "sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.3.0.tgz", + "integrity": "sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg==", "dev": true, "requires": { - "@jest/console": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/console": "^26.3.0", + "@jest/types": "^26.3.0", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/test-sequencer": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", - "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz", + "integrity": "sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog==", "dev": true, "requires": { - "@jest/test-result": "^26.0.1", + "@jest/test-result": "^26.3.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1" + "jest-haste-map": "^26.3.0", + "jest-runner": "^26.4.2", + "jest-runtime": "^26.4.2" } }, "@jest/transform": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", - "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.3.0.tgz", + "integrity": "sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "babel-plugin-istanbul": "^6.0.0", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", + "jest-haste-map": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-util": "^26.0.1", + "jest-util": "^26.3.0", "micromatch": "^4.0.2", "pirates": "^4.0.1", "slash": "^3.0.0", @@ -752,16 +943,36 @@ }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -775,33 +986,12 @@ "@types/istanbul-reports": "^1.1.1", "@types/yargs": "^15.0.0", "chalk": "^3.0.0" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", - "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" } }, "@sinonjs/commons": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz", - "integrity": "sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -817,15 +1007,15 @@ } }, "@types/aws-lambda": { - "version": "8.10.51", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.51.tgz", - "integrity": "sha512-XK7RerpXj4r+IO0r7qIeNqUSU6L4qhPMwNhISxozJJiUX/jdXj9WYzTShRVisEcUQHXgJ4TTBqTArM8f9Mjb8g==", + "version": "8.10.62", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.62.tgz", + "integrity": "sha512-43XhJY5jvyH03U0cRsSqeHWhvmGZNoLzTPSAZ3JAqgG9p20uw+Sx9Auk5OAefJajd9ZcfjDJ/9lJ2BiBxfKJRw==", "dev": true }, "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", + "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -855,9 +1045,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.14.tgz", + "integrity": "sha512-8w9szzKs14ZtBVuP6Wn7nMLRJ0D6dfB0VEBEyRgxrZ/Ln49aNMykrghM2FaNn4FJRzNppCSa0Rv9pBRM5Xc3wg==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -957,15 +1147,10 @@ "@types/express": "*" } }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" - }, "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz", + "integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "*", @@ -974,9 +1159,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz", - "integrity": "sha512-EMgTj/DF9qpgLXyc+Btimg+XoH7A2liE8uKul8qSmMTHCeNYzydDKFdsJskDvw42UsesCnhO63dO0Grbj8J4Dw==", + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz", + "integrity": "sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -984,17 +1169,16 @@ } }, "@types/faker": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-4.1.12.tgz", - "integrity": "sha512-0MEyzJrLLs1WaOCx9ULK6FzdCSj2EuxdSP9kvuxxdBEGujZYUOZ4vkPXdgu3dhyg/pOdn7VCatelYX7k0YShlA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.1.0.tgz", + "integrity": "sha512-7iK+rNvtSmG3FcAgI67BphmQVzBPkI6SCjJqR/SXxGr6tDUoMovkhGYIxNICEfy/trTgiGQL2v1tKPuFEXIrQg==", "dev": true }, "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", "requires": { - "@types/events": "*", "@types/minimatch": "*", "@types/node": "*" } @@ -1009,17 +1193,17 @@ } }, "@types/helmet": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.47.tgz", - "integrity": "sha512-TcHA/djjdUtrMtq/QAayVLrsgjNNZ1Uhtz0KhfH01mrmjH44E54DA1A0HNbwW0H/NBFqV+tGMo85ACuEhMXcdg==", + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.48.tgz", + "integrity": "sha512-C7MpnvSDrunS1q2Oy1VWCY7CDWHozqSnM8P4tFeRTuzwqni+PYOjEredwcqWG+kLpYcgLsgcY3orHB54gbx2Jw==", "requires": { "@types/express": "*" } }, "@types/istanbul-lib-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, "@types/istanbul-lib-report": { @@ -1042,9 +1226,9 @@ } }, "@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", + "version": "26.0.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.14.tgz", + "integrity": "sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg==", "dev": true, "requires": { "jest-diff": "^25.2.1", @@ -1058,9 +1242,9 @@ "dev": true }, "@types/mime": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", - "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" }, "@types/minimatch": { "version": "3.0.3", @@ -1068,18 +1252,18 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/mongodb": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.18.tgz", - "integrity": "sha512-fEmnRmwXt4pEFhqWB/ZlyNaDhLfQv5GALaZAlH9Pb0kEttvsCr66umJ9pfBEEP3ks1hjlwAuMtqk/+DyZDLAXQ==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.27.tgz", + "integrity": "sha512-1jxKDgdfJEOO9zp+lv43p8jOqRs02xPrdUTzAZIVK9tVEySfCEmktL2jEu9A3wOBEOs18yKzpVIKUh8b8ALk3w==", "requires": { "@types/bson": "*", "@types/node": "*" } }, "@types/node": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.3.tgz", - "integrity": "sha512-a8TR2N5VEJCL9HEJrAfwv3UI1bZq50HydowDDVV6pfnY7ZwG5Pjii+nSDhrDtGW3XKMoVKOgG8zS/Kv5j399uA==" + "version": "14.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.2.tgz", + "integrity": "sha512-IzMhbDYCpv26pC2wboJ4MMOa9GKtjplXfcAqrMeNJpUUwpM/2ATt2w1JPUXwS6spu856TvKZL2AOmeU2rAxskw==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -1100,15 +1284,15 @@ "dev": true }, "@types/prettier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.0.tgz", - "integrity": "sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.1.tgz", + "integrity": "sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ==", "dev": true }, "@types/qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==" + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" }, "@types/range-parser": { "version": "1.2.3", @@ -1116,9 +1300,9 @@ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, "@types/serve-static": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", - "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz", + "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==", "requires": { "@types/express-serve-static-core": "*", "@types/mime": "*" @@ -1143,9 +1327,9 @@ "dev": true }, "@types/superagent": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.7.tgz", - "integrity": "sha512-JSwNPgRYjIC4pIeOqLwWwfGj6iP1n5NE6kNBEbGx2V8H78xCPwx7QpNp9plaI30+W3cFEzJO7BIIsXE+dbtaGg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", "dev": true, "requires": { "@types/cookiejar": "*", @@ -1153,18 +1337,18 @@ } }, "@types/supertest": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.9.tgz", - "integrity": "sha512-0BTpWWWAO1+uXaP/oA0KW1eOZv4hc0knhrWowV06Gwwz3kqQxNO98fUFM2e15T+PdPRmOouNFrYvaBgdojPJ3g==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", "dev": true, "requires": { "@types/superagent": "*" } }, "@types/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" }, "@types/uuid-validate": { "version": "0.0.1", @@ -1192,9 +1376,9 @@ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" }, "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, "accepts": { @@ -1207,9 +1391,9 @@ } }, "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "acorn-globals": { @@ -1223,15 +1407,15 @@ } }, "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -1239,9 +1423,9 @@ } }, "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -1251,9 +1435,9 @@ } }, "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-escapes": { @@ -1301,12 +1485,6 @@ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -1413,12 +1591,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, "async-array-reduce": { "version": "0.2.1", @@ -1449,38 +1624,58 @@ "dev": true }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, "babel-jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", - "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz", + "integrity": "sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g==", "dev": true, "requires": { - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "@types/babel__core": "^7.1.7", "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.0.0", + "babel-preset-jest": "^26.3.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -1498,25 +1693,27 @@ } }, "babel-plugin-jest-hoist": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", - "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz", + "integrity": "sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA==", "dev": true, "requires": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", "@types/babel__traverse": "^7.0.6" } }, "babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz", + "integrity": "sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==", "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1527,13 +1724,13 @@ } }, "babel-preset-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", - "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz", + "integrity": "sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^26.0.0", - "babel-preset-current-node-syntax": "^0.1.2" + "babel-plugin-jest-hoist": "^26.2.0", + "babel-preset-current-node-syntax": "^0.1.3" } }, "balanced-match": { @@ -1629,15 +1826,14 @@ "dev": true }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "optional": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, "bl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1665,11 +1861,6 @@ "type-is": "~1.6.17" } }, - "bowser": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", - "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1712,9 +1903,9 @@ } }, "bson": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.0.4.tgz", - "integrity": "sha512-Ioi3TD0/1V3aI8+hPfC56TetYmzfq2H07jJa9A1lKTxWsFtHtYdLMGMXjtGEg9v0f72NSM07diRQEUNYhLupIA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.1.0.tgz", + "integrity": "sha512-xwNzRRsK2xmHvHuPESi0zVfgsm4edO47WdulNaShTriunNUMRDOAlKwpJc8zpkOXczIzbxUD7kFzhUGVYoZLDw==", "requires": { "buffer": "^5.1.0", "long": "^4.0.0" @@ -1799,11 +1990,6 @@ } } }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -1820,9 +2006,9 @@ "dev": true }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -1836,10 +2022,9 @@ "dev": true }, "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", - "optional": true, + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -1855,7 +2040,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "optional": true, "requires": { "is-glob": "^4.0.1" } @@ -1863,14 +2047,12 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "optional": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "optional": true, "requires": { "is-extglob": "^2.1.1" } @@ -2084,11 +2266,6 @@ "simple-swizzle": "^0.2.2" } }, - "colornames": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" - }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -2184,11 +2361,6 @@ "safe-buffer": "5.1.2" } }, - "content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==" - }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -2302,16 +2474,16 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", + "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", - "yaml": "^1.7.2" + "yaml": "^1.10.0" } }, "cross-spawn": { @@ -2443,11 +2615,6 @@ "assert-plus": "^1.0.0" } }, - "dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -2469,12 +2636,6 @@ "meow": "^3.3.0" } }, - "debounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", - "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2522,6 +2683,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "optional": true, "requires": { "clone": "^1.0.2" } @@ -2561,16 +2723,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, - "diagnostics": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", - "requires": { - "colorspace": "1.1.x", - "enabled": "1.0.x", - "kuler": "1.0.x" - } - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -2582,11 +2734,6 @@ "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", "dev": true }, - "dns-prefetch-control": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", - "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==" - }, "doctrine": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", @@ -2628,11 +2775,6 @@ } } }, - "dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" - }, "duration": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", @@ -2675,10 +2817,10 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "elegant-spinner": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-2.0.0.tgz", - "integrity": "sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA==", + "emittery": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", + "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==", "dev": true }, "emoji-regex": { @@ -2688,12 +2830,9 @@ "dev": true }, "enabled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", - "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", - "requires": { - "env-variable": "0.0.x" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "encodeurl": { "version": "1.0.2", @@ -2710,19 +2849,14 @@ } }, "enquirer": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz", - "integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { - "ansi-colors": "^3.2.1" + "ansi-colors": "^4.1.1" } }, - "env-variable": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", - "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2802,9 +2936,9 @@ "dev": true }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, "requires": { "esprima": "^4.0.1", @@ -2855,6 +2989,14 @@ "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + } } }, "exit": { @@ -2887,44 +3029,59 @@ } }, "expect": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.0.1.tgz", - "integrity": "sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.4.2.tgz", + "integrity": "sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-styles": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true } } }, - "expect-ct": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", - "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==" - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -3078,15 +3235,15 @@ "dev": true }, "faker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", - "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.1.0.tgz", + "integrity": "sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==", "dev": true }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { @@ -3115,15 +3272,10 @@ "bser": "2.1.1" } }, - "feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" - }, "fecha": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" }, "figures": { "version": "3.2.0", @@ -3172,15 +3324,6 @@ "through2": "^2.0.1" } }, - "filewatcher": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", - "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=", - "dev": true, - "requires": { - "debounce": "^1.0.0" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3222,6 +3365,11 @@ "semver-regex": "^2.0.0" } }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3265,11 +3413,6 @@ "map-cache": "^0.2.2" } }, - "frameguard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", - "integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==" - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -3314,6 +3457,12 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -3406,7 +3555,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3415,12 +3565,12 @@ "dev": true }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -3498,54 +3648,9 @@ } }, "helmet": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.22.0.tgz", - "integrity": "sha512-Xrqicn2nm1ZIUxP3YGuTBmbDL04neKsIT583Sjh0FkiwKDXYCMUqGqC88w3NUvVXtA75JyR2Jn6jw6ZEMOD+ZA==", - "requires": { - "depd": "2.0.0", - "dns-prefetch-control": "0.2.0", - "dont-sniff-mimetype": "1.1.0", - "expect-ct": "0.2.0", - "feature-policy": "0.3.0", - "frameguard": "3.1.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.10.0", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "ienoopen": "1.1.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, - "helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" - }, - "helmet-csp": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", - "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", - "requires": { - "bowser": "2.9.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" - } - }, - "hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.1.1.tgz", + "integrity": "sha512-Avg4XxSBrehD94mkRwEljnO+6RZx7AGfk8Wa6K1nxaU+hbXlFOhlOIMgPfFqOYQB/dBCsTpootTGuiOG+CHiQA==" }, "homedir-polyfill": { "version": "1.0.3", @@ -3561,26 +3666,6 @@ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, - "hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" - }, - "hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", - "requires": { - "depd": "2.0.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -3631,21 +3716,33 @@ "dev": true }, "husky": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", - "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", "dev": true, "requires": { "chalk": "^4.0.0", "ci-info": "^2.0.0", "compare-versions": "^3.6.0", - "cosmiconfig": "^6.0.0", + "cosmiconfig": "^7.0.0", "find-versions": "^3.2.0", "opencollective-postinstall": "^2.0.2", "pkg-dir": "^4.2.0", "please-upgrade-node": "^3.2.0", "slash": "^3.0.0", "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "iconv-lite": { @@ -3661,11 +3758,6 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, - "ienoopen": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", - "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==" - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -3772,7 +3864,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "optional": true, "requires": { "binary-extensions": "^2.0.0" } @@ -3820,9 +3911,9 @@ } }, "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", "dev": true, "optional": true }, @@ -3917,9 +4008,9 @@ } }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, "is-typedarray": { "version": "1.0.0", @@ -4060,45 +4151,65 @@ } }, "jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz", + "integrity": "sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw==", "dev": true, "requires": { - "@jest/core": "^26.0.1", + "@jest/core": "^26.4.2", "import-local": "^3.0.2", - "jest-cli": "^26.0.1" + "jest-cli": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-cli": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", - "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.2.tgz", + "integrity": "sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw==", "dev": true, "requires": { - "@jest/core": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/core": "^26.4.2", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", + "jest-config": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "prompts": "^2.0.1", "yargs": "^15.3.1" } @@ -4106,32 +4217,52 @@ } }, "jest-changed-files": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", - "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.3.0.tgz", + "integrity": "sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "execa": "^4.0.0", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -4140,9 +4271,9 @@ } }, "execa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", - "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -4157,20 +4288,14 @@ } }, "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -4213,56 +4338,76 @@ } }, "jest-config": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", - "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.4.2.tgz", + "integrity": "sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.0.1", - "@jest/types": "^26.0.1", - "babel-jest": "^26.0.1", + "@jest/test-sequencer": "^26.4.2", + "@jest/types": "^26.3.0", + "babel-jest": "^26.3.0", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.0.1", - "jest-environment-node": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.0.1", + "jest-environment-jsdom": "^26.3.0", + "jest-environment-node": "^26.3.0", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.4.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", + "jest-resolve": "^26.4.0", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "micromatch": "^4.0.2", - "pretty-format": "^26.0.1" + "pretty-format": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4280,18 +4425,6 @@ "diff-sequences": "^25.2.6", "jest-get-type": "^25.2.6", "pretty-format": "^25.5.0" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-docblock": { @@ -4304,43 +4437,63 @@ } }, "jest-each": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.0.1.tgz", - "integrity": "sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.4.2.tgz", + "integrity": "sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1" + "jest-get-type": "^26.3.0", + "jest-util": "^26.3.0", + "pretty-format": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4349,57 +4502,99 @@ } }, "jest-environment-jsdom": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", - "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz", + "integrity": "sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA==", "dev": true, "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1", + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0", "jsdom": "^16.2.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-environment-node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", - "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.3.0.tgz", + "integrity": "sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw==", "dev": true, "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -4410,93 +4605,126 @@ "dev": true }, "jest-haste-map": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.0.1.tgz", - "integrity": "sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.3.0.tgz", + "integrity": "sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "@types/graceful-fs": "^4.1.2", + "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", - "jest-serializer": "^26.0.0", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.3.0", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" + "walker": "^1.0.7" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", "dev": true, "requires": { - "isexe": "^2.0.0" + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } } } }, "jest-jasmine2": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", - "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz", + "integrity": "sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/environment": "^26.3.0", + "@jest/source-map": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.0.1", + "expect": "^26.4.2", "is-generator-fn": "^2.0.0", - "jest-each": "^26.0.1", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1", + "jest-each": "^26.4.2", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-runtime": "^26.4.2", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "pretty-format": "^26.4.2", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4505,40 +4733,60 @@ } }, "jest-leak-detector": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", - "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz", + "integrity": "sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA==", "dev": true, "requires": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4547,60 +4795,80 @@ } }, "jest-matcher-utils": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz", - "integrity": "sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz", + "integrity": "sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" + "jest-diff": "^26.4.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", + "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", "dev": true }, "jest-diff": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", - "integrity": "sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", + "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" + "diff-sequences": "^26.3.0", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" } }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4609,13 +4877,13 @@ } }, "jest-message-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", - "integrity": "sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.3.0.tgz", + "integrity": "sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "@types/stack-utils": "^1.0.1", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", @@ -4625,46 +4893,87 @@ }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-mock": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.0.1.tgz", - "integrity": "sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.3.0.tgz", + "integrity": "sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q==", "dev": true, "requires": { - "@jest/types": "^26.0.1" + "@jest/types": "^26.3.0", + "@types/node": "*" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, "jest-regex-util": { @@ -4674,224 +4983,326 @@ "dev": true }, "jest-resolve": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.0.1.tgz", - "integrity": "sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz", + "integrity": "sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "jest-util": "^26.0.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.3.0", "read-pkg-up": "^7.0.1", "resolve": "^1.17.0", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-resolve-dependencies": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", - "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz", + "integrity": "sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.0.1" + "jest-snapshot": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } } } }, "jest-runner": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", - "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.2.tgz", + "integrity": "sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g==", "dev": true, "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/console": "^26.3.0", + "@jest/environment": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", "chalk": "^4.0.0", + "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", + "jest-config": "^26.4.2", "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.0.1", - "jest-jasmine2": "^26.0.1", - "jest-leak-detector": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", + "jest-haste-map": "^26.3.0", + "jest-leak-detector": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-runtime": "^26.4.2", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", "source-map-support": "^0.5.6", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-runtime": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", - "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/globals": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.2.tgz", + "integrity": "sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ==", + "dev": true, + "requires": { + "@jest/console": "^26.3.0", + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/globals": "^26.4.2", + "@jest/source-map": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "@types/yargs": "^15.0.0", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", + "jest-config": "^26.4.2", + "jest-haste-map": "^26.3.0", + "jest-message-util": "^26.3.0", + "jest-mock": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", + "jest-resolve": "^26.4.0", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", "yargs": "^15.3.1" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-serializer": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.0.0.tgz", - "integrity": "sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.3.0.tgz", + "integrity": "sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow==", "dev": true, "requires": { + "@types/node": "*", "graceful-fs": "^4.2.4" } }, "jest-snapshot": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.0.1.tgz", - "integrity": "sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.2.tgz", + "integrity": "sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.0.1", + "expect": "^26.4.2", "graceful-fs": "^4.2.4", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "make-dir": "^3.0.0", + "jest-diff": "^26.4.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.3.0", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-resolve": "^26.4.0", "natural-compare": "^1.4.0", - "pretty-format": "^26.0.1", + "pretty-format": "^26.4.2", "semver": "^7.3.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", + "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", "dev": true }, "jest-diff": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", - "integrity": "sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", + "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" + "diff-sequences": "^26.3.0", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" } }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4906,77 +5317,118 @@ } }, "jest-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", - "integrity": "sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.3.0.tgz", + "integrity": "sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", + "@types/node": "*", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", - "make-dir": "^3.0.0" + "micromatch": "^4.0.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-validate": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", - "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.2.tgz", + "integrity": "sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "camelcase": "^6.0.0", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", + "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.0.1" + "pretty-format": "^26.4.2" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "camelcase": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", "dev": true }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", "dev": true, "requires": { - "@jest/types": "^26.0.1", + "@jest/types": "^26.3.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4985,39 +5437,61 @@ } }, "jest-watcher": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", - "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.3.0.tgz", + "integrity": "sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ==", "dev": true, "requires": { - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", + "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.0.1", + "jest-util": "^26.3.0", "string-length": "^4.0.1" }, "dependencies": { "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-worker": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", - "integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", + "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } @@ -5029,9 +5503,9 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -5045,9 +5519,9 @@ "dev": true }, "jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", "dev": true, "requires": { "abab": "^2.0.3", @@ -5070,7 +5544,7 @@ "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", + "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", @@ -5084,10 +5558,10 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { @@ -5166,12 +5640,9 @@ } }, "kuler": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", - "requires": { - "colornames": "^1.1.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, "lazy-cache": { "version": "2.0.2", @@ -5204,18 +5675,20 @@ "dev": true }, "lint-staged": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.4.tgz", - "integrity": "sha512-doTMGKXQAT34c3S3gwDrTnXmCZp/z1/92D8suPqqh755sKPT18ew1NoPNHxJdrvv1D4WrJ7CEnx79Ns3EdEFbg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz", + "integrity": "sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==", "dev": true, "requires": { - "chalk": "^4.0.0", - "commander": "^5.1.0", - "cosmiconfig": "^6.0.0", + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.0.0", + "cosmiconfig": "^7.0.0", "debug": "^4.1.1", "dedent": "^0.7.0", - "execa": "^4.0.1", - "listr2": "^2.0.2", + "enquirer": "^2.3.6", + "execa": "^4.0.3", + "listr2": "^2.6.0", "log-symbols": "^4.0.0", "micromatch": "^4.0.2", "normalize-path": "^3.0.0", @@ -5224,10 +5697,26 @@ "stringify-object": "^3.3.0" }, "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -5245,9 +5734,9 @@ } }, "execa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", - "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -5262,20 +5751,14 @@ } }, "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5324,25 +5807,31 @@ } }, "listr2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.0.3.tgz", - "integrity": "sha512-2dKxql0jPuiAyMLYUkzzvoDroenO+aiecNnNfjn+S4jK5P9uuHKN55u4eVX8Czb9JsgAjGx7yPQYMIEzHMmKSA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", + "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", "dev": true, "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "chalk": "^4.0.0", - "cli-cursor": "^3.1.0", + "chalk": "^4.1.0", "cli-truncate": "^2.1.0", - "elegant-spinner": "^2.0.0", - "enquirer": "^2.3.5", "figures": "^3.2.0", "indent-string": "^4.0.0", "log-update": "^4.0.0", - "nanoid": "^3.1.9", "p-map": "^4.0.0", - "pad": "^3.2.0", - "rxjs": "^6.5.5", + "rxjs": "^6.6.2", "through": "^2.3.8" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "load-json-file": { @@ -5390,7 +5879,8 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true }, "lodash.clonedeep": { "version": "4.5.0", @@ -5425,6 +5915,18 @@ "dev": true, "requires": { "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "log-update": { @@ -5459,13 +5961,13 @@ } }, "logform": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", - "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", "requires": { "colors": "^1.2.1", "fast-safe-stringify": "^2.0.4", - "fecha": "^2.3.3", + "fecha": "^4.2.0", "ms": "^2.1.1", "triple-beam": "^1.3.0" }, @@ -5746,11 +6248,11 @@ } }, "mongodb": { - "version": "3.5.7", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.7.tgz", - "integrity": "sha512-lMtleRT+vIgY/JhhTn1nyGwnSMmJkJELp+4ZbrjctrnBxuLbj6rmLuJFz8W2xUzUqWmqoyVxJLYuC58ZKpcTYQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", + "integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==", "requires": { - "bl": "^2.2.0", + "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", @@ -5759,9 +6261,9 @@ }, "dependencies": { "bson": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", - "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" } } }, @@ -5781,12 +6283,6 @@ "thenify-all": "^1.0.0" } }, - "nanoid": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.9.tgz", - "integrity": "sha512-fFiXlFo4Wkuei3i6w9SQI6yuzGRTGi8Z2zZKZpUxv/bQlBi4jtbVPBSNFZHQA9PNjofWqtIa8p+pnsc0kgZrhQ==", - "dev": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -5916,11 +6412,6 @@ "lower-case": "^1.1.1" } }, - "nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" - }, "node-dependency-injection": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/node-dependency-injection/-/node-dependency-injection-2.6.8.tgz", @@ -5966,17 +6457,17 @@ "dev": true }, "node-notifier": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.0.tgz", - "integrity": "sha512-y8ThJESxsHcak81PGpzWwQKxzk+5YtP3IxR8AYdpXQ1IB6FmcVzFdZXrkPin49F/DKUCfeeiziB8ptY9npzGuA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", "dev": true, "optional": true, "requires": { "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^7.2.1", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", "shellwords": "^0.1.1", - "uuid": "^7.0.3", + "uuid": "^8.3.0", "which": "^2.0.2" }, "dependencies": { @@ -5987,13 +6478,6 @@ "dev": true, "optional": true }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "optional": true - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6033,21 +6517,14 @@ } }, "nunjucks": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.1.tgz", - "integrity": "sha512-LYlVuC1ZNSalQQkLNNPvcgPt2M9FTY9bs39mTCuFXtqh7jWbYzhDlmz2M6onPiXEhdZo+b9anRhc+uBGuJZ2bQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz", + "integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==", "requires": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", "chokidar": "^3.3.0", - "commander": "^3.0.2" - }, - "dependencies": { - "commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" - } + "commander": "^5.1.0" } }, "nwsapi": { @@ -6146,23 +6623,26 @@ } }, "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" } }, "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "dev": true }, "optionator": { @@ -6229,15 +6709,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "pad": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pad/-/pad-3.2.0.tgz", - "integrity": "sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==", - "dev": true, - "requires": { - "wcwidth": "^1.0.1" - } - }, "pad-right": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", @@ -6257,14 +6728,14 @@ } }, "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", + "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, @@ -6396,9 +6867,9 @@ "dev": true }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, "pretty-format": { @@ -6549,7 +7020,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "optional": true, "requires": { "picomatch": "^2.2.1" } @@ -6575,11 +7045,6 @@ } } }, - "referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" - }, "regenerator-runtime": { "version": "0.13.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", @@ -6702,21 +7167,29 @@ } }, "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", "dev": true, "requires": { - "lodash": "^4.17.15" + "lodash": "^4.17.19" + }, + "dependencies": { + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + } } }, "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", "dev": true, "requires": { - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" }, @@ -6832,9 +7305,9 @@ "dev": true }, "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -7210,7 +7683,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -7407,9 +7881,9 @@ } }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -8064,18 +8538,19 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "ts-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.0.0.tgz", - "integrity": "sha512-eBpWH65mGgzobuw7UZy+uPP9lwu+tPp60o324ASRX4Ijg8UC5dl2zcge4kkmqr2Zeuk9FwIjvCTOPuNMEyGWWw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.3.0.tgz", + "integrity": "sha512-Jq2uKfx6bPd9+JDpZNMBJMdMQUC3sJ08acISj8NXlVgR2d5OqslEHOR2KHMgwymu8h50+lKIm0m0xj/ioYdW2Q==", "dev": true, "requires": { + "@types/jest": "26.x", "bs-logger": "0.x", "buffer-from": "1.x", "fast-json-stable-stringify": "2.x", + "jest-util": "26.x", "json5": "2.x", "lodash.memoize": "4.x", "make-error": "1.x", - "micromatch": "4.x", "mkdirp": "1.x", "semver": "7.x", "yargs-parser": "18.x" @@ -8096,9 +8571,9 @@ } }, "ts-node": { - "version": "8.10.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.1.tgz", - "integrity": "sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", "requires": { "arg": "^4.1.0", "diff": "^4.0.1", @@ -8108,44 +8583,30 @@ } }, "ts-node-dev": { - "version": "1.0.0-pre.44", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.44.tgz", - "integrity": "sha512-M5ZwvB6FU3jtc70i5lFth86/6Qj5XR5nMMBwVxZF4cZhpO7XcbWw6tbNiJo22Zx0KfjEj9py5DANhwLOkPPufw==", + "version": "1.0.0-pre.62", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.62.tgz", + "integrity": "sha512-hfsEuCqUZOVnZ86l7A3icxD1nFt1HEmLVbx4YOHCkrbSHPBNWcw+IczAPZo3zz7YiOm9vs0xG6OENNrkgm89tQ==", "dev": true, "requires": { + "chokidar": "^3.4.0", "dateformat": "~1.0.4-1.2.3", "dynamic-dedupe": "^0.3.0", - "filewatcher": "~3.0.0", - "minimist": "^1.1.3", - "mkdirp": "^0.5.1", - "node-notifier": "^5.4.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", - "tree-kill": "^1.2.1", - "ts-node": "*", + "tree-kill": "^1.2.2", + "ts-node": "^8.10.2", "tsconfig": "^7.0.0" }, "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, - "node-notifier": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -8154,6 +8615,19 @@ "requires": { "glob": "^7.1.3" } + }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } } } }, @@ -8184,9 +8658,9 @@ "dev": true }, "tslint": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz", - "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -8200,7 +8674,7 @@ "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.10.0", + "tslib": "^1.13.0", "tsutils": "^2.29.0" }, "dependencies": { @@ -8377,9 +8851,9 @@ } }, "typescript": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz", - "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==" }, "unc-path-regex": { "version": "0.1.2", @@ -8456,9 +8930,9 @@ "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -8493,9 +8967,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" }, "uuid-validate": { "version": "0.0.3", @@ -8503,9 +8977,9 @@ "integrity": "sha512-Fykw5U4eZESbq739BeLvEBFRuJODfrlmjx5eJux7W817LjRaq4b7/i4t2zxQmhcX+fAj4nMfRdTzO4tmwLKn0w==" }, "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz", + "integrity": "sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -8593,6 +9067,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "optional": true, "requires": { "defaults": "^1.0.3" } @@ -8619,22 +9094,14 @@ "dev": true }, "whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-PcVnO6NiewhkmzV0qn7A+UZ9Xx4maNTI+O+TShmfE4pqjoCMwUMjkvoNhNHPTvgR7QH9Xt3R13iHuWy2sToFxQ==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } + "webidl-conversions": "^6.1.0" } }, "which": { @@ -8658,19 +9125,19 @@ "dev": true }, "winston": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", - "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", "requires": { - "async": "^2.6.1", - "diagnostics": "^1.1.1", - "is-stream": "^1.1.0", - "logform": "^2.1.1", - "one-time": "0.0.4", - "readable-stream": "^3.1.1", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.3.0" + "winston-transport": "^4.4.0" }, "dependencies": { "readable-stream": { @@ -8686,11 +9153,11 @@ } }, "winston-transport": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", - "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", "requires": { - "readable-stream": "^2.3.6", + "readable-stream": "^2.3.7", "triple-beam": "^1.2.0" } }, @@ -8757,16 +9224,11 @@ } }, "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", "dev": true }, - "x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" - }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -8806,9 +9268,9 @@ "dev": true }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -8821,7 +9283,7 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "yargs-parser": "^18.1.2" }, "dependencies": { "is-fullwidth-code-point": { diff --git a/package.json b/package.json index fda7251..4815d67 100644 --- a/package.json +++ b/package.json @@ -29,15 +29,15 @@ "@types/compression": "^1.7.0", "@types/convict": "^5.2.1", "@types/errorhandler": "1.5.0", - "@types/express": "^4.17.6", - "@types/glob": "^7.1.1", - "@types/helmet": "0.0.47", - "@types/mongodb": "^3.5.18", - "@types/node": "^14.0.3", - "@types/uuid": "^8.0.0", + "@types/express": "^4.17.8", + "@types/glob": "^7.1.3", + "@types/helmet": "0.0.48", + "@types/mongodb": "^3.5.27", + "@types/node": "^14.10.2", + "@types/uuid": "^8.3.0", "@types/uuid-validate": "0.0.1", "body-parser": "^1.19.0", - "bson": "^4.0.4", + "bson": "^4.1.0", "compression": "^1.7.4", "connect-flash": "^0.1.1", "convict": "^6.0.0", @@ -48,38 +48,38 @@ "express": "^4.17.1", "express-validator": "^6.6.1", "glob": "^7.1.6", - "helmet": "^3.22.0", + "helmet": "^4.1.1", "http-status": "^1.4.2", "mandrill-api": "^1.0.45", - "mongodb": "^3.5.7", + "mongodb": "^3.6.2", "node-dependency-injection": "^2.6.8", - "nunjucks": "^3.2.1", - "ts-node": "^8.10.1", - "typescript": "^3.9.2", - "uuid": "^8.0.0", + "nunjucks": "^3.2.2", + "ts-node": "^9.0.0", + "typescript": "^4.0.2", + "uuid": "^8.3.0", "uuid-validate": "0.0.3", - "winston": "^3.2.1" + "winston": "^3.3.3" }, "devDependencies": { - "@types/aws-lambda": "^8.10.51", + "@types/aws-lambda": "^8.10.62", "@types/connect-flash": "0.0.35", "@types/cookie-parser": "^1.4.2", "@types/cookie-session": "^2.0.41", "@types/cucumber": "^6.0.1", - "@types/faker": "^4.1.12", - "@types/jest": "^25.2.3", + "@types/faker": "^5.1.0", + "@types/jest": "^26.0.14", "@types/nunjucks": "^3.1.3", - "@types/supertest": "^2.0.9", + "@types/supertest": "^2.0.10", "cucumber": "^6.0.5", - "faker": "^4.1.0", - "husky": "^4.2.5", - "jest": "^26.0.1", - "lint-staged": "10.2.4", - "prettier": "^2.0.5", + "faker": "^5.1.0", + "husky": "^4.3.0", + "jest": "^26.4.2", + "lint-staged": "10.4.0", + "prettier": "^2.1.2", "supertest": "^4.0.2", - "ts-jest": "^26.0.0", - "ts-node-dev": "^1.0.0-pre.44", - "tslint": "^6.1.2", + "ts-jest": "^26.3.0", + "ts-node-dev": "^1.0.0-pre.62", + "tslint": "^6.1.3", "tslint-config-prettier": "~1.18.0", "tslint-eslint-rules": "^5.4.0" }, From 972a6186cde60c5eb5dcccbca54b418d556138e3 Mon Sep 17 00:00:00 2001 From: Antonio Leon Date: Wed, 16 Sep 2020 23:37:44 +0200 Subject: [PATCH 36/48] Create Retention BC from the notifications module of the Mooc BC --- .../SendWelcomeUserEmail.ts | 17 +++++++++ .../SendWelcomeUserEmailOnUserRegistered.ts | 18 +++++++++ .../Retention/Campaign/domain/Email.ts | 37 +++++++++++++++++++ .../Retention/Campaign/domain/EmailAddress.ts | 3 ++ .../Retention/Campaign/domain/EmailId.ts | 3 ++ .../Retention/Campaign/domain/EmailSender.ts | 5 +++ .../domain/UserRegisteredDomainEvent.ts | 32 ++++++++++++++++ .../Campaign/domain/WelcomeUserEmail.ts | 13 +++++++ .../Campaign/domain/WelcomeUserEmailError.ts | 7 ++++ .../infrastructure/FakeEmailSender.ts | 8 ++++ src/apps/retention/app.ts | 3 ++ .../Campaign/application.yaml | 14 +++++++ .../dependency-injection/Campaign/config.ts | 23 ++++++++++++ .../Campaign/default.json | 1 + .../dependency-injection/Campaign/dev.json | 1 + .../Campaign/production.json | 1 + .../Campaign/staging.json | 1 + .../dependency-injection/Campaign/test.json | 3 ++ .../dependency-injection/application.yaml | 2 + .../dependency-injection/application_dev.yaml | 2 + .../application_production.yaml | 2 + .../application_staging.yaml | 2 + .../application_test.yaml | 2 + .../config/dependency-injection/index.ts | 9 +++++ src/apps/retention/subscribers.ts | 14 +++++++ 25 files changed, 223 insertions(+) create mode 100644 src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts create mode 100644 src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts create mode 100644 src/Contexts/Retention/Campaign/domain/Email.ts create mode 100644 src/Contexts/Retention/Campaign/domain/EmailAddress.ts create mode 100644 src/Contexts/Retention/Campaign/domain/EmailId.ts create mode 100644 src/Contexts/Retention/Campaign/domain/EmailSender.ts create mode 100644 src/Contexts/Retention/Campaign/domain/UserRegisteredDomainEvent.ts create mode 100644 src/Contexts/Retention/Campaign/domain/WelcomeUserEmail.ts create mode 100644 src/Contexts/Retention/Campaign/domain/WelcomeUserEmailError.ts create mode 100644 src/Contexts/Retention/Campaign/infrastructure/FakeEmailSender.ts create mode 100644 src/apps/retention/app.ts create mode 100644 src/apps/retention/config/dependency-injection/Campaign/application.yaml create mode 100644 src/apps/retention/config/dependency-injection/Campaign/config.ts create mode 100644 src/apps/retention/config/dependency-injection/Campaign/default.json create mode 100644 src/apps/retention/config/dependency-injection/Campaign/dev.json create mode 100644 src/apps/retention/config/dependency-injection/Campaign/production.json create mode 100644 src/apps/retention/config/dependency-injection/Campaign/staging.json create mode 100644 src/apps/retention/config/dependency-injection/Campaign/test.json create mode 100644 src/apps/retention/config/dependency-injection/application.yaml create mode 100644 src/apps/retention/config/dependency-injection/application_dev.yaml create mode 100644 src/apps/retention/config/dependency-injection/application_production.yaml create mode 100644 src/apps/retention/config/dependency-injection/application_staging.yaml create mode 100644 src/apps/retention/config/dependency-injection/application_test.yaml create mode 100644 src/apps/retention/config/dependency-injection/index.ts create mode 100644 src/apps/retention/subscribers.ts diff --git a/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts b/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts new file mode 100644 index 0000000..140e081 --- /dev/null +++ b/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmail.ts @@ -0,0 +1,17 @@ +import { EmailSender } from '../../domain/EmailSender'; +import { EmailAddress } from '../../domain/EmailAddress'; +import { WelcomeUserEmail } from '../../domain/WelcomeUserEmail'; +import { WelcomeUserEmailError } from '../../domain/WelcomeUserEmailError'; + +export default class SendWelcomeUserEmail { + constructor(private emailSender: EmailSender) {} + + async run(userEmailAddress: EmailAddress): Promise { + const welcomeUserEmail = new WelcomeUserEmail(userEmailAddress); + try { + await this.emailSender.send(welcomeUserEmail); + } catch (error) { + throw new WelcomeUserEmailError(userEmailAddress); + } + } +} diff --git a/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts b/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts new file mode 100644 index 0000000..785e76b --- /dev/null +++ b/src/Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered.ts @@ -0,0 +1,18 @@ +import { DomainEventSubscriber } from '../../../../Shared/domain/DomainEventSubscriber'; +import { UserRegisteredDomainEvent } from '../../domain/UserRegisteredDomainEvent'; +import { DomainEventClass } from '../../../../Shared/domain/DomainEvent'; +import SendWelcomeUserEmail from './SendWelcomeUserEmail'; +import { EmailAddress } from '../../domain/EmailAddress'; + +export default class SendWelcomeUserEmailOnUserRegistered implements DomainEventSubscriber { + constructor(private sendWelcomeUserEmail: SendWelcomeUserEmail) {} + + subscribedTo(): DomainEventClass[] { + return [UserRegisteredDomainEvent]; + } + + async on(domainEvent: UserRegisteredDomainEvent): Promise { + const userEmailAddress = new EmailAddress(domainEvent.userEmailAddress); + await this.sendWelcomeUserEmail.run(userEmailAddress); + } +} diff --git a/src/Contexts/Retention/Campaign/domain/Email.ts b/src/Contexts/Retention/Campaign/domain/Email.ts new file mode 100644 index 0000000..673dc3c --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/Email.ts @@ -0,0 +1,37 @@ +import { EmailAddress } from './EmailAddress'; +import { EmailId } from './EmailId'; +import { Uuid } from '../../../Shared/domain/value-object/Uuid'; + +type ConstructorParams = { + id?: EmailId; + from: EmailAddress; + to: EmailAddress; + subject: string; + body: string; +}; + +export class Email { + readonly id: EmailId; + readonly from: EmailAddress; + readonly to: EmailAddress; + readonly subject: string; + readonly body: string; + + constructor(params: ConstructorParams) { + this.id = params.id || new EmailId(Uuid.random().value); + this.from = params.from; + this.to = params.to; + this.subject = params.subject; + this.body = params.body; + } + + equals(otherEmail: Email): boolean { + return ( + this.id.value === otherEmail.id.value && + this.from.value === otherEmail.from.value && + this.to.value === otherEmail.to.value && + this.subject === otherEmail.subject && + this.body === otherEmail.body + ); + } +} diff --git a/src/Contexts/Retention/Campaign/domain/EmailAddress.ts b/src/Contexts/Retention/Campaign/domain/EmailAddress.ts new file mode 100644 index 0000000..f014743 --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/EmailAddress.ts @@ -0,0 +1,3 @@ +import { StringValueObject } from '../../../Shared/domain/value-object/StringValueObject'; + +export class EmailAddress extends StringValueObject {} diff --git a/src/Contexts/Retention/Campaign/domain/EmailId.ts b/src/Contexts/Retention/Campaign/domain/EmailId.ts new file mode 100644 index 0000000..3ba583c --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/EmailId.ts @@ -0,0 +1,3 @@ +import { Uuid } from '../../../Shared/domain/value-object/Uuid'; + +export class EmailId extends Uuid {} diff --git a/src/Contexts/Retention/Campaign/domain/EmailSender.ts b/src/Contexts/Retention/Campaign/domain/EmailSender.ts new file mode 100644 index 0000000..5a96e50 --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/EmailSender.ts @@ -0,0 +1,5 @@ +import { Email } from './Email'; + +export interface EmailSender { + send(email: Email): Promise; +} diff --git a/src/Contexts/Retention/Campaign/domain/UserRegisteredDomainEvent.ts b/src/Contexts/Retention/Campaign/domain/UserRegisteredDomainEvent.ts new file mode 100644 index 0000000..e45a2a0 --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/UserRegisteredDomainEvent.ts @@ -0,0 +1,32 @@ +import { DomainEvent } from '../../../Shared/domain/DomainEvent'; + +type UserRegisteredDomainEventBody = { userEmailAddress: string }; + +export class UserRegisteredDomainEvent extends DomainEvent { + static readonly EVENT_NAME = 'user.registered'; + readonly userEmailAddress: string; + + constructor(data: { id: string; userEmailAddress: string; eventId?: string; occurredOn?: Date }) { + const { id, eventId, occurredOn, userEmailAddress } = data; + super(UserRegisteredDomainEvent.EVENT_NAME, id, eventId, occurredOn); + this.userEmailAddress = userEmailAddress; + } + + toPrimitive(): Object { + return { userEmailAddress: this.userEmailAddress }; + } + + static fromPrimitives( + aggregateId: string, + body: UserRegisteredDomainEventBody, + eventId: string, + occurredOn: Date + ): DomainEvent { + return new UserRegisteredDomainEvent({ + id: aggregateId, + userEmailAddress: body.userEmailAddress, + eventId, + occurredOn + }); + } +} diff --git a/src/Contexts/Retention/Campaign/domain/WelcomeUserEmail.ts b/src/Contexts/Retention/Campaign/domain/WelcomeUserEmail.ts new file mode 100644 index 0000000..22a2fd9 --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/WelcomeUserEmail.ts @@ -0,0 +1,13 @@ +import { Email } from './Email'; +import { EmailAddress } from './EmailAddress'; + +export class WelcomeUserEmail extends Email { + constructor(to: EmailAddress) { + super({ + from: new EmailAddress('welcome@foo.com'), + to, + subject: 'Welcome', + body: 'Welcome to our platform' + }); + } +} diff --git a/src/Contexts/Retention/Campaign/domain/WelcomeUserEmailError.ts b/src/Contexts/Retention/Campaign/domain/WelcomeUserEmailError.ts new file mode 100644 index 0000000..ef6eddc --- /dev/null +++ b/src/Contexts/Retention/Campaign/domain/WelcomeUserEmailError.ts @@ -0,0 +1,7 @@ +import { EmailAddress } from './EmailAddress'; + +export class WelcomeUserEmailError extends Error { + constructor(userEmailAddress: EmailAddress) { + super(`Error sending WelcomeUser email to ${userEmailAddress.value}`); + } +} diff --git a/src/Contexts/Retention/Campaign/infrastructure/FakeEmailSender.ts b/src/Contexts/Retention/Campaign/infrastructure/FakeEmailSender.ts new file mode 100644 index 0000000..e148b9f --- /dev/null +++ b/src/Contexts/Retention/Campaign/infrastructure/FakeEmailSender.ts @@ -0,0 +1,8 @@ +import { EmailSender } from '../domain/EmailSender'; +import { Email } from '../domain/Email'; + +export default class FakeEmailSender implements EmailSender { + async send(email: Email): Promise { + // do nothing + } +} diff --git a/src/apps/retention/app.ts b/src/apps/retention/app.ts new file mode 100644 index 0000000..c02a5a7 --- /dev/null +++ b/src/apps/retention/app.ts @@ -0,0 +1,3 @@ +import { registerSubscribers } from './subscribers'; + +registerSubscribers(); diff --git a/src/apps/retention/config/dependency-injection/Campaign/application.yaml b/src/apps/retention/config/dependency-injection/Campaign/application.yaml new file mode 100644 index 0000000..c6d6832 --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/application.yaml @@ -0,0 +1,14 @@ +services: + Retention.campaign.EmailSender: + class: ../../../../../Contexts/Retention/Campaign/infrastructure/FakeEmailSender + arguments: [] + + Retention.campaign.SendWelcomeUserEmail: + class: ../../../../../Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmail + arguments: ["@Retention.campaign.EmailSender"] + + Retention.campaign.SendWelcomeUserEmailOnUserRegistered: + class: ../../../../../Contexts/Retention/Campaign/application/SendWelcomeUserEmail/SendWelcomeUserEmailOnUserRegistered + arguments: ["@Retention.campaign.SendWelcomeUserEmail"] + tags: + - { name: 'domainEventSubscriber' } \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/Campaign/config.ts b/src/apps/retention/config/dependency-injection/Campaign/config.ts new file mode 100644 index 0000000..5d5092c --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/config.ts @@ -0,0 +1,23 @@ +import convict from 'convict'; + +// We Keep same Mongo connection URL but create a new config so it can be migrated easily +const convictConfig = convict({ + env: { + doc: 'The application environment.', + format: ['production', 'development', 'staging', 'test'], + default: 'default', + env: 'NODE_ENV' + }, + mongo: { + url: { + doc: 'The Mongo connection URL', + format: String, + env: 'MONGO_URL', + default: 'mongodb://localhost:27017/mooc-backend-dev' + } + } +}); + +convictConfig.loadFile([__dirname + '/default.json', __dirname + '/' + convictConfig.get('env') + '.json']); + +export default convictConfig; diff --git a/src/apps/retention/config/dependency-injection/Campaign/default.json b/src/apps/retention/config/dependency-injection/Campaign/default.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/default.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/Campaign/dev.json b/src/apps/retention/config/dependency-injection/Campaign/dev.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/dev.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/Campaign/production.json b/src/apps/retention/config/dependency-injection/Campaign/production.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/production.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/Campaign/staging.json b/src/apps/retention/config/dependency-injection/Campaign/staging.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/staging.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/Campaign/test.json b/src/apps/retention/config/dependency-injection/Campaign/test.json new file mode 100644 index 0000000..d5e154f --- /dev/null +++ b/src/apps/retention/config/dependency-injection/Campaign/test.json @@ -0,0 +1,3 @@ +{ + "mongo": { "url": "mongodb://localhost:27017/mooc-backend-test" } + } diff --git a/src/apps/retention/config/dependency-injection/application.yaml b/src/apps/retention/config/dependency-injection/application.yaml new file mode 100644 index 0000000..81d141c --- /dev/null +++ b/src/apps/retention/config/dependency-injection/application.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./Campaign/application.yaml } \ No newline at end of file diff --git a/src/apps/retention/config/dependency-injection/application_dev.yaml b/src/apps/retention/config/dependency-injection/application_dev.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/retention/config/dependency-injection/application_dev.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/retention/config/dependency-injection/application_production.yaml b/src/apps/retention/config/dependency-injection/application_production.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/retention/config/dependency-injection/application_production.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/retention/config/dependency-injection/application_staging.yaml b/src/apps/retention/config/dependency-injection/application_staging.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/retention/config/dependency-injection/application_staging.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/retention/config/dependency-injection/application_test.yaml b/src/apps/retention/config/dependency-injection/application_test.yaml new file mode 100644 index 0000000..287933e --- /dev/null +++ b/src/apps/retention/config/dependency-injection/application_test.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: ./application.yaml } diff --git a/src/apps/retention/config/dependency-injection/index.ts b/src/apps/retention/config/dependency-injection/index.ts new file mode 100644 index 0000000..27d2a35 --- /dev/null +++ b/src/apps/retention/config/dependency-injection/index.ts @@ -0,0 +1,9 @@ +import { ContainerBuilder, YamlFileLoader } from 'node-dependency-injection'; + +const container = new ContainerBuilder(); +const loader = new YamlFileLoader(container); +const env = process.env.NODE_ENV || 'dev'; + +loader.load(`${__dirname}/application_${env}.yaml`); + +export default container; diff --git a/src/apps/retention/subscribers.ts b/src/apps/retention/subscribers.ts new file mode 100644 index 0000000..6a7667c --- /dev/null +++ b/src/apps/retention/subscribers.ts @@ -0,0 +1,14 @@ +import container from './config/dependency-injection'; +import { InMemoryAsyncEventBus } from '../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus'; +import { Definition } from 'node-dependency-injection'; +import { DomainEventSubscriber } from '../../Contexts/Shared/domain/DomainEventSubscriber'; +import { DomainEvent } from '../../Contexts/Shared/domain/DomainEvent'; + +export function registerSubscribers() { + const eventBus = container.get('Shared.EventBus') as InMemoryAsyncEventBus; + const subscriberDefinitions = container.findTaggedServiceIds('domainEventSubscriber') as Map; + const subscribers: Array> = []; + + subscriberDefinitions.forEach((value: any, key: any) => subscribers.push(container.get(key))); + eventBus.addSubscribers(subscribers); +} From 5ef7312f753ebd9ccd755ca6fea2546e2b8cd7c6 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 18 Sep 2020 17:16:14 +0200 Subject: [PATCH 37/48] Course e2e test --- cypress.json | 3 + cypress/fixtures/example.json | 5 + .../integration/backoffice/courses.spec.ts | 14 + cypress/plugins/index.js | 21 + cypress/support/commands.js | 25 + cypress/support/index.js | 20 + package-lock.json | 1288 +++++++++++++++-- package.json | 5 +- src/apps/backoffice/frontend/seed.ts | 2 +- 9 files changed, 1287 insertions(+), 96 deletions(-) create mode 100644 cypress.json create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/integration/backoffice/courses.spec.ts create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/index.js diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000..0f0f794 --- /dev/null +++ b/cypress.json @@ -0,0 +1,3 @@ +{ + "video": false +} diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000..da18d93 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/integration/backoffice/courses.spec.ts b/cypress/integration/backoffice/courses.spec.ts new file mode 100644 index 0000000..887fc28 --- /dev/null +++ b/cypress/integration/backoffice/courses.spec.ts @@ -0,0 +1,14 @@ +describe('Courses', () => { + + it('can create a course', () => { + const courseName = 'New course'; + cy.visit('http://localhost:8032/courses'); + + cy.get('input[name="name"]').type(courseName); + cy.get('input[name="duration"]').type('8 days'); + cy.get('form').submit(); + + cy.get('div[role="alert"]').contains(`Felicidades, el curso ${courseName} ha sido creado!`); + // cy.contains('Actualmente CodelyTV Pro cuenta con 10 curso'); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000..aa9918d --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,21 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000..ca4d256 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000..d68db96 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/package-lock.json b/package-lock.json index 8b6ac16..c6af7e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -479,6 +479,180 @@ "minimist": "^1.2.0" } }, + "@cypress/listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@cypress/request": { + "version": "2.88.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz", + "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -988,6 +1162,15 @@ "chalk": "^3.0.0" } }, + "@samverschueren/stream-to-observable": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", + "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, "@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", @@ -1308,6 +1491,18 @@ "@types/mime": "*" } }, + "@types/sinonjs__fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", + "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", + "dev": true + }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1485,6 +1680,12 @@ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -1500,6 +1701,12 @@ "picomatch": "^2.0.4" } }, + "arch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==", + "dev": true + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1611,6 +1818,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1839,6 +2052,12 @@ "safe-buffer": "^5.1.1" } }, + "blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1920,6 +2139,12 @@ "ieee754": "^1.1.4" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1961,6 +2186,12 @@ } } }, + "cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2021,6 +2252,12 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true + }, "chokidar": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", @@ -2204,6 +2441,12 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -2294,6 +2537,12 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, "compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -2340,6 +2589,18 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "connect-flash": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", @@ -2563,39 +2824,237 @@ "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", "dev": true }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + } + } + }, + "cucumber-expressions": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-8.3.0.tgz", + "integrity": "sha512-cP2ya0EiorwXBC7Ll7Cj7NELYbasNv9Ty42L4u7sso9KruWemWG1ZiTq4PMqir3SNDSrbykoqI5wZgMbLEDjLQ==", + "dev": true, + "requires": { + "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.4.0", + "xregexp": "^4.2.4" + } + }, + "cucumber-tag-expressions": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.3.tgz", + "integrity": "sha512-+x5j1IfZrBtbvYHuoUX0rl4nUGxaey6Do9sM0CABmZfDCcWXuuRm1fQeCaklIYQgOFHQ6xOHvDSdkMHHpni6tQ==", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cypress": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-5.2.0.tgz", + "integrity": "sha512-9S2spcrpIXrQ+CQIKHsjRoLQyRc2ehB06clJXPXXp1zyOL/uZMM3Qc20ipNki4CcNwY0nBTQZffPbRpODeGYQg==", + "dev": true, + "requires": { + "@cypress/listr-verbose-renderer": "^0.4.1", + "@cypress/request": "^2.88.5", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "^6.0.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.1.2", + "blob-util": "2.0.2", + "bluebird": "^3.7.2", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-table3": "~0.6.0", + "commander": "^4.1.1", + "common-tags": "^1.8.0", + "debug": "^4.1.1", + "eventemitter2": "^6.4.2", + "execa": "^4.0.2", + "executable": "^4.1.1", + "extract-zip": "^1.7.0", + "fs-extra": "^9.0.1", + "getos": "^3.2.1", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.2", + "lazy-ass": "^1.6.0", + "listr": "^0.14.3", + "lodash": "^4.17.19", + "log-symbols": "^4.0.0", + "minimist": "^1.2.5", + "moment": "^2.27.0", + "ospath": "^1.2.2", + "pretty-bytes": "^5.3.0", + "ramda": "~0.26.1", + "request-progress": "^3.0.0", + "supports-color": "^7.1.0", + "tmp": "~0.2.1", + "untildify": "^4.0.0", + "url": "^0.11.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, - "cucumber-expressions": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-8.3.0.tgz", - "integrity": "sha512-cP2ya0EiorwXBC7Ll7Cj7NELYbasNv9Ty42L4u7sso9KruWemWG1ZiTq4PMqir3SNDSrbykoqI5wZgMbLEDjLQ==", - "dev": true, - "requires": { - "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.4.0", - "xregexp": "^4.2.4" - } - }, - "cucumber-tag-expressions": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.3.tgz", - "integrity": "sha512-+x5j1IfZrBtbvYHuoUX0rl4nUGxaey6Do9sM0CABmZfDCcWXuuRm1fQeCaklIYQgOFHQ6xOHvDSdkMHHpni6tQ==", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -2626,6 +3085,12 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, "dateformat": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", @@ -2817,6 +3282,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, "emittery": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", @@ -2970,6 +3441,12 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "eventemitter2": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", + "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==", + "dev": true + }, "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", @@ -2999,12 +3476,27 @@ } } }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + } + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -3228,6 +3720,18 @@ } } }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dev": true, + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + } + }, "extsprintf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", @@ -3272,6 +3776,15 @@ "bser": "2.1.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "fecha": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", @@ -3428,6 +3941,18 @@ "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3484,6 +4009,15 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "requires": { + "async": "^3.2.0" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -3520,6 +4054,15 @@ "is-glob": "^2.0.0" } }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, "global-modules": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", @@ -3574,6 +4117,23 @@ "har-schema": "^2.0.0" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3959,6 +4519,16 @@ "is-extglob": "^1.0.0" } }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3970,6 +4540,21 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + } + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -3993,6 +4578,12 @@ "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", "dev": true }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -5591,6 +6182,16 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5644,6 +6245,12 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", @@ -5698,110 +6305,479 @@ }, "dependencies": { "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, - "commander": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", - "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "ms": "^2.1.1" + "number-is-nan": "^1.0.0" } }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", "dev": true, "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "chalk": "^1.0.0" } }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", "dev": true, "requires": { - "pump": "^3.0.0" + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "path-key": "^3.0.0" + "mimic-fn": "^1.0.0" } }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, - "shebang-command": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", "dev": true, "requires": { - "shebang-regex": "^3.0.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + } + } + }, + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" } }, - "shebang-regex": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "isexe": "^2.0.0" + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } @@ -5893,6 +6869,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -6247,6 +7229,12 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.28.0.tgz", + "integrity": "sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw==", + "dev": true + }, "mongodb": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", @@ -6516,6 +7504,12 @@ "path-key": "^2.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "nunjucks": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz", @@ -6664,6 +7658,12 @@ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, + "ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "dev": true + }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -6795,6 +7795,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -6872,6 +7878,12 @@ "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, + "pretty-bytes": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==", + "dev": true + }, "pretty-format": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", @@ -6941,6 +7953,18 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "ramda": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", + "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", + "dev": true + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7166,6 +8190,15 @@ } } }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, "request-promise-core": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", @@ -8237,6 +9270,12 @@ "supports-color": "^7.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -8293,6 +9332,12 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -8318,6 +9363,15 @@ "upper-case": "^1.0.3" } }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -8841,6 +9895,12 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -8872,6 +9932,12 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -8923,6 +9989,12 @@ } } }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -8944,6 +10016,24 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -9323,6 +10413,16 @@ "decamelize": "^1.2.0" } }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 4815d67..e8875ce 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "build": "npm run build:clean && npm run build:tsc && npm run build:di", "build:tsc": "tsc -p tsconfig.prod.json", "build:di": "copy 'src/**/*.{json,yaml,html,png}' dist/src", - "build:clean": "rm -r dist; exit 0" + "build:clean": "rm -r dist; exit 0", + "cypress:open": "NODE_ENV=test cypress open", + "cypress:run": "NODE_ENV=test cypress run" }, "dependencies": { "@types/bson": "^4.0.2", @@ -71,6 +73,7 @@ "@types/nunjucks": "^3.1.3", "@types/supertest": "^2.0.10", "cucumber": "^6.0.5", + "cypress": "^5.2.0", "faker": "^5.1.0", "husky": "^4.3.0", "jest": "^26.4.2", diff --git a/src/apps/backoffice/frontend/seed.ts b/src/apps/backoffice/frontend/seed.ts index 03f871f..09df2a1 100644 --- a/src/apps/backoffice/frontend/seed.ts +++ b/src/apps/backoffice/frontend/seed.ts @@ -10,7 +10,7 @@ export async function seed() { const alreadyExists = await repository.search(); const isTestEnvironment = process.env.NODE_ENV === 'test'; - if (!alreadyExists && !isTestEnvironment) { + if (!alreadyExists) { logger.info('[Seed] Initializing CourseCounter'); const courseCounter = CoursesCounter.initialize(CoursesCounterId.random()); await repository.save(courseCounter); From 9ee5023f30785fc9b24adb2a969a19ed5ef9e0f7 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 18 Sep 2020 17:39:37 +0200 Subject: [PATCH 38/48] Add default baseUrl --- cypress.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cypress.json b/cypress.json index 0f0f794..0163845 100644 --- a/cypress.json +++ b/cypress.json @@ -1,3 +1,4 @@ { - "video": false + "baseUrl": "http://localhost:8032", + "video": false } From 7be2cbbe044a67b7fdb96deb48ab2b4fcfdae0f5 Mon Sep 17 00:00:00 2001 From: David Matas Date: Sat, 19 Sep 2020 15:12:58 +0200 Subject: [PATCH 39/48] Add cypress plugin to clean/seed database --- .../integration/backoffice/courses.spec.ts | 26 ++++++++++++++----- cypress/plugins/index.js | 21 --------------- cypress/plugins/index.ts | 15 +++++++++++ cypress/tsconfig.json | 11 ++++++++ .../controllers/CoursesPostController.ts | 2 +- 5 files changed, 46 insertions(+), 29 deletions(-) delete mode 100644 cypress/plugins/index.js create mode 100644 cypress/plugins/index.ts create mode 100644 cypress/tsconfig.json diff --git a/cypress/integration/backoffice/courses.spec.ts b/cypress/integration/backoffice/courses.spec.ts index 887fc28..c7fc9b1 100644 --- a/cypress/integration/backoffice/courses.spec.ts +++ b/cypress/integration/backoffice/courses.spec.ts @@ -1,14 +1,26 @@ +import faker from 'faker'; + describe('Courses', () => { - it('can create a course', () => { - const courseName = 'New course'; + beforeEach(() => { + cy.task('reset:db'); + }); + + it('can create courses', () => { cy.visit('http://localhost:8032/courses'); - cy.get('input[name="name"]').type(courseName); - cy.get('input[name="duration"]').type('8 days'); - cy.get('form').submit(); + cy.contains('Actualmente CodelyTV Pro cuenta con 0 cursos.'); + + let i = 0; + while (i <= 5) { + i++; + const courseName = faker.lorem.sentence(2); + cy.get('input[name="name"]').type(courseName); + cy.get('input[name="duration"]').type('8 days'); + cy.get('form').submit(); - cy.get('div[role="alert"]').contains(`Felicidades, el curso ${courseName} ha sido creado!`); - // cy.contains('Actualmente CodelyTV Pro cuenta con 10 curso'); + cy.get('div[role="alert"]').contains(`Felicidades, el curso ${courseName} ha sido creado!`); + cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); + } }); }); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index aa9918d..0000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts new file mode 100644 index 0000000..a40e856 --- /dev/null +++ b/cypress/plugins/index.ts @@ -0,0 +1,15 @@ +import container from '../../src/apps/mooc_backend/config/dependency-injection'; +import { EnvironmentArranger } from '../../tests/Contexts/Shared/infrastructure/arranger/EnvironmentArranger'; +import {seed} from '../../src/apps/backoffice/frontend/seed'; + +const environmentArranger: Promise = container.get('Mooc.EnvironmentArranger'); + +export default (on: Cypress.PluginEvents, config: Cypress.PluginConfig) => { + on('task', { + async 'reset:db'() { + await (await environmentArranger).arrange(); + await seed(); + return null; + } + }); +}; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000..f507fd4 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "lib": ["es2015", "dom"], + "types": ["cypress"] + }, + "include": [ + "**/*.ts" + ] +} + diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts index f7cd3ff..efa1c49 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -12,7 +12,7 @@ export class CoursesPostController extends WebController { static validator(): ValidationChain[] { return [ body('id').isUUID(), - body('name').isLength({ min: 1, max: 255 }), + body('name').isLength({ min: 1, max: 30 }), body('duration').isLength({ min: 4, max: 100 }) ]; } From e98713d37574bd23f8dcb9b4075fc27895bd4ea1 Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Mon, 21 Sep 2020 09:30:38 +0200 Subject: [PATCH 40/48] Add script for launching the server before the cypress run --- .gitignore | 1 + .../integration/backoffice/courses.spec.ts | 2 +- cypress/plugins/index.ts | 2 +- cypress/start.ts | 37 +++++++++++++++++++ package.json | 2 +- 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 cypress/start.ts diff --git a/.gitignore b/.gitignore index aab2965..6bb7f1c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist/ logs/ src/Contexts/Mooc/Courses/infrastructure/persistence/courses.* data +test-results.xml diff --git a/cypress/integration/backoffice/courses.spec.ts b/cypress/integration/backoffice/courses.spec.ts index c7fc9b1..4008e8e 100644 --- a/cypress/integration/backoffice/courses.spec.ts +++ b/cypress/integration/backoffice/courses.spec.ts @@ -20,7 +20,7 @@ describe('Courses', () => { cy.get('form').submit(); cy.get('div[role="alert"]').contains(`Felicidades, el curso ${courseName} ha sido creado!`); - cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); + // cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); } }); }); diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts index a40e856..7274878 100644 --- a/cypress/plugins/index.ts +++ b/cypress/plugins/index.ts @@ -1,6 +1,6 @@ import container from '../../src/apps/mooc_backend/config/dependency-injection'; import { EnvironmentArranger } from '../../tests/Contexts/Shared/infrastructure/arranger/EnvironmentArranger'; -import {seed} from '../../src/apps/backoffice/frontend/seed'; +import { seed } from '../../src/apps/backoffice/frontend/seed'; const environmentArranger: Promise = container.get('Mooc.EnvironmentArranger'); diff --git a/cypress/start.ts b/cypress/start.ts new file mode 100644 index 0000000..7cc7227 --- /dev/null +++ b/cypress/start.ts @@ -0,0 +1,37 @@ +import app from '../src/apps/backoffice/frontend/app'; +import cypress from 'cypress'; +import { Server } from 'http'; + +async function run() { + const server = await startServer(); + await runCypress(); + server.close(() => { + process.exit(0); + }); +} + +async function startServer(): Promise { + let server: Server; + + return new Promise((resolve, reject) => { + server = app.listen(app.get('port'), async () => { + console.log(` Backoffice frontend is running at http://localhost:${app.get('port')} in ${app.get('env')} mode`); + console.log(' Press CTRL-C to stop\n'); + resolve(server); + }); + }); +} + +async function runCypress() { + return cypress.run({ + reporter: 'junit', + browser: 'chrome', + headless: true, + config: { + baseUrl: `http://localhost:${app.get('port')}`, + video: false + } + }); +} + +run(); diff --git a/package.json b/package.json index e8875ce..02ec8ed 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build:di": "copy 'src/**/*.{json,yaml,html,png}' dist/src", "build:clean": "rm -r dist; exit 0", "cypress:open": "NODE_ENV=test cypress open", - "cypress:run": "NODE_ENV=test cypress run" + "cypress:run": "NODE_ENV=test ts-node cypress/start" }, "dependencies": { "@types/bson": "^4.0.2", From 11d0d5637fbd1ebea2982f51eb7059f65a691f38 Mon Sep 17 00:00:00 2001 From: Fernando Vilas Maciel Date: Fri, 25 Sep 2020 16:05:20 +0200 Subject: [PATCH 41/48] Fix asynchronous test at the cypress backoffice tests Co-authored-by: David Matas --- cypress.json | 5 +++-- cypress/integration/backoffice/courses.spec.ts | 2 +- cypress/start.ts | 3 ++- src/Contexts/Mooc/Courses/application/CourseCreator.ts | 2 +- .../application/Increment/CoursesCounterIncrementer.ts | 2 +- .../config/dependency-injection/Shared/application.yaml | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cypress.json b/cypress.json index 0163845..68d2c3f 100644 --- a/cypress.json +++ b/cypress.json @@ -1,4 +1,5 @@ { - "baseUrl": "http://localhost:8032", - "video": false + "video": false, + "screenshotOnRunFailure": false + } diff --git a/cypress/integration/backoffice/courses.spec.ts b/cypress/integration/backoffice/courses.spec.ts index 4008e8e..c7fc9b1 100644 --- a/cypress/integration/backoffice/courses.spec.ts +++ b/cypress/integration/backoffice/courses.spec.ts @@ -20,7 +20,7 @@ describe('Courses', () => { cy.get('form').submit(); cy.get('div[role="alert"]').contains(`Felicidades, el curso ${courseName} ha sido creado!`); - // cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); + cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); } }); }); diff --git a/cypress/start.ts b/cypress/start.ts index 7cc7227..479433e 100644 --- a/cypress/start.ts +++ b/cypress/start.ts @@ -1,6 +1,7 @@ import app from '../src/apps/backoffice/frontend/app'; import cypress from 'cypress'; import { Server } from 'http'; +import cypressConfig from '../cypress.json'; async function run() { const server = await startServer(); @@ -28,8 +29,8 @@ async function runCypress() { browser: 'chrome', headless: true, config: { + ...cypressConfig, baseUrl: `http://localhost:${app.get('port')}`, - video: false } }); } diff --git a/src/Contexts/Mooc/Courses/application/CourseCreator.ts b/src/Contexts/Mooc/Courses/application/CourseCreator.ts index 0880cdf..db2b81b 100644 --- a/src/Contexts/Mooc/Courses/application/CourseCreator.ts +++ b/src/Contexts/Mooc/Courses/application/CourseCreator.ts @@ -29,6 +29,6 @@ export class CourseCreator { ); await this.repository.save(course); - this.eventBus.publish(course.pullDomainEvents()); + await this.eventBus.publish(course.pullDomainEvents()); } } diff --git a/src/Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer.ts b/src/Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer.ts index 957b051..8b4ce66 100644 --- a/src/Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer.ts +++ b/src/Contexts/Mooc/CoursesCounter/application/Increment/CoursesCounterIncrementer.ts @@ -14,7 +14,7 @@ export class CoursesCounterIncrementer { counter.increment(courseId); await this.repository.save(counter); - this.bus.publish(counter.pullDomainEvents()); + await this.bus.publish(counter.pullDomainEvents()); } } diff --git a/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml b/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml index 813e2c5..043d1ed 100644 --- a/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml +++ b/src/apps/backoffice/frontend/config/dependency-injection/Shared/application.yaml @@ -10,7 +10,7 @@ services: arguments: ['mooc'] Shared.EventBus: - class: ../../../../../../Contexts/Shared/infrastructure/EventBus/InMemoryAsyncEventBus + class: ../../../../../../Contexts/Shared/infrastructure/EventBus/InMemorySyncEventBus arguments: [] Shared.QueryBus: From 202d49c909a5244d32bc03d88d6708f233dfe9a2 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 2 Oct 2020 20:13:14 +0200 Subject: [PATCH 42/48] Cypress root modification --- cypress.json | 7 +++- cypress/start.ts | 38 ------------------- package.json | 6 +-- .../features/courses/create_course.cypress.ts | 1 - .../utils/cypress}/fixtures/example.json | 0 tests/utils/cypress/open.ts | 16 ++++++++ .../utils/cypress}/plugins/index.ts | 6 +-- tests/utils/cypress/run.ts | 26 +++++++++++++ .../utils/cypress/startBackofficeFrontend.ts | 14 +++++++ .../utils/cypress}/tsconfig.json | 6 +-- 10 files changed, 69 insertions(+), 51 deletions(-) delete mode 100644 cypress/start.ts rename cypress/integration/backoffice/courses.spec.ts => tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts (99%) rename {cypress => tests/utils/cypress}/fixtures/example.json (100%) create mode 100644 tests/utils/cypress/open.ts rename {cypress => tests/utils/cypress}/plugins/index.ts (55%) create mode 100644 tests/utils/cypress/run.ts create mode 100644 tests/utils/cypress/startBackofficeFrontend.ts rename {cypress => tests/utils/cypress}/tsconfig.json (57%) diff --git a/cypress.json b/cypress.json index 68d2c3f..3cf53f8 100644 --- a/cypress.json +++ b/cypress.json @@ -1,5 +1,8 @@ { "video": false, - "screenshotOnRunFailure": false - + "screenshotOnRunFailure": false, + "testFiles": "**/*.cypress.*", + "integrationFolder": "tests/apps", + "fixturesFolder": "tests/utils/cypress/fixtures", + "pluginsFile": "tests/utils/cypress/plugins/index.ts" } diff --git a/cypress/start.ts b/cypress/start.ts deleted file mode 100644 index 479433e..0000000 --- a/cypress/start.ts +++ /dev/null @@ -1,38 +0,0 @@ -import app from '../src/apps/backoffice/frontend/app'; -import cypress from 'cypress'; -import { Server } from 'http'; -import cypressConfig from '../cypress.json'; - -async function run() { - const server = await startServer(); - await runCypress(); - server.close(() => { - process.exit(0); - }); -} - -async function startServer(): Promise { - let server: Server; - - return new Promise((resolve, reject) => { - server = app.listen(app.get('port'), async () => { - console.log(` Backoffice frontend is running at http://localhost:${app.get('port')} in ${app.get('env')} mode`); - console.log(' Press CTRL-C to stop\n'); - resolve(server); - }); - }); -} - -async function runCypress() { - return cypress.run({ - reporter: 'junit', - browser: 'chrome', - headless: true, - config: { - ...cypressConfig, - baseUrl: `http://localhost:${app.get('port')}`, - } - }); -} - -run(); diff --git a/package.json b/package.json index 02ec8ed..bfcd977 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "scripts": { "dev": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules --inspect=0.0.0.0:9267 ./src/apps/mooc_backend/server.ts", "dev:backoffice:frontend": "NODE_ENV=dev ts-node-dev --ignore-watch node_modules ./src/apps/backoffice/frontend/server.ts", - "test": "npm run test:unit && npm run test:features", + "test": "npm run test:unit && npm run test:features && npm run cypress:run", "test:unit": "NODE_ENV=test jest", "test:features": "NODE_ENV=test cucumber-js -p default", "lint": "tslint src/**/*.ts{,x}", @@ -23,8 +23,8 @@ "build:tsc": "tsc -p tsconfig.prod.json", "build:di": "copy 'src/**/*.{json,yaml,html,png}' dist/src", "build:clean": "rm -r dist; exit 0", - "cypress:open": "NODE_ENV=test cypress open", - "cypress:run": "NODE_ENV=test ts-node cypress/start" + "cypress:open": "NODE_ENV=test ts-node tests/utils/cypress/open", + "cypress:run": "NODE_ENV=test ts-node tests/utils/cypress/run" }, "dependencies": { "@types/bson": "^4.0.2", diff --git a/cypress/integration/backoffice/courses.spec.ts b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts similarity index 99% rename from cypress/integration/backoffice/courses.spec.ts rename to tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts index c7fc9b1..0c1b8b1 100644 --- a/cypress/integration/backoffice/courses.spec.ts +++ b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts @@ -1,7 +1,6 @@ import faker from 'faker'; describe('Courses', () => { - beforeEach(() => { cy.task('reset:db'); }); diff --git a/cypress/fixtures/example.json b/tests/utils/cypress/fixtures/example.json similarity index 100% rename from cypress/fixtures/example.json rename to tests/utils/cypress/fixtures/example.json diff --git a/tests/utils/cypress/open.ts b/tests/utils/cypress/open.ts new file mode 100644 index 0000000..5a17c51 --- /dev/null +++ b/tests/utils/cypress/open.ts @@ -0,0 +1,16 @@ +import cypress from 'cypress'; +import { startBackofficeFrontend } from './startBackofficeFrontend'; + +async function open() { + const server = await startBackofficeFrontend(); + await openCypress(); + server.close(() => { + process.exit(0); + }); +} + +async function openCypress() { + return cypress.open(); +} + +open(); diff --git a/cypress/plugins/index.ts b/tests/utils/cypress/plugins/index.ts similarity index 55% rename from cypress/plugins/index.ts rename to tests/utils/cypress/plugins/index.ts index 7274878..ebfeb08 100644 --- a/cypress/plugins/index.ts +++ b/tests/utils/cypress/plugins/index.ts @@ -1,6 +1,6 @@ -import container from '../../src/apps/mooc_backend/config/dependency-injection'; -import { EnvironmentArranger } from '../../tests/Contexts/Shared/infrastructure/arranger/EnvironmentArranger'; -import { seed } from '../../src/apps/backoffice/frontend/seed'; +import container from '../../../../src/apps/mooc_backend/config/dependency-injection'; +import { EnvironmentArranger } from '../../../Contexts/Shared/infrastructure/arranger/EnvironmentArranger'; +import { seed } from '../../../../src/apps/backoffice/frontend/seed'; const environmentArranger: Promise = container.get('Mooc.EnvironmentArranger'); diff --git a/tests/utils/cypress/run.ts b/tests/utils/cypress/run.ts new file mode 100644 index 0000000..e4e2862 --- /dev/null +++ b/tests/utils/cypress/run.ts @@ -0,0 +1,26 @@ +import app from '../../../src/apps/backoffice/frontend/app'; +import cypress from 'cypress'; +import cypressConfig from '../../../cypress.json'; +import { startBackofficeFrontend } from './startBackofficeFrontend'; + +async function run() { + const server = await startBackofficeFrontend(); + await runCypress(); + server.close(() => { + process.exit(0); + }); +} + +async function runCypress() { + return cypress.run({ + reporter: 'junit', + browser: 'chrome', + headless: true, + config: { + ...cypressConfig, + baseUrl: `http://localhost:${app.get('port')}` + } + }); +} + +run(); diff --git a/tests/utils/cypress/startBackofficeFrontend.ts b/tests/utils/cypress/startBackofficeFrontend.ts new file mode 100644 index 0000000..5fa1f6b --- /dev/null +++ b/tests/utils/cypress/startBackofficeFrontend.ts @@ -0,0 +1,14 @@ +import app from '../../../src/apps/backoffice/frontend/app'; +import { Server } from 'http'; + +export async function startBackofficeFrontend(): Promise { + let server: Server; + + return new Promise((resolve, reject) => { + server = app.listen(app.get('port'), async () => { + console.log(` Backoffice frontend is running at http://localhost:${app.get('port')} in ${app.get('env')} mode`); + console.log(' Press CTRL-C to stop\n'); + resolve(server); + }); + }); +} diff --git a/cypress/tsconfig.json b/tests/utils/cypress/tsconfig.json similarity index 57% rename from cypress/tsconfig.json rename to tests/utils/cypress/tsconfig.json index f507fd4..cf8c499 100644 --- a/cypress/tsconfig.json +++ b/tests/utils/cypress/tsconfig.json @@ -1,11 +1,9 @@ { - "extends": "../tsconfig.json", + "extends": "../../../tsconfig.json", "compilerOptions": { "lib": ["es2015", "dom"], "types": ["cypress"] }, - "include": [ - "**/*.ts" - ] + "include": ["**/*.ts"] } From da23a4b0f16a6cc5885f8801827cea282dec0d52 Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 2 Oct 2020 20:13:39 +0200 Subject: [PATCH 43/48] Avoid Cypress/Jest types colision --- tsconfig.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index eb52c0a..c91bb5a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,10 +13,7 @@ "outDir": "./dist" }, "include": [ - "src/**/**.ts", - "tests/**/**.ts", - "package*.json", - "project*.json" + "src/**/**.ts" ], "exclude": ["node_modules"] } From bc7dee7cbed2f9d9847a007306a90381f205ae0a Mon Sep 17 00:00:00 2001 From: David Matas Date: Fri, 2 Oct 2020 21:11:07 +0200 Subject: [PATCH 44/48] Fine tune --- cypress/support/commands.js | 25 ------------------- cypress/support/index.js | 20 --------------- .../features/courses/create_course.cypress.ts | 2 +- tests/{utils/cypress => }/tsconfig.json | 2 +- tests/utils/cypress/open.ts | 8 +++++- tests/utils/cypress/run.ts | 1 + 6 files changed, 10 insertions(+), 48 deletions(-) delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js rename tests/{utils/cypress => }/tsconfig.json (74%) diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index ca4d256..0000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index d68db96..0000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts index 0c1b8b1..f8a8a3c 100644 --- a/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts +++ b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts @@ -6,7 +6,7 @@ describe('Courses', () => { }); it('can create courses', () => { - cy.visit('http://localhost:8032/courses'); + cy.visit('courses'); cy.contains('Actualmente CodelyTV Pro cuenta con 0 cursos.'); diff --git a/tests/utils/cypress/tsconfig.json b/tests/tsconfig.json similarity index 74% rename from tests/utils/cypress/tsconfig.json rename to tests/tsconfig.json index cf8c499..4392b35 100644 --- a/tests/utils/cypress/tsconfig.json +++ b/tests/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.json", + "extends": "../tsconfig.json", "compilerOptions": { "lib": ["es2015", "dom"], "types": ["cypress"] diff --git a/tests/utils/cypress/open.ts b/tests/utils/cypress/open.ts index 5a17c51..c81c28d 100644 --- a/tests/utils/cypress/open.ts +++ b/tests/utils/cypress/open.ts @@ -1,5 +1,6 @@ import cypress from 'cypress'; import { startBackofficeFrontend } from './startBackofficeFrontend'; +import app from '../../../src/apps/backoffice/frontend/app'; async function open() { const server = await startBackofficeFrontend(); @@ -10,7 +11,12 @@ async function open() { } async function openCypress() { - return cypress.open(); + return cypress.open({ + config: { + supportFile: false, + baseUrl: `http://localhost:${app.get('port')}` + } + }); } open(); diff --git a/tests/utils/cypress/run.ts b/tests/utils/cypress/run.ts index e4e2862..4544ad8 100644 --- a/tests/utils/cypress/run.ts +++ b/tests/utils/cypress/run.ts @@ -18,6 +18,7 @@ async function runCypress() { headless: true, config: { ...cypressConfig, + supportFile: false, baseUrl: `http://localhost:${app.get('port')}` } }); From fc97497b923c0578c956ab3562782c5bf243dac9 Mon Sep 17 00:00:00 2001 From: David Matas Date: Sun, 18 Oct 2020 11:40:23 +0200 Subject: [PATCH 45/48] Move specific TS config for cypress to app folder tests --- tests/{ => apps}/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{ => apps}/tsconfig.json (75%) diff --git a/tests/tsconfig.json b/tests/apps/tsconfig.json similarity index 75% rename from tests/tsconfig.json rename to tests/apps/tsconfig.json index 4392b35..48ceea6 100644 --- a/tests/tsconfig.json +++ b/tests/apps/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { "lib": ["es2015", "dom"], "types": ["cypress"] From 5bd79e37eb12744cadbc5652a450250b3fb7bf09 Mon Sep 17 00:00:00 2001 From: David Matas Date: Sun, 18 Oct 2020 17:12:02 +0200 Subject: [PATCH 46/48] Include more e2e tests --- package-lock.json | 6 +- package.json | 2 +- .../controllers/CoursesPostController.ts | 6 +- .../frontend/controllers/WebController.ts | 2 +- .../features/courses/create_course.cypress.ts | 115 +++++++++++++++++- 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6af7e9..0b53e94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1352,9 +1352,9 @@ } }, "@types/faker": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.1.0.tgz", - "integrity": "sha512-7iK+rNvtSmG3FcAgI67BphmQVzBPkI6SCjJqR/SXxGr6tDUoMovkhGYIxNICEfy/trTgiGQL2v1tKPuFEXIrQg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.1.2.tgz", + "integrity": "sha512-a3FADSHjjinczCwr7tTejoMZzbSS5vi70VCyns4C1idxJrDSRGZCQG0s27YppXLcoWrBOkwBbBsZ9vDRDpQK7A==", "dev": true }, "@types/glob": { diff --git a/package.json b/package.json index bfcd977..ff3c9df 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@types/cookie-parser": "^1.4.2", "@types/cookie-session": "^2.0.41", "@types/cucumber": "^6.0.1", - "@types/faker": "^5.1.0", + "@types/faker": "^5.1.2", "@types/jest": "^26.0.14", "@types/nunjucks": "^3.1.3", "@types/supertest": "^2.0.10", diff --git a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts index efa1c49..204c996 100644 --- a/src/apps/backoffice/frontend/controllers/CoursesPostController.ts +++ b/src/apps/backoffice/frontend/controllers/CoursesPostController.ts @@ -11,9 +11,9 @@ export class CoursesPostController extends WebController { static validator(): ValidationChain[] { return [ - body('id').isUUID(), - body('name').isLength({ min: 1, max: 30 }), - body('duration').isLength({ min: 4, max: 100 }) + body('id').isUUID().withMessage('Invalid course id'), + body('name').isLength({ min: 1, max: 30 }).withMessage('Invalid name'), + body('duration').isLength({ min: 4, max: 100 }).withMessage('Invalid duration') ]; } diff --git a/src/apps/backoffice/frontend/controllers/WebController.ts b/src/apps/backoffice/frontend/controllers/WebController.ts index ae64abb..5ba63be 100644 --- a/src/apps/backoffice/frontend/controllers/WebController.ts +++ b/src/apps/backoffice/frontend/controllers/WebController.ts @@ -40,7 +40,7 @@ export abstract class WebController { protected render(req: Request, res: Response, template: string, data: { [key: string]: any }) { const flash = this.feedFlash(req, data); - res.render('pages/courses/courses', { + res.render(template, { ...data, ...flash }); diff --git a/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts index f8a8a3c..65ecd0e 100644 --- a/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts +++ b/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts @@ -1,19 +1,21 @@ import faker from 'faker'; describe('Courses', () => { - beforeEach(() => { + before(() => { cy.task('reset:db'); }); - it('can create courses', () => { + beforeEach(() => { cy.visit('courses'); + }); + it('can create courses', () => { cy.contains('Actualmente CodelyTV Pro cuenta con 0 cursos.'); let i = 0; - while (i <= 5) { + while (i <= 3) { i++; - const courseName = faker.lorem.sentence(2); + const courseName = faker.random.words(1); cy.get('input[name="name"]').type(courseName); cy.get('input[name="duration"]').type('8 days'); cy.get('form').submit(); @@ -22,4 +24,109 @@ describe('Courses', () => { cy.contains(`Actualmente CodelyTV Pro cuenta con ${i} cursos.`); } }); + + describe('Course id field', () => { + it('has value by default', () => { + cy.get('input[name="id"]').invoke('val').should('not.be.empty'); + }); + + it('has flash messages when is invalid', () => { + cy.get('input[name="id"]').clear().type('invalid course id'); + cy.get('form').submit(); + + cy.get('input[name="id"] + p').contains('Invalid course id'); + }); + + it('maintain the value introduced by the user when invalid', () => { + cy.get('input[name="id"]').clear().type('invalid course id'); + cy.get('form').submit(); + + cy.get('input[name="id"]').should('have.value', 'invalid course id'); + }); + + it('maintain the value introduced by the user when valid', () => { + const uuid = faker.random.uuid(); + + cy.get('input[name="id"]').clear().type(uuid); + cy.get('form').submit(); + + cy.get('input[name="id"]').should('have.value', uuid); + }); + }); + + describe('Name field', () => { + it('has flash messages when is empty', () => { + cy.get('form').submit(); + + cy.get('input[name="name"] + p').contains('Invalid name'); + }); + + it('has flash messages when is longer than 30 character', () => { + cy.get('input[name="name"]').type(faker.random.alphaNumeric(31)); + + cy.get('form').submit(); + + cy.get('input[name="name"] + p').contains('Invalid name'); + }); + + it('maintain the value introduced by the user when invalid', () => { + const invalidCourseName = faker.random.alphaNumeric(3); + + cy.get('input[name="name"]').clear().type(invalidCourseName); + cy.get('form').submit(); + + cy.get('input[name="name"]').should('have.value', invalidCourseName); + }); + + it('maintain the value introduced by the user when valid', () => { + const validCourseName = faker.random.alphaNumeric(1); + + cy.get('input[name="name"]').clear().type(validCourseName); + cy.get('form').submit(); + + cy.get('input[name="name"]').should('have.value', validCourseName); + }); + }); + + describe('Duration field', () => { + it('has flash messages when is empty', () => { + cy.get('form').submit(); + + cy.get('input[name="duration"] + p').contains('Invalid duration'); + }); + + it('has flash messages when is shorter than 4 character', () => { + cy.get('input[name="duration"]').type(faker.random.alphaNumeric(3)); + + cy.get('form').submit(); + + cy.get('input[name="duration"] + p').contains('Invalid duration'); + }); + + it('has flash messages when is longer than 100 character', () => { + cy.get('input[name="duration"]').type(faker.random.alphaNumeric(101)); + + cy.get('form').submit(); + + cy.get('input[name="duration"] + p').contains('Invalid duration'); + }); + + it('maintain the value introduced by the user when invalid', () => { + const invalidCourseDuration = faker.random.alphaNumeric(101); + + cy.get('input[name="duration"]').clear().type(invalidCourseDuration); + cy.get('form').submit(); + + cy.get('input[name="duration"]').should('have.value', invalidCourseDuration); + }); + + it('maintain the value introduced by the user when valid', () => { + const validCourseDuration = faker.random.alphaNumeric(5); + + cy.get('input[name="duration"]').clear().type(validCourseDuration); + cy.get('form').submit(); + + cy.get('input[name="duration"]').should('have.value', validCourseDuration); + }); + }); }); From 9f907316423f98eb30a62529b847d9c67e2ebf04 Mon Sep 17 00:00:00 2001 From: David Matas Date: Thu, 22 Oct 2020 12:45:06 +0200 Subject: [PATCH 47/48] Change folder names --- .../frontend}/features/courses/create_course.cypress.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/apps/{backoffice_frontend => backoffice/frontend}/features/courses/create_course.cypress.ts (100%) diff --git a/tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts b/tests/apps/backoffice/frontend/features/courses/create_course.cypress.ts similarity index 100% rename from tests/apps/backoffice_frontend/features/courses/create_course.cypress.ts rename to tests/apps/backoffice/frontend/features/courses/create_course.cypress.ts From 6d27b8e8320f9331a2d739d3eeb457ae859610c7 Mon Sep 17 00:00:00 2001 From: David Matas Date: Sat, 21 Nov 2020 13:57:25 +0100 Subject: [PATCH 48/48] Remove unneded file --- .../persistence/FileCourseRepository.ts | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 src/Contexts/Mooc/Courses/infrastructure/persistence/FileCourseRepository.ts diff --git a/src/Contexts/Mooc/Courses/infrastructure/persistence/FileCourseRepository.ts b/src/Contexts/Mooc/Courses/infrastructure/persistence/FileCourseRepository.ts deleted file mode 100644 index 499baf3..0000000 --- a/src/Contexts/Mooc/Courses/infrastructure/persistence/FileCourseRepository.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CourseRepository } from '../../domain/CourseRepository'; -import { Course } from '../../domain/Course'; -import fs from 'fs'; -import BSON from 'bson'; -import { Nullable } from '../../../../Shared/domain/Nullable'; -import { CourseId } from '../../../Shared/domain/Courses/CourseId'; - -export class FileCourseRepository implements CourseRepository { - private FILE_PATH = `${__dirname}/courses`; - - async save(course: Course): Promise { - const filePath = this.filePath(course.id.value); - const data = BSON.serialize(course); - - return fs.writeFileSync(filePath, data); - } - - async search(id: CourseId): Promise> { - const filePath = this.filePath(id.value); - const exists = fs.existsSync(filePath); - - return exists ? BSON.deserialize(fs.readFileSync(this.filePath(id.value))) : null; - } - - private filePath(id: string): string { - return `${this.FILE_PATH}.${id}.repo`; - } -}