From 7631f085ef8b5e7601678978eee637889b8af251 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Mon, 16 Sep 2019 10:46:17 +0700 Subject: [PATCH] Fix packages/server --- .eslintignore | 1 + .eslintrc.yaml | 16 +- packages/common/src/auth/AuthService.ts | 13 ++ .../common/src/auth/ChangePasswordParams.ts | 5 + packages/common/src/auth/index.ts | 2 + packages/common/src/index.ts | 1 + .../src/permissions/BelongsToTeamParams.ts | 4 + .../common/src/permissions/UserPermissions.ts | 6 + packages/common/src/permissions/index.t | 2 + packages/common/src/permissions/index.ts | 3 + packages/server/.eslintrc.yaml | 4 + packages/server/jest.config.js | 8 +- .../{IAppServer.ts => AppServer.ts} | 2 +- .../server/src/application/Application.ts | 7 + packages/server/src/application/Bootstrap.ts | 182 ++---------------- .../server/src/application/CLIBootstrap.ts | 171 ++++++++++++++++ .../src/application/{IConfig.ts => Config.ts} | 2 +- .../server/src/application/IApplication.ts | 7 - packages/server/src/application/IBootstrap.ts | 13 -- .../server/src/application/IServerConfig.ts | 20 -- packages/server/src/application/IServices.ts | 6 - .../server/src/application/ServerConfig.ts | 20 ++ packages/server/src/application/Services.ts | 6 + .../server/src/application/configureServer.ts | 50 +++-- .../server/src/application/createServer.ts | 6 +- packages/server/src/application/index.ts | 9 +- packages/server/src/bootstrap.ts | 8 +- packages/server/src/config.ts | 4 +- packages/server/src/database/Database.ts | 63 +----- packages/server/src/database/IDatabase.ts | 17 -- .../src/database/ITransactionManager.ts | 28 --- packages/server/src/database/SQLDatabase.ts | 51 +++++ .../src/database/SQLTransactionManager.ts | 72 +++++++ .../server/src/database/TransactionManager.ts | 92 +++------ packages/server/src/database/index.test.ts | 21 +- packages/server/src/database/index.ts | 4 +- packages/server/src/entities/BaseEntity.ts | 6 +- packages/server/src/entities/Role.ts | 4 +- packages/server/src/entities/Session.ts | 8 +- packages/server/src/entities/Team.ts | 8 +- packages/server/src/entities/User.ts | 10 +- packages/server/src/entities/UserEmail.ts | 6 +- packages/server/src/entities/UserTeam.ts | 10 +- packages/server/src/error/ErrorTransformer.ts | 2 +- .../server/src/error/valueOrError.test.ts | 2 +- packages/server/src/logger/SqlLogger.ts | 12 +- packages/server/src/logger/index.ts | 2 +- .../src/middleware/Authenticator.test.ts | 15 +- .../server/src/middleware/Authenticator.ts | 20 +- .../server/src/middleware/CSRFMiddleware.ts | 14 +- .../server/src/middleware/ErrorApiHandler.ts | 14 +- .../{TErrorHandler.ts => ErrorHandler.ts} | 2 +- .../server/src/middleware/ErrorPageHandler.ts | 12 +- packages/server/src/middleware/Handler.ts | 3 + packages/server/src/middleware/IMiddleware.ts | 6 - packages/server/src/middleware/Middleware.ts | 6 + .../server/src/middleware/PromiseHandler.ts | 4 + .../server/src/middleware/RequestLogger.ts | 12 +- .../src/middleware/SessionMiddleware.ts | 38 ++-- packages/server/src/middleware/THandler.ts | 3 - .../server/src/middleware/TPromiseHandler.ts | 4 - ...ransaction.ts => TransactionMiddleware.ts} | 8 +- .../src/middleware/ensureLoggedIn.test.ts | 6 +- .../server/src/middleware/ensureLoggedIn.ts | 8 +- .../server/src/middleware/handlePromise.ts | 6 +- packages/server/src/middleware/index.ts | 10 +- .../server/src/router/AsyncRouter.test.ts | 32 +-- packages/server/src/router/AsyncRouter.ts | 60 +++--- packages/server/src/router/ITypedRequest.ts | 8 - packages/server/src/router/TTypedHandler.ts | 23 --- .../server/src/router/TransactionalRouter.ts | 16 +- packages/server/src/router/TypedHandler.ts | 23 +++ packages/server/src/router/TypedRequest.ts | 8 + packages/server/src/router/index.ts | 4 +- packages/server/src/routes/AuthRoutes.ts | 53 ----- packages/server/src/routes/BaseRoute.ts | 14 -- ...es.test.ts => configureAuthRoutes.test.ts} | 0 .../server/src/routes/configureAuthRoutes.ts | 46 +++++ packages/server/src/routes/index.ts | 3 +- packages/server/src/rpc/RPC.ts | 8 +- ...Service.test.ts => SQLTeamService.test.ts} | 4 +- .../rpc/{TeamService.ts => SQLTeamService.ts} | 33 ++-- ...Service.test.ts => SQLUserService.test.ts} | 6 +- .../rpc/{UserService.ts => SQLUserService.ts} | 19 +- packages/server/src/rpc/index.ts | 4 +- packages/server/src/services/IAuthService.ts | 13 -- .../server/src/services/IUserPermissions.ts | 4 - ...Service.test.ts => SQLAuthService.test.ts} | 8 +- .../{AuthService.ts => SQLAuthService.ts} | 19 +- ...erPermissions.ts => SQLUserPermissions.ts} | 10 +- packages/server/src/services/index.ts | 6 +- .../{ISession.ts => DefaultSession.ts} | 2 +- .../server/src/session/SessionStore.test.ts | 25 ++- packages/server/src/session/SessionStore.ts | 26 +-- packages/server/src/session/index.ts | 2 +- .../server/src/test-utils/NamespaceMock.ts | 2 +- .../src/test-utils/RequestTester.test.ts | 10 +- .../server/src/test-utils/RequestTester.ts | 60 +++--- packages/server/src/test-utils/TestUtils.ts | 37 ++-- packages/server/src/test-utils/index.ts | 4 +- packages/server/src/test.ts | 16 +- 101 files changed, 885 insertions(+), 910 deletions(-) create mode 100644 packages/common/src/auth/AuthService.ts create mode 100644 packages/common/src/auth/ChangePasswordParams.ts create mode 100644 packages/common/src/permissions/BelongsToTeamParams.ts create mode 100644 packages/common/src/permissions/UserPermissions.ts create mode 100644 packages/common/src/permissions/index.t create mode 100644 packages/common/src/permissions/index.ts create mode 100644 packages/server/.eslintrc.yaml rename packages/server/src/application/{IAppServer.ts => AppServer.ts} (89%) create mode 100644 packages/server/src/application/Application.ts create mode 100644 packages/server/src/application/CLIBootstrap.ts rename packages/server/src/application/{IConfig.ts => Config.ts} (93%) delete mode 100644 packages/server/src/application/IApplication.ts delete mode 100644 packages/server/src/application/IBootstrap.ts delete mode 100644 packages/server/src/application/IServerConfig.ts delete mode 100644 packages/server/src/application/IServices.ts create mode 100644 packages/server/src/application/ServerConfig.ts create mode 100644 packages/server/src/application/Services.ts delete mode 100644 packages/server/src/database/IDatabase.ts delete mode 100644 packages/server/src/database/ITransactionManager.ts create mode 100644 packages/server/src/database/SQLDatabase.ts create mode 100644 packages/server/src/database/SQLTransactionManager.ts rename packages/server/src/middleware/{TErrorHandler.ts => ErrorHandler.ts} (82%) create mode 100644 packages/server/src/middleware/Handler.ts delete mode 100644 packages/server/src/middleware/IMiddleware.ts create mode 100644 packages/server/src/middleware/Middleware.ts create mode 100644 packages/server/src/middleware/PromiseHandler.ts delete mode 100644 packages/server/src/middleware/THandler.ts delete mode 100644 packages/server/src/middleware/TPromiseHandler.ts rename packages/server/src/middleware/{Transaction.ts => TransactionMiddleware.ts} (78%) delete mode 100644 packages/server/src/router/ITypedRequest.ts delete mode 100644 packages/server/src/router/TTypedHandler.ts create mode 100644 packages/server/src/router/TypedHandler.ts create mode 100644 packages/server/src/router/TypedRequest.ts delete mode 100644 packages/server/src/routes/AuthRoutes.ts delete mode 100644 packages/server/src/routes/BaseRoute.ts rename packages/server/src/routes/{AuthRoutes.test.ts => configureAuthRoutes.test.ts} (100%) create mode 100644 packages/server/src/routes/configureAuthRoutes.ts rename packages/server/src/rpc/{TeamService.test.ts => SQLTeamService.test.ts} (97%) rename packages/server/src/rpc/{TeamService.ts => SQLTeamService.ts} (73%) rename packages/server/src/rpc/{UserService.test.ts => SQLUserService.test.ts} (87%) rename packages/server/src/rpc/{UserService.ts => SQLUserService.ts} (67%) delete mode 100644 packages/server/src/services/IAuthService.ts delete mode 100644 packages/server/src/services/IUserPermissions.ts rename packages/server/src/services/{AuthService.test.ts => SQLAuthService.test.ts} (93%) rename packages/server/src/services/{AuthService.ts => SQLAuthService.ts} (87%) rename packages/server/src/services/{UserPermissions.ts => SQLUserPermissions.ts} (59%) rename packages/server/src/session/{ISession.ts => DefaultSession.ts} (70%) diff --git a/.eslintignore b/.eslintignore index c37dddc..b4f437d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ packages/*/lib packages/*/esm build/ +packages/*/src/migrations diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 476d45b..0b08e16 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -1,8 +1,8 @@ extends: - - eslint:recommended - - plugin:react/recommended - - plugin:@typescript-eslint/eslint-recommended - - plugin:@typescript-eslint/recommended + - eslint:recommended + - plugin:react/recommended + - plugin:@typescript-eslint/eslint-recommended + - plugin:@typescript-eslint/recommended settings: react: version: 'detect' @@ -43,11 +43,13 @@ rules: - ignoreRestArgs: true overrides: - files: - - '*.test.ts' - - '*.test.tsx' + - '*.test.ts' + - '*.test.tsx' rules: '@typescript-eslint/no-explicit-any': off - files: - - '*.js' + - '*.js' + rules: + '@typescript-eslint/no-var-requires': off env: node: true diff --git a/packages/common/src/auth/AuthService.ts b/packages/common/src/auth/AuthService.ts new file mode 100644 index 0000000..9d9894c --- /dev/null +++ b/packages/common/src/auth/AuthService.ts @@ -0,0 +1,13 @@ +import { NewUser, UserProfile } from "../user"; +import { ChangePasswordParams } from "./ChangePasswordParams"; +import { Credentials } from "./Credentials"; + +export interface AuthService { + createUser(credentials: NewUser): Promise + changePassword(params: ChangePasswordParams): Promise + validateCredentials( + credentials: Credentials, + ): Promise + findOne(id: number): Promise + findUserByEmail(email: string): Promise +} diff --git a/packages/common/src/auth/ChangePasswordParams.ts b/packages/common/src/auth/ChangePasswordParams.ts new file mode 100644 index 0000000..a8ab7eb --- /dev/null +++ b/packages/common/src/auth/ChangePasswordParams.ts @@ -0,0 +1,5 @@ +export interface ChangePasswordParams { + userId: number + oldPassword: string + newPassword: string +} diff --git a/packages/common/src/auth/index.ts b/packages/common/src/auth/index.ts index 7021d94..3fd38ac 100644 --- a/packages/common/src/auth/index.ts +++ b/packages/common/src/auth/index.ts @@ -1 +1,3 @@ +export * from './AuthService' export * from './Credentials' +export * from './ChangePasswordParams' diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 3352e2b..81728ea 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -5,6 +5,7 @@ export * from './entities' export * from './filterProps' export * from './guard' export * from './indexBy' +export * from './permissions' export * from './StringUtils' export * from './team' export * from './types' diff --git a/packages/common/src/permissions/BelongsToTeamParams.ts b/packages/common/src/permissions/BelongsToTeamParams.ts new file mode 100644 index 0000000..88d2acd --- /dev/null +++ b/packages/common/src/permissions/BelongsToTeamParams.ts @@ -0,0 +1,4 @@ +export interface BelongsToTeamParams { + userId: number + teamId: number +} diff --git a/packages/common/src/permissions/UserPermissions.ts b/packages/common/src/permissions/UserPermissions.ts new file mode 100644 index 0000000..975c447 --- /dev/null +++ b/packages/common/src/permissions/UserPermissions.ts @@ -0,0 +1,6 @@ +import {BelongsToTeamParams} from './BelongsToTeamParams' + +export interface UserPermissions { + // TODO check for role too + belongsToTeam(params: BelongsToTeamParams): Promise +} diff --git a/packages/common/src/permissions/index.t b/packages/common/src/permissions/index.t new file mode 100644 index 0000000..963e182 --- /dev/null +++ b/packages/common/src/permissions/index.t @@ -0,0 +1,2 @@ +export * from './BelongsToTeamParams' +export * from './UserPermissions' diff --git a/packages/common/src/permissions/index.ts b/packages/common/src/permissions/index.ts new file mode 100644 index 0000000..b55be3b --- /dev/null +++ b/packages/common/src/permissions/index.ts @@ -0,0 +1,3 @@ +export * from './BelongsToTeamParams' +export * from './UserPermissions' + diff --git a/packages/server/.eslintrc.yaml b/packages/server/.eslintrc.yaml new file mode 100644 index 0000000..fb11a84 --- /dev/null +++ b/packages/server/.eslintrc.yaml @@ -0,0 +1,4 @@ +extends: + - ../../.eslintrc.yaml +rules: + '@typescript-eslint/no-explicit-any': off diff --git a/packages/server/jest.config.js b/packages/server/jest.config.js index 7b62f2d..ec06ec7 100644 --- a/packages/server/jest.config.js +++ b/packages/server/jest.config.js @@ -1,17 +1,17 @@ module.exports = { roots: [ - '/src' + '/src', ], transform: { - '^.+\\.tsx?$': 'ts-jest' + '^.+\\.tsx?$': 'ts-jest', }, testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$', moduleFileExtensions: [ 'ts', 'tsx', 'js', - 'jsx' + 'jsx', ], setupFiles: ['/jest.setup.js'], - verbose: false + verbose: false, } diff --git a/packages/server/src/application/IAppServer.ts b/packages/server/src/application/AppServer.ts similarity index 89% rename from packages/server/src/application/IAppServer.ts rename to packages/server/src/application/AppServer.ts index bffa032..279189a 100644 --- a/packages/server/src/application/IAppServer.ts +++ b/packages/server/src/application/AppServer.ts @@ -1,6 +1,6 @@ import { Server } from 'http' -export interface IAppServer { +export interface AppServer { listen(callback?: () => void): Server listen(callback?: () => void): Server listen(portOrPath: number | string, callback?: () => void): Server diff --git a/packages/server/src/application/Application.ts b/packages/server/src/application/Application.ts new file mode 100644 index 0000000..2495d5c --- /dev/null +++ b/packages/server/src/application/Application.ts @@ -0,0 +1,7 @@ +import { Database } from '../database/Database' +import { AppServer } from './AppServer' + +export interface Application { + readonly server: AppServer + readonly database: Database +} diff --git a/packages/server/src/application/Bootstrap.ts b/packages/server/src/application/Bootstrap.ts index 38efd9b..621f947 100644 --- a/packages/server/src/application/Bootstrap.ts +++ b/packages/server/src/application/Bootstrap.ts @@ -1,173 +1,13 @@ -import assert from 'assert' -import { createNamespace, Namespace } from 'cls-hooked' -import { Server } from 'http' -import { AddressInfo } from 'net' -import { Database } from '../database/Database' -import { IDatabase } from '../database/IDatabase' -import { loggerFactory, SqlLogger } from '../logger' -import { ServerConfigurator } from './configureServer' -import { createServer } from './createServer' -import { IApplication } from './IApplication' -import { IBootstrap } from './IBootstrap' -import { IConfig } from './IConfig' -import { IServerConfig } from './IServerConfig' +import {AddressInfo} from 'net' +import {Application} from './Application' +import {Database} from '../database/Database' +import {Config} from './Config' -export interface IBootstrapParams { - readonly config: IConfig - readonly configureServer: ServerConfigurator - readonly namespace?: Namespace - readonly exit?: (code: number) => void - readonly entities?: object - readonly migrations?: object -} - -// tslint:disable-next-line -function getFunctions(obj: object): Function[] { - return Object.keys(obj) - .map(k => (obj as any)[k]) - .filter(f => typeof f === 'function') -} - -export class Bootstrap implements IBootstrap { - protected config: IConfig - protected configureServer: ServerConfigurator - protected namespace: Namespace - protected exit: (code: number) => void - - protected server?: Server - protected inUse: boolean = false - readonly application: IApplication - readonly database: IDatabase - - constructor(params: IBootstrapParams) { - this.config = { - ...params.config, - app: { - ...params.config.app, - db: { - ...params.config.app.db, - entities: params.entities - ? getFunctions(params.entities) - : params.config.app.db.entities, - migrations: params.migrations - ? getFunctions(params.migrations) - : params.config.app.db.migrations, - }, - }, - } - this.configureServer = params.configureServer - this.namespace = params.namespace || createNamespace('application') - this.exit = params.exit || process.exit - - this.database = this.createDatabase() - this.application = this.createApplication(this.database) - } - - getConfig(): IConfig { - return this.config - } - - protected createDatabase(): IDatabase { - const {namespace} = this - const sqlLogger = new SqlLogger(loggerFactory.getLogger('sql'), namespace) - return new Database(namespace, sqlLogger, this.getConfig().app.db) - } - - protected createApplication(database: IDatabase): IApplication { - const {configureServer} = this - return createServer(configureServer(this.getConfig(), database)) - } - - async exec(command: string = 'listen') { - switch (command) { - case 'listen': - await this.listen() - return - case 'migrate': - await this.migrate() - return - case 'migrate-undo': - await this.migrateUndo() - return - default: - throw new Error('Unknown command: ' + command) - } - } - - async listen( - port: number | string | undefined = process.env.PORT || 3000, - hostname: string | undefined= process.env.BIND_HOST, - ) { - const apiLogger = loggerFactory.getLogger('api') - try { - await this.start(port, hostname) - } catch (err) { - apiLogger.error('Error starting server: %s', err.stack) - this.exit(1) - throw err - } - } - - async migrate() { - const connection = await this.database.connect() - await connection.runMigrations() - await connection.close() - } - - async migrateUndo() { - const connection = await this.database.connect() - await connection.undoLastMigration() - await connection.close() - } - - protected async start( - port: number | string | undefined = process.env.PORT, - hostname?: string, - ): Promise { - assert.ok(!this.inUse, 'Server already in use!') - this.inUse = true - - await this.database.connect() - - await new Promise((resolve, reject) => { - const _resolve = () => resolve() - if (!port) { - this.server = this.application.server.listen(_resolve) - return - } else if (typeof port === 'number' && hostname) { - this.server = this.application.server.listen(port, hostname, _resolve) - } else { - this.server = this.application.server.listen(port, _resolve) - } - }) - - const apiLogger = loggerFactory.getLogger('api') - - if (hostname) { - apiLogger.info('Listening on %s %s', port, hostname) - } else { - apiLogger.info('Listening on %s', port) - } - } - - getAddress(): AddressInfo | string { - const address = this.server!.address() - if (!address) { - throw new Error('Server addres is null') - } - return address - } - - async close(): Promise { - return new Promise((resolve, reject) => { - this.server!.close(err => { - if (!err) { - return resolve() - } - reject(err) - }) - this.server = undefined - this.inUse = false - }) - } +export interface Bootstrap { + readonly application: Application + readonly database: Database + getConfig(): Config + listen(port?: number | string, hostname?: string): Promise + getAddress(): AddressInfo | string + close(): Promise } diff --git a/packages/server/src/application/CLIBootstrap.ts b/packages/server/src/application/CLIBootstrap.ts new file mode 100644 index 0000000..3a71362 --- /dev/null +++ b/packages/server/src/application/CLIBootstrap.ts @@ -0,0 +1,171 @@ +import assert from 'assert' +import { createNamespace, Namespace } from 'cls-hooked' +import { Server } from 'http' +import { AddressInfo } from 'net' +import { Database, SQLDatabase } from '../database' +import { loggerFactory, SQLLogger } from '../logger' +import { Application } from './Application' +import { Bootstrap } from './Bootstrap' +import { Config } from './Config' +import { ServerConfigurator } from './configureServer' +import { createServer } from './createServer' + +export interface CLIBootstrapParams { + readonly config: Config + readonly configureServer: ServerConfigurator + readonly namespace?: Namespace + readonly exit?: (code: number) => void + readonly entities?: object + readonly migrations?: object +} + +// tslint:disable-next-line +function getFunctions(obj: object): Function[] { + return Object.keys(obj) + .map(k => (obj as any)[k]) + .filter(f => typeof f === 'function') +} + +export class CLIBootstrap implements Bootstrap { + protected config: Config + protected configureServer: ServerConfigurator + protected namespace: Namespace + protected exit: (code: number) => void + + protected server?: Server + protected inUse = false + readonly application: Application + readonly database: Database + + constructor(params: CLIBootstrapParams) { + this.config = { + ...params.config, + app: { + ...params.config.app, + db: { + ...params.config.app.db, + entities: params.entities + ? getFunctions(params.entities) + : params.config.app.db.entities, + migrations: params.migrations + ? getFunctions(params.migrations) + : params.config.app.db.migrations, + }, + }, + } + this.configureServer = params.configureServer + this.namespace = params.namespace || createNamespace('application') + this.exit = params.exit || process.exit + + this.database = this.createDatabase() + this.application = this.createApplication(this.database) + } + + getConfig(): Config { + return this.config + } + + protected createDatabase(): Database { + const {namespace} = this + const sqlLogger = new SQLLogger(loggerFactory.getLogger('sql'), namespace) + return new SQLDatabase(namespace, sqlLogger, this.getConfig().app.db) + } + + protected createApplication(database: Database): Application { + const {configureServer} = this + return createServer(configureServer(this.getConfig(), database)) + } + + async exec(command = 'listen') { + switch (command) { + case 'listen': + await this.listen() + return + case 'migrate': + await this.migrate() + return + case 'migrate-undo': + await this.migrateUndo() + return + default: + throw new Error('Unknown command: ' + command) + } + } + + async listen( + port: number | string | undefined = process.env.PORT || 3000, + hostname: string | undefined= process.env.BIND_HOST, + ) { + const apiLogger = loggerFactory.getLogger('api') + try { + await this.start(port, hostname) + } catch (err) { + apiLogger.error('Error starting server: %s', err.stack) + this.exit(1) + throw err + } + } + + async migrate() { + const connection = await this.database.connect() + await connection.runMigrations() + await connection.close() + } + + async migrateUndo() { + const connection = await this.database.connect() + await connection.undoLastMigration() + await connection.close() + } + + protected async start( + port: number | string | undefined = process.env.PORT, + hostname?: string, + ): Promise { + assert.ok(!this.inUse, 'Server already in use!') + this.inUse = true + + await this.database.connect() + + await new Promise((resolve, reject) => { + const _resolve = () => resolve() + if (!port) { + this.server = this.application.server.listen(_resolve) + return + } else if (typeof port === 'number' && hostname) { + this.server = this.application.server.listen(port, hostname, _resolve) + } else { + this.server = this.application.server.listen(port, _resolve) + } + }) + + const apiLogger = loggerFactory.getLogger('api') + + if (hostname) { + apiLogger.info('Listening on %s %s', port, hostname) + } else { + apiLogger.info('Listening on %s', port) + } + } + + getAddress(): AddressInfo | string { + const address = this.server!.address() + if (!address) { + throw new Error('Server addres is null') + } + return address + } + + async close(): Promise { + return new Promise((resolve, reject) => { + this.server!.close(err => { + if (!err) { + return resolve() + } + reject(err) + }) + this.server = undefined + this.inUse = false + }) + } +} diff --git a/packages/server/src/application/IConfig.ts b/packages/server/src/application/Config.ts similarity index 93% rename from packages/server/src/application/IConfig.ts rename to packages/server/src/application/Config.ts index be7e762..866df25 100644 --- a/packages/server/src/application/IConfig.ts +++ b/packages/server/src/application/Config.ts @@ -1,7 +1,7 @@ import {UrlWithStringQuery} from 'url' import {ConnectionOptions} from 'typeorm' -export interface IConfig { +export interface Config { readonly app: { readonly name: string readonly baseUrl: UrlWithStringQuery diff --git a/packages/server/src/application/IApplication.ts b/packages/server/src/application/IApplication.ts deleted file mode 100644 index 3c3619e..0000000 --- a/packages/server/src/application/IApplication.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IDatabase } from '../database/IDatabase' -import { IAppServer } from './IAppServer' - -export interface IApplication { - readonly server: IAppServer - readonly database: IDatabase -} diff --git a/packages/server/src/application/IBootstrap.ts b/packages/server/src/application/IBootstrap.ts deleted file mode 100644 index 53a0079..0000000 --- a/packages/server/src/application/IBootstrap.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {AddressInfo} from 'net' -import {IApplication} from './IApplication' -import {IDatabase} from '../database/IDatabase' -import {IConfig} from './IConfig' - -export interface IBootstrap { - readonly application: IApplication - readonly database: IDatabase - getConfig(): IConfig - listen(port?: number | string, hostname?: string): Promise - getAddress(): AddressInfo | string - close(): Promise -} diff --git a/packages/server/src/application/IServerConfig.ts b/packages/server/src/application/IServerConfig.ts deleted file mode 100644 index eab79e5..0000000 --- a/packages/server/src/application/IServerConfig.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IConfig } from './IConfig' -import { IDatabase } from '../database' -import { ILogger } from '@rondo.dev/logger' -import { IServices } from './IServices' -import { RequestHandlerParams, ErrorRequestHandler } from 'express-serve-static-core' - -export interface IServerMiddleware { - path: string - handle: RequestHandlerParams[] - error?: ErrorRequestHandler -} - -export interface IServerConfig { - readonly config: IConfig - readonly database: IDatabase - readonly logger: ILogger - readonly services: IServices - readonly globalErrorHandler: ErrorRequestHandler - readonly framework: Record -} diff --git a/packages/server/src/application/IServices.ts b/packages/server/src/application/IServices.ts deleted file mode 100644 index c5f1a51..0000000 --- a/packages/server/src/application/IServices.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IAuthService, IUserPermissions } from '../services' - -export interface IServices { - authService: IAuthService - userPermissions: IUserPermissions -} diff --git a/packages/server/src/application/ServerConfig.ts b/packages/server/src/application/ServerConfig.ts new file mode 100644 index 0000000..b98219e --- /dev/null +++ b/packages/server/src/application/ServerConfig.ts @@ -0,0 +1,20 @@ +import { Config } from './Config' +import { Database } from '../database' +import { Logger } from '@rondo.dev/logger' +import { Services } from './Services' +import { RequestHandlerParams, ErrorRequestHandler } from 'express-serve-static-core' + +export interface ServerMiddleware { + path: string + handle: RequestHandlerParams[] + error?: ErrorRequestHandler +} + +export interface ServerConfig { + readonly config: Config + readonly database: Database + readonly logger: Logger + readonly services: Services + readonly globalErrorHandler: ErrorRequestHandler + readonly framework: Record +} diff --git a/packages/server/src/application/Services.ts b/packages/server/src/application/Services.ts new file mode 100644 index 0000000..d658dd6 --- /dev/null +++ b/packages/server/src/application/Services.ts @@ -0,0 +1,6 @@ +import { AuthService, UserPermissions } from '@rondo.dev/common' + +export interface Services { + authService: AuthService + userPermissions: UserPermissions +} diff --git a/packages/server/src/application/configureServer.ts b/packages/server/src/application/configureServer.ts index 3b07a6b..18e4a25 100644 --- a/packages/server/src/application/configureServer.ts +++ b/packages/server/src/application/configureServer.ts @@ -1,54 +1,52 @@ -import { IContext } from '@rondo.dev/common' -import { IRoutes } from '@rondo.dev/http-types' import { bulkjsonrpc, jsonrpc } from '@rondo.dev/jsonrpc' import { json } from 'body-parser' import cookieParser from 'cookie-parser' -import { IDatabase } from '../database' +import { Database } from '../database' import { loggerFactory } from '../logger' import * as Middleware from '../middleware' import { TransactionalRouter } from '../router' import * as routes from '../routes' -import * as rpc from '../rpc' -import * as Services from '../services' -import { IConfig } from './IConfig' -import { IServerConfig } from './IServerConfig' -import { IServices } from './IServices' +import { SQLTeamService, SQLUserService, Context } from '../rpc' +import { SQLAuthService, SQLUserPermissions } from '../services' +import { Config } from './Config' +import { ServerConfig } from './ServerConfig' +import { Services } from './Services' +import { Routes } from '@rondo.dev/http-types' +import { configureAuthRoutes } from '../routes/configureAuthRoutes' +import { TransactionMiddleware, CSRFMiddleware, RequestLogger } from '../middleware' export type ServerConfigurator< - T extends IServerConfig = IServerConfig + T extends ServerConfig = ServerConfig > = ( - config: IConfig, - database: IDatabase, + config: Config, + database: Database, ) => T export const configureServer: ServerConfigurator = (config, database) => { const logger = loggerFactory.getLogger('api') - const services: IServices = { - authService: new Services.AuthService(database), - userPermissions: new Services.UserPermissions(database), + const services: Services = { + authService: new SQLAuthService(database), + userPermissions: new SQLUserPermissions(database), } const rpcServices = { - userService: new rpc.UserService(database), - teamService: new rpc.TeamService(database, services.userPermissions), + userService: new SQLUserService(database), + teamService: new SQLTeamService(database, services.userPermissions), } - const getContext = (req: Express.Request): IContext => ({user: req.user}) + const getContext = (req: Express.Request): Context => ({user: req.user}) const rpcMiddleware = jsonrpc( req => getContext(req), logger, - // (details, invoke) => database - // .transactionManager - // .doInNewTransaction(() => invoke()), ) const authenticator = new Middleware.Authenticator(services.authService) const transactionManager = database.transactionManager - const createTransactionalRouter = () => + const createTransactionalRouter = () => new TransactionalRouter(transactionManager) const globalErrorHandler = new Middleware.ErrorPageHandler(logger).handle @@ -69,14 +67,14 @@ export const configureServer: ServerConfigurator = (config, database) => { sessionName: config.app.session.name, sessionSecret: config.app.session.secret, }).handle, - new Middleware.RequestLogger(logger).handle, + new RequestLogger(logger).handle, json(), cookieParser(config.app.session.secret), - new Middleware.CSRFMiddleware({ + new CSRFMiddleware({ baseUrl: config.app.baseUrl, cookieName: config.app.session.name + '_csrf', }).handle, - new Middleware.Transaction(database.namespace).handle, + new TransactionMiddleware(database.namespace).handle, authenticator.handle, ], }, @@ -87,11 +85,11 @@ export const configureServer: ServerConfigurator = (config, database) => { api: { path: '/api', handle: [ - new routes.AuthRoutes( + configureAuthRoutes( services.authService, authenticator, createTransactionalRouter(), - ).handle, + ), ], error: new Middleware.ErrorApiHandler(logger).handle, }, diff --git a/packages/server/src/application/createServer.ts b/packages/server/src/application/createServer.ts index 159e886..ba31b38 100644 --- a/packages/server/src/application/createServer.ts +++ b/packages/server/src/application/createServer.ts @@ -1,8 +1,8 @@ -import { IServerConfig } from './IServerConfig' -import { IApplication } from './IApplication' import express from 'express' +import { Application } from './Application' +import { ServerConfig } from './ServerConfig' -export function createServer(appConfig: IServerConfig): IApplication { +export function createServer(appConfig: ServerConfig): Application { const {config, database, framework} = appConfig const server = express() diff --git a/packages/server/src/application/index.ts b/packages/server/src/application/index.ts index d19af9f..5f41e0f 100644 --- a/packages/server/src/application/index.ts +++ b/packages/server/src/application/index.ts @@ -1,7 +1,8 @@ +export * from './CLIBootstrap' export * from './Bootstrap' -export * from './IApplication' -export * from './IConfig' -export * from './IServerConfig' -export * from './IServices' +export * from './Application' +export * from './Config' +export * from './ServerConfig' +export * from './Services' export * from './configureServer' export * from './createServer' diff --git a/packages/server/src/bootstrap.ts b/packages/server/src/bootstrap.ts index cec1db3..ed111cb 100644 --- a/packages/server/src/bootstrap.ts +++ b/packages/server/src/bootstrap.ts @@ -1,8 +1,8 @@ -import {config} from './config' -import {Bootstrap} from './application/Bootstrap' -import {configureServer} from './application/configureServer' +import { CLIBootstrap } from './application' +import { configureServer } from './application/configureServer' +import { config } from './config' -export default new Bootstrap({ +export default new CLIBootstrap({ config, configureServer, }) diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index 88e9a94..522b76d 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -1,12 +1,12 @@ import ConfigReader from '@rondo.dev/config' -import {IConfig} from './application' +import {Config} from './application' import URL from 'url' const cfg = new ConfigReader(__dirname).read() const baseUrl = URL.parse(cfg.get('app.baseUrl')) -export const config: IConfig = { +export const config: Config = { app: { name: cfg.get('app.name'), assets: cfg.get('app.assets'), diff --git a/packages/server/src/database/Database.ts b/packages/server/src/database/Database.ts index 4ed6b3a..cbd6d10 100644 --- a/packages/server/src/database/Database.ts +++ b/packages/server/src/database/Database.ts @@ -1,58 +1,15 @@ -import {IDatabase} from './IDatabase' -import {Namespace} from 'cls-hooked' -import {TransactionManager} from './TransactionManager' -import { - createConnection, - Connection, - ConnectionOptions, - Logger, - EntitySchema, - ObjectType, - Repository, -} from 'typeorm' +import { Namespace } from 'cls-hooked' +import { Connection, EntityManager, EntitySchema, ObjectType, Repository } from 'typeorm' +import { TransactionManager } from './TransactionManager' -export class Database implements IDatabase { - protected connection?: Connection +export interface Database { + namespace: Namespace transactionManager: TransactionManager - - constructor( - readonly namespace: Namespace, - protected readonly logger: Logger, - protected readonly options: ConnectionOptions, - ) { - this.transactionManager = new TransactionManager( - namespace, - this.getConnection, - ) - } - - async connect(): Promise { - this.connection = await createConnection({ - ...this.options, - logger: this.logger, - }) - return this.connection - } - - getConnection = (): Connection => { - if (!this.connection) { - throw new Error('Not connected! Did you call connect?') - } - return this.connection - } - - async close() { - await this.getConnection().close() - } - - getEntityManager() { - return this.transactionManager.getEntityManager() - } - + connect(): Promise + getConnection(): Connection + getEntityManager(): EntityManager getRepository( target: ObjectType | EntitySchema | string, - ): Repository { - return this.transactionManager.getRepository(target) - } - + ): Repository + close(): Promise } diff --git a/packages/server/src/database/IDatabase.ts b/packages/server/src/database/IDatabase.ts deleted file mode 100644 index 0491b57..0000000 --- a/packages/server/src/database/IDatabase.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - Connection, EntityManager, ObjectType, EntitySchema, Repository -} from 'typeorm' -import {ITransactionManager} from './ITransactionManager' -import {Namespace} from 'cls-hooked' - -export interface IDatabase { - namespace: Namespace - transactionManager: ITransactionManager - connect(): Promise - getConnection(): Connection - getEntityManager(): EntityManager - getRepository( - target: ObjectType | EntitySchema | string, - ): Repository - close(): Promise -} diff --git a/packages/server/src/database/ITransactionManager.ts b/packages/server/src/database/ITransactionManager.ts deleted file mode 100644 index dfa2b37..0000000 --- a/packages/server/src/database/ITransactionManager.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - EntityManager, - EntitySchema, - ObjectType, - Repository, -} from 'typeorm' - -export const ENTITY_MANAGER = 'ENTITY_MANAGER' -export const TRANSACTION_ID = 'TRANSACTION_ID' - -export interface ITransactionManager { - getEntityManager: () => EntityManager - getRepository: ( - target: ObjectType | EntitySchema | string, - ) => Repository - isInTransaction: () => boolean - /** - * Start a new or reuse an existing transaction. - */ - doInTransaction: ( - fn: (entityManager: EntityManager) => Promise) => Promise - /** - * Always start a new transaction, regardless if there is one already in - * progress in the current context. - */ - doInNewTransaction: ( - fn: (entityManager: EntityManager) => Promise) => Promise -} diff --git a/packages/server/src/database/SQLDatabase.ts b/packages/server/src/database/SQLDatabase.ts new file mode 100644 index 0000000..b071a16 --- /dev/null +++ b/packages/server/src/database/SQLDatabase.ts @@ -0,0 +1,51 @@ +import { Namespace } from 'cls-hooked' +import { Connection, ConnectionOptions, createConnection, EntitySchema, Logger, ObjectType, Repository } from 'typeorm' +import { Database } from './Database' +import { SQLTransactionManager } from './SQLTransactionManager' +import { TransactionManager } from './TransactionManager' + +export class SQLDatabase implements Database { + protected connection?: Connection + transactionManager: TransactionManager + + constructor( + readonly namespace: Namespace, + protected readonly logger: Logger, + protected readonly options: ConnectionOptions, + ) { + this.transactionManager = new SQLTransactionManager( + namespace, + this.getConnection, + ) + } + + async connect(): Promise { + this.connection = await createConnection({ + ...this.options, + logger: this.logger, + }) + return this.connection + } + + getConnection = (): Connection => { + if (!this.connection) { + throw new Error('Not connected! Did you call connect?') + } + return this.connection + } + + async close() { + await this.getConnection().close() + } + + getEntityManager() { + return this.transactionManager.getEntityManager() + } + + getRepository( + target: ObjectType | EntitySchema | string, + ): Repository { + return this.transactionManager.getRepository(target) + } + +} diff --git a/packages/server/src/database/SQLTransactionManager.ts b/packages/server/src/database/SQLTransactionManager.ts new file mode 100644 index 0000000..460d45e --- /dev/null +++ b/packages/server/src/database/SQLTransactionManager.ts @@ -0,0 +1,72 @@ +import loggerFactory from '@rondo.dev/logger' +import { Namespace } from 'cls-hooked' +import shortid from 'shortid' +import { Connection, EntityManager, EntitySchema, ObjectType, Repository } from 'typeorm' +import { ENTITY_MANAGER, TransactionManager, TRANSACTION_ID } from './TransactionManager' + +const log = loggerFactory.getLogger('db') + +export type GetConnection = () => Connection + +export class SQLTransactionManager implements TransactionManager { + constructor( + readonly ns: Namespace, + readonly getConnection: GetConnection, + ) {} + + getEntityManager = (): EntityManager => { + const entityManager = this.ns.get(ENTITY_MANAGER) as EntityManager + if (entityManager) { + return entityManager + } + return this.getConnection().manager + } + + getRepository = ( + target: ObjectType | EntitySchema | string, + ): Repository => { + return this.getEntityManager().getRepository(target) + } + + isInTransaction = (): boolean => { + return !!this.ns.get(ENTITY_MANAGER) + } + + async doInTransaction(fn: (em: EntityManager) => Promise) { + const alreadyInTransaction = this.isInTransaction() + if (alreadyInTransaction) { + log.info('doInTransaction: reusing existing transaction') + return await fn(this.getEntityManager()) + } + + log.info('doInTransaction: starting new transaction') + return this.doInNewTransaction(fn) + } + + async doInNewTransaction(fn: (em: EntityManager) => Promise) { + return this.ns.runAndReturn(async () => { + this.setTransactionId(shortid()) + try { + return await this.getConnection().manager + .transaction(async entityManager => { + this.setEntityManager(entityManager) + try { + return await fn(entityManager) + } finally { + this.setEntityManager(undefined) + } + }) + } finally { + this.setTransactionId(undefined) + } + }) + } + + protected setTransactionId(transactionId: string | undefined) { + this.ns.set(TRANSACTION_ID, transactionId) + } + + protected setEntityManager(entityManager: EntityManager | undefined) { + this.ns.set(ENTITY_MANAGER, entityManager) + } +} diff --git a/packages/server/src/database/TransactionManager.ts b/packages/server/src/database/TransactionManager.ts index 4521766..51cb91c 100644 --- a/packages/server/src/database/TransactionManager.ts +++ b/packages/server/src/database/TransactionManager.ts @@ -1,72 +1,28 @@ -import loggerFactory from '@rondo.dev/logger' -import { Namespace } from 'cls-hooked' -import shortid from 'shortid' -import { Connection, EntityManager, EntitySchema, ObjectType, Repository } from 'typeorm' -import { ENTITY_MANAGER, ITransactionManager, TRANSACTION_ID } from './ITransactionManager' +import { + EntityManager, + EntitySchema, + ObjectType, + Repository, +} from 'typeorm' -const log = loggerFactory.getLogger('db') +export const ENTITY_MANAGER = 'ENTITY_MANAGER' +export const TRANSACTION_ID = 'TRANSACTION_ID' -export type TConnectionGetter = () => Connection - -export class TransactionManager implements ITransactionManager { - constructor( - readonly ns: Namespace, - readonly getConnection: TConnectionGetter, - ) {} - - getEntityManager = (): EntityManager => { - const entityManager = this.ns.get(ENTITY_MANAGER) as EntityManager - if (entityManager) { - return entityManager - } - return this.getConnection().manager - } - - getRepository = ( +export interface TransactionManager { + getEntityManager: () => EntityManager + getRepository: ( target: ObjectType | EntitySchema | string, - ): Repository => { - return this.getEntityManager().getRepository(target) - } - - isInTransaction = (): boolean => { - return !!this.ns.get(ENTITY_MANAGER) - } - - async doInTransaction(fn: (em: EntityManager) => Promise) { - const alreadyInTransaction = this.isInTransaction() - if (alreadyInTransaction) { - log.info('doInTransaction: reusing existing transaction') - return await fn(this.getEntityManager()) - } - - log.info('doInTransaction: starting new transaction') - return this.doInNewTransaction(fn) - } - - async doInNewTransaction(fn: (em: EntityManager) => Promise) { - return this.ns.runAndReturn(async () => { - this.setTransactionId(shortid()) - try { - return await this.getConnection().manager - .transaction(async entityManager => { - this.setEntityManager(entityManager) - try { - return await fn(entityManager) - } finally { - this.setEntityManager(undefined) - } - }) - } finally { - this.setTransactionId(undefined) - } - }) - } - - protected setTransactionId(transactionId: string | undefined) { - this.ns.set(TRANSACTION_ID, transactionId) - } - - protected setEntityManager(entityManager: EntityManager | undefined) { - this.ns.set(ENTITY_MANAGER, entityManager) - } + ) => Repository + isInTransaction: () => boolean + /** + * Start a new or reuse an existing transaction. + */ + doInTransaction: ( + fn: (entityManager: EntityManager) => Promise) => Promise + /** + * Always start a new transaction, regardless if there is one already in + * progress in the current context. + */ + doInNewTransaction: ( + fn: (entityManager: EntityManager) => Promise) => Promise } diff --git a/packages/server/src/database/index.test.ts b/packages/server/src/database/index.test.ts index 65cd360..dfdb721 100644 --- a/packages/server/src/database/index.test.ts +++ b/packages/server/src/database/index.test.ts @@ -1,15 +1,16 @@ -import {Database, ENTITY_MANAGER} from './' +import { createNamespace } from 'cls-hooked' +import express, { NextFunction, Request, Response } from 'express' import request from 'supertest' -import express, {Request, Response, NextFunction} from 'express' -import {createNamespace} from 'cls-hooked' -import {CORRELATION_ID, Transaction} from '../middleware' -import {config} from '../config' -import {SqlLogger, loggerFactory} from '../logger' +import { config } from '../config' +import { loggerFactory, SQLLogger } from '../logger' +import { CORRELATION_ID, TransactionMiddleware } from '../middleware' +import { ENTITY_MANAGER } from './' +import { SQLDatabase } from './SQLDatabase' const ns = createNamespace('clsify-test') -const database = new Database( +const database = new SQLDatabase( ns, - new SqlLogger(loggerFactory.getLogger('sql'), ns), + new SQLLogger(loggerFactory.getLogger('sql'), ns), config.app.db, ) @@ -25,7 +26,7 @@ describe('middleware/Transaction', () => { setImmediate(next) } - app.use(new Transaction(ns).handle) + app.use(new TransactionMiddleware(ns).handle) app.use('/:id', handler) app.use('/:id', handler) app.use('/:id', handler) @@ -61,7 +62,7 @@ describe('doInTransaction', () => { let entityManager: any const app = express() - app.use(new Transaction(ns).handle) + app.use(new TransactionMiddleware(ns).handle) app.use('/', (req, res, next) => { if (entityManager) { ns.set(ENTITY_MANAGER, entityManager) diff --git a/packages/server/src/database/index.ts b/packages/server/src/database/index.ts index 59ba1d2..8d23dba 100644 --- a/packages/server/src/database/index.ts +++ b/packages/server/src/database/index.ts @@ -1,4 +1,4 @@ -export * from './ITransactionManager' export * from './TransactionManager' -export * from './IDatabase' +export * from './SQLTransactionManager' export * from './Database' +export * from './SQLDatabase' diff --git a/packages/server/src/entities/BaseEntity.ts b/packages/server/src/entities/BaseEntity.ts index 8eaa765..81d38dc 100644 --- a/packages/server/src/entities/BaseEntity.ts +++ b/packages/server/src/entities/BaseEntity.ts @@ -1,8 +1,4 @@ -import { - PrimaryGeneratedColumn, - CreateDateColumn, - UpdateDateColumn, -} from 'typeorm' +import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' const transformer = { from: (value: Date) => !isNaN(value.getTime()) ? value.toISOString() : value, diff --git a/packages/server/src/entities/Role.ts b/packages/server/src/entities/Role.ts index 2a20c9c..6bcd78b 100644 --- a/packages/server/src/entities/Role.ts +++ b/packages/server/src/entities/Role.ts @@ -1,5 +1,5 @@ -import {BaseEntity} from './BaseEntity' -import {Column, Entity} from 'typeorm' +import { Column, Entity } from 'typeorm' +import { BaseEntity } from './BaseEntity' @Entity() export class Role extends BaseEntity { diff --git a/packages/server/src/entities/Session.ts b/packages/server/src/entities/Session.ts index a0d863f..2d24a4f 100644 --- a/packages/server/src/entities/Session.ts +++ b/packages/server/src/entities/Session.ts @@ -1,9 +1,9 @@ -import {ISession} from '../session/ISession' -import {Column, Entity, PrimaryColumn, Index, ManyToOne} from 'typeorm' -import {User} from './User' +import { Column, Entity, Index, ManyToOne, PrimaryColumn } from 'typeorm' +import { DefaultSession } from '../session/DefaultSession' +import { User } from './User' @Entity() -export class Session implements ISession { +export class Session implements DefaultSession { @PrimaryColumn() id!: string diff --git a/packages/server/src/entities/Team.ts b/packages/server/src/entities/Team.ts index 66b6e65..24498a3 100644 --- a/packages/server/src/entities/Team.ts +++ b/packages/server/src/entities/Team.ts @@ -1,7 +1,7 @@ -import {BaseEntity} from './BaseEntity' -import {Column, Entity, OneToMany, ManyToOne, Index} from 'typeorm' -import {UserTeam} from './UserTeam' -import {User} from './User' +import { Column, Entity, Index, ManyToOne, OneToMany } from 'typeorm' +import { BaseEntity } from './BaseEntity' +import { User } from './User' +import { UserTeam } from './UserTeam' @Entity() export class Team extends BaseEntity { diff --git a/packages/server/src/entities/User.ts b/packages/server/src/entities/User.ts index 56b98af..043f9a9 100644 --- a/packages/server/src/entities/User.ts +++ b/packages/server/src/entities/User.ts @@ -1,8 +1,8 @@ -import {BaseEntity} from './BaseEntity' -import {Column, Entity, OneToMany} from 'typeorm' -import {Session} from './Session' -import {UserTeam} from './UserTeam' -import {UserEmail} from './UserEmail' +import { Column, Entity, OneToMany } from 'typeorm' +import { BaseEntity } from './BaseEntity' +import { Session } from './Session' +import { UserEmail } from './UserEmail' +import { UserTeam } from './UserTeam' @Entity() export class User extends BaseEntity { diff --git a/packages/server/src/entities/UserEmail.ts b/packages/server/src/entities/UserEmail.ts index 91ea8a0..4bbc6b6 100644 --- a/packages/server/src/entities/UserEmail.ts +++ b/packages/server/src/entities/UserEmail.ts @@ -1,6 +1,6 @@ -import {BaseEntity} from './BaseEntity' -import {Column, Entity, PrimaryGeneratedColumn, ManyToOne} from 'typeorm' -import {User} from './User' +import { Column, Entity, ManyToOne } from 'typeorm' +import { BaseEntity } from './BaseEntity' +import { User } from './User' @Entity() export class UserEmail extends BaseEntity { diff --git a/packages/server/src/entities/UserTeam.ts b/packages/server/src/entities/UserTeam.ts index 97a87e0..dd1889d 100644 --- a/packages/server/src/entities/UserTeam.ts +++ b/packages/server/src/entities/UserTeam.ts @@ -1,8 +1,8 @@ -import {BaseEntity} from './BaseEntity' -import {Column, Entity, ManyToOne} from 'typeorm' -import {Role} from './Role' -import {Team} from './Team' -import {User} from './User' +import { Column, Entity, ManyToOne } from 'typeorm' +import { BaseEntity } from './BaseEntity' +import { Role } from './Role' +import { Team } from './Team' +import { User } from './User' @Entity() export class UserTeam extends BaseEntity { diff --git a/packages/server/src/error/ErrorTransformer.ts b/packages/server/src/error/ErrorTransformer.ts index 071af56..bc63f9d 100644 --- a/packages/server/src/error/ErrorTransformer.ts +++ b/packages/server/src/error/ErrorTransformer.ts @@ -1,4 +1,4 @@ -import {TransformedError} from './TransformedError' +import { TransformedError } from './TransformedError' export class ErrorTransformer { constructor( diff --git a/packages/server/src/error/valueOrError.test.ts b/packages/server/src/error/valueOrError.test.ts index 58fbae5..14fd55e 100644 --- a/packages/server/src/error/valueOrError.test.ts +++ b/packages/server/src/error/valueOrError.test.ts @@ -1,4 +1,4 @@ -import {valueOrError} from './' +import { valueOrError } from './' describe('valueOrError', () => { diff --git a/packages/server/src/logger/SqlLogger.ts b/packages/server/src/logger/SqlLogger.ts index 8bf868f..698bc35 100644 --- a/packages/server/src/logger/SqlLogger.ts +++ b/packages/server/src/logger/SqlLogger.ts @@ -1,12 +1,12 @@ -import {ILogger} from '@rondo.dev/logger' -import {Logger, QueryRunner} from 'typeorm' -import {Namespace} from 'cls-hooked' -import {CORRELATION_ID} from '../middleware/Transaction' +import { Logger } from '@rondo.dev/logger' +import { Namespace } from 'cls-hooked' +import { Logger as TypeORMLogger, QueryRunner } from 'typeorm' import { TRANSACTION_ID } from '../database' +import { CORRELATION_ID } from '../middleware/TransactionMiddleware' -export class SqlLogger implements Logger { +export class SQLLogger implements TypeORMLogger { constructor( - protected readonly logger: ILogger, + protected readonly logger: Logger, protected readonly ns: Namespace, ) {} diff --git a/packages/server/src/logger/index.ts b/packages/server/src/logger/index.ts index ea3db8d..d60685c 100644 --- a/packages/server/src/logger/index.ts +++ b/packages/server/src/logger/index.ts @@ -1,6 +1,6 @@ export * from './SQLLogger' import loggerFactory from '@rondo.dev/logger' -export {loggerFactory} +export { loggerFactory } export const getLogger = loggerFactory.getLogger export const apiLogger = getLogger('api') diff --git a/packages/server/src/middleware/Authenticator.test.ts b/packages/server/src/middleware/Authenticator.test.ts index 7db406d..9e186a8 100644 --- a/packages/server/src/middleware/Authenticator.test.ts +++ b/packages/server/src/middleware/Authenticator.test.ts @@ -1,10 +1,9 @@ -import express, {Application} from 'express' +import { AuthService, Credentials } from '@rondo.dev/common' +import { urlencoded } from 'body-parser' +import express, { Application } from 'express' import request from 'supertest' -import {Authenticator} from './Authenticator' -import {ICredentials} from '@rondo.dev/common' -import {IAuthService} from '../services' -import {handlePromise} from './handlePromise' -import {urlencoded} from 'body-parser' +import { Authenticator } from './Authenticator' +import { handlePromise } from './handlePromise' describe('Authenticator', () => { @@ -18,7 +17,7 @@ describe('Authenticator', () => { firstName: 'test', lastName: 'test', } - const authService = new (class implements IAuthService { + const authService = new (class implements AuthService { async createUser() { return {id: 1, ...userInfo} } @@ -26,7 +25,7 @@ describe('Authenticator', () => { async findOne(id: number) { return {id, ...userInfo} } - async validateCredentials({username, password}: ICredentials) { + async validateCredentials({username, password}: Credentials) { if (username === 'test' && password === 'pass') { return {id: 1, ...userInfo} return diff --git a/packages/server/src/middleware/Authenticator.ts b/packages/server/src/middleware/Authenticator.ts index b24173b..002b9e1 100644 --- a/packages/server/src/middleware/Authenticator.ts +++ b/packages/server/src/middleware/Authenticator.ts @@ -1,15 +1,15 @@ -import {Authenticator as A, Passport} from 'passport' -import {IAuthService} from '../services' -import {Strategy as LocalStrategy} from 'passport-local' -import {THandler} from './THandler' -import {IMiddleware} from './IMiddleware' +import { Authenticator as A, Passport } from 'passport' +import { Strategy as LocalStrategy } from 'passport-local' +import { Middleware } from './Middleware' +import { Handler } from './Handler' +import { AuthService } from '@rondo.dev/common' -export class Authenticator implements IMiddleware { +export class Authenticator implements Middleware { protected readonly passport: A - readonly handle: THandler[] + readonly handle: Handler[] - constructor(protected readonly authService: IAuthService) { + constructor(protected readonly authService: AuthService) { this.passport = new Passport() as any this.configurePassport() @@ -22,7 +22,7 @@ export class Authenticator implements IMiddleware { ] } - withLogInPromise: THandler = (req, res, next) => { + withLogInPromise: Handler = (req, res, next) => { req.logInPromise = (user) => { return new Promise((resolve, reject) => { req.logIn(user, err => { @@ -72,7 +72,7 @@ export class Authenticator implements IMiddleware { .catch(done) } - authenticate(strategy: string | string[]): THandler { + authenticate(strategy: string | string[]): Handler { return (req, res, next) => { return new Promise((resolve, reject) => { this.passport.authenticate(strategy, (err: Error, user, info) => { diff --git a/packages/server/src/middleware/CSRFMiddleware.ts b/packages/server/src/middleware/CSRFMiddleware.ts index 14c5b6e..c0e1ab0 100644 --- a/packages/server/src/middleware/CSRFMiddleware.ts +++ b/packages/server/src/middleware/CSRFMiddleware.ts @@ -1,17 +1,17 @@ import Csurf from 'csurf' -import {THandler} from './THandler' -import {IMiddleware} from './IMiddleware' -import {UrlWithStringQuery} from 'url' +import { UrlWithStringQuery } from 'url' +import { Middleware } from './Middleware' +import { Handler } from './Handler' -export interface ICSRFParams { +export interface CSRFMiddlewareParams { baseUrl: UrlWithStringQuery cookieName: string } -export class CSRFMiddleware implements IMiddleware { - readonly handle: THandler +export class CSRFMiddleware implements Middleware { + readonly handle: Handler - constructor(readonly params: ICSRFParams) { + constructor(readonly params: CSRFMiddlewareParams) { this.handle = Csurf({ cookie: { signed: true, diff --git a/packages/server/src/middleware/ErrorApiHandler.ts b/packages/server/src/middleware/ErrorApiHandler.ts index 90d53f3..947742f 100644 --- a/packages/server/src/middleware/ErrorApiHandler.ts +++ b/packages/server/src/middleware/ErrorApiHandler.ts @@ -1,12 +1,12 @@ -import {TErrorHandler} from './TErrorHandler' -import {ILogger} from '@rondo.dev/logger' -import {IMiddleware} from './IMiddleware' -import {ValidationError} from '@rondo.dev/validator' +import { Logger } from '@rondo.dev/logger' +import { ValidationError } from '@rondo.dev/validator' +import { ErrorHandler } from './ErrorHandler' +import { Middleware } from './Middleware' -export class ErrorApiHandler implements IMiddleware { - constructor(readonly logger: ILogger) {} +export class ErrorApiHandler implements Middleware { + constructor(readonly logger: Logger) {} - handle: TErrorHandler = (err, req, res, next) => { + handle: ErrorHandler = (err, req, res, next) => { this.logger.error('%s An API error occurred: %s', req.correlationId, err.stack) const statusCode = this.getStatus(err) diff --git a/packages/server/src/middleware/TErrorHandler.ts b/packages/server/src/middleware/ErrorHandler.ts similarity index 82% rename from packages/server/src/middleware/TErrorHandler.ts rename to packages/server/src/middleware/ErrorHandler.ts index 32978b1..74e3a73 100644 --- a/packages/server/src/middleware/TErrorHandler.ts +++ b/packages/server/src/middleware/ErrorHandler.ts @@ -1,4 +1,4 @@ import {Request, Response, NextFunction} from 'express' -export type TErrorHandler = +export type ErrorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => any diff --git a/packages/server/src/middleware/ErrorPageHandler.ts b/packages/server/src/middleware/ErrorPageHandler.ts index 2eec2c6..e211c62 100644 --- a/packages/server/src/middleware/ErrorPageHandler.ts +++ b/packages/server/src/middleware/ErrorPageHandler.ts @@ -1,11 +1,11 @@ -import {ILogger} from '@rondo.dev/logger' -import {IMiddleware} from './IMiddleware' -import {TErrorHandler} from './TErrorHandler' +import { Logger } from '@rondo.dev/logger' +import { ErrorHandler } from './ErrorHandler' +import { Middleware } from './Middleware' -export class ErrorPageHandler implements IMiddleware { - constructor(readonly logger: ILogger) {} +export class ErrorPageHandler implements Middleware { + constructor(readonly logger: Logger) {} - handle: TErrorHandler = (err, req, res, next) => { + handle: ErrorHandler = (err, req, res, next) => { this.logger.error( '%s An error occurred: %s', req.correlationId, err.stack) diff --git a/packages/server/src/middleware/Handler.ts b/packages/server/src/middleware/Handler.ts new file mode 100644 index 0000000..bf5c74f --- /dev/null +++ b/packages/server/src/middleware/Handler.ts @@ -0,0 +1,3 @@ +import { NextFunction, Request, Response } from 'express' + +export type Handler = (req: Request, res: Response, next: NextFunction) => any diff --git a/packages/server/src/middleware/IMiddleware.ts b/packages/server/src/middleware/IMiddleware.ts deleted file mode 100644 index 46400a5..0000000 --- a/packages/server/src/middleware/IMiddleware.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {THandler} from './THandler' -import {TErrorHandler} from './TErrorHandler' - -export interface IMiddleware { - handle: THandler | THandler[] | TErrorHandler -} diff --git a/packages/server/src/middleware/Middleware.ts b/packages/server/src/middleware/Middleware.ts new file mode 100644 index 0000000..db1ac73 --- /dev/null +++ b/packages/server/src/middleware/Middleware.ts @@ -0,0 +1,6 @@ +import {Handler} from './Handler' +import {ErrorHandler} from './ErrorHandler' + +export interface Middleware { + handle: Handler | Handler[] | ErrorHandler +} diff --git a/packages/server/src/middleware/PromiseHandler.ts b/packages/server/src/middleware/PromiseHandler.ts new file mode 100644 index 0000000..42be8f8 --- /dev/null +++ b/packages/server/src/middleware/PromiseHandler.ts @@ -0,0 +1,4 @@ +import { NextFunction, Request, Response } from 'express' + +export type PromiseHandler = + (req: Request, res: Response, next: NextFunction) => Promise diff --git a/packages/server/src/middleware/RequestLogger.ts b/packages/server/src/middleware/RequestLogger.ts index 191c374..fc27739 100644 --- a/packages/server/src/middleware/RequestLogger.ts +++ b/packages/server/src/middleware/RequestLogger.ts @@ -1,12 +1,12 @@ -import {THandler} from './THandler' -import {ILogger} from '@rondo.dev/logger' -import {IMiddleware} from './IMiddleware' +import { Logger } from '@rondo.dev/logger' import shortid from 'shortid' +import { Handler } from './Handler' +import { Middleware } from './Middleware' -export class RequestLogger implements IMiddleware { - constructor(protected readonly logger: ILogger) {} +export class RequestLogger implements Middleware { + constructor(protected readonly logger: Logger) {} - handle: THandler = (req, res, next) => { + handle: Handler = (req, res, next) => { const start = Date.now() req.correlationId = shortid.generate() res.on('finish', () => { diff --git a/packages/server/src/middleware/SessionMiddleware.ts b/packages/server/src/middleware/SessionMiddleware.ts index 9c76670..acf0f9d 100644 --- a/packages/server/src/middleware/SessionMiddleware.ts +++ b/packages/server/src/middleware/SessionMiddleware.ts @@ -1,24 +1,24 @@ import ExpressSession from 'express-session' -import {THandler} from './THandler' -import {IMiddleware} from './IMiddleware' -import {ISession} from '../session/ISession' -import {ITransactionManager} from '../database/ITransactionManager' -import {Session as SessionEntity} from '../entities/Session' -import {SessionStore} from '../session/SessionStore' -import {UrlWithStringQuery} from 'url' -import {apiLogger} from '../logger' +import { UrlWithStringQuery } from 'url' +import { TransactionManager } from '../database' +import { Session as SessionEntity } from '../entities/Session' +import { apiLogger } from '../logger' +import { SessionStore } from '../session/SessionStore' +import { Handler } from './Handler' +import { Middleware } from './Middleware' +import { DefaultSession } from '../session' -export interface ISessionOptions { - transactionManager: ITransactionManager, - baseUrl: UrlWithStringQuery, - sessionName: string, - sessionSecret: string | string[], +export interface SessionMiddlewareParams { + transactionManager: TransactionManager + baseUrl: UrlWithStringQuery + sessionName: string + sessionSecret: string | string[] } -export class SessionMiddleware implements IMiddleware { - readonly handle: THandler +export class SessionMiddleware implements Middleware { + readonly handle: Handler - constructor(readonly params: ISessionOptions) { + constructor(readonly params: SessionMiddlewareParams) { this.handle = ExpressSession({ saveUninitialized: false, secret: params.sessionSecret, @@ -42,8 +42,10 @@ export class SessionMiddleware implements IMiddleware { }) } - protected buildSession = (sessionData: Express.SessionData, sess: ISession) - : SessionEntity => { + protected buildSession = ( + sessionData: Express.SessionData, + sess: DefaultSession, + ): SessionEntity => { return {...sess, userId: sessionData.userId } } diff --git a/packages/server/src/middleware/THandler.ts b/packages/server/src/middleware/THandler.ts deleted file mode 100644 index e7bd743..0000000 --- a/packages/server/src/middleware/THandler.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {Request, Response, NextFunction} from 'express' - -export type THandler = (req: Request, res: Response, next: NextFunction) => any diff --git a/packages/server/src/middleware/TPromiseHandler.ts b/packages/server/src/middleware/TPromiseHandler.ts deleted file mode 100644 index 43f52b3..0000000 --- a/packages/server/src/middleware/TPromiseHandler.ts +++ /dev/null @@ -1,4 +0,0 @@ -import {Request, Response, NextFunction} from 'express' - -export type TPromiseHandler = - (req: Request, res: Response, next: NextFunction) => Promise diff --git a/packages/server/src/middleware/Transaction.ts b/packages/server/src/middleware/TransactionMiddleware.ts similarity index 78% rename from packages/server/src/middleware/Transaction.ts rename to packages/server/src/middleware/TransactionMiddleware.ts index e5c6984..eaa961e 100644 --- a/packages/server/src/middleware/Transaction.ts +++ b/packages/server/src/middleware/TransactionMiddleware.ts @@ -1,14 +1,14 @@ import shortid from 'shortid' -import {IMiddleware} from './IMiddleware' -import {THandler} from './THandler' +import {Middleware} from './Middleware' +import {Handler} from './Handler' import {Namespace} from 'cls-hooked' export const CORRELATION_ID = 'CORRELATION_ID' -export class Transaction implements IMiddleware { +export class TransactionMiddleware implements Middleware { constructor(readonly ns: Namespace) {} - handle: THandler = (req, res, next) => { + handle: Handler = (req, res, next) => { const {ns} = this ns.bindEmitter(req) ns.bindEmitter(res) diff --git a/packages/server/src/middleware/ensureLoggedIn.test.ts b/packages/server/src/middleware/ensureLoggedIn.test.ts index 4a28b5d..18d5173 100644 --- a/packages/server/src/middleware/ensureLoggedIn.test.ts +++ b/packages/server/src/middleware/ensureLoggedIn.test.ts @@ -1,8 +1,6 @@ -import {ensureLoggedInApi, ensureLoggedInSite} from './ensureLoggedIn' -import express, { - Application, Request, Response, NextFunction as Next, -} from 'express' +import express, { Application, NextFunction as Next, Request, Response } from 'express' import request from 'supertest' +import { ensureLoggedInApi, ensureLoggedInSite } from './ensureLoggedIn' function createMockUserMiddleware() { const context: {user?: any} = {} diff --git a/packages/server/src/middleware/ensureLoggedIn.ts b/packages/server/src/middleware/ensureLoggedIn.ts index 9e7e999..8267bb3 100644 --- a/packages/server/src/middleware/ensureLoggedIn.ts +++ b/packages/server/src/middleware/ensureLoggedIn.ts @@ -1,10 +1,10 @@ +import { Request } from 'express' import createError from 'http-errors' -import {Request} from 'express' -import {THandler} from './THandler' +import { Handler } from './Handler' const isLoggedIn = (req: Request) => !!(req as any).user -export const ensureLoggedInApi: THandler = (req, res, next) => { +export const ensureLoggedInApi: Handler = (req, res, next) => { if (!isLoggedIn(req)) { next(createError(401)) return @@ -12,7 +12,7 @@ export const ensureLoggedInApi: THandler = (req, res, next) => { next() } -export const ensureLoggedInSite = (redirectTo: string): THandler => { +export const ensureLoggedInSite = (redirectTo: string): Handler => { return function _ensureLoggedInSite(req, res, next) { if (!isLoggedIn(req)) { res.redirect(redirectTo) diff --git a/packages/server/src/middleware/handlePromise.ts b/packages/server/src/middleware/handlePromise.ts index e3f4f56..41dec27 100644 --- a/packages/server/src/middleware/handlePromise.ts +++ b/packages/server/src/middleware/handlePromise.ts @@ -1,7 +1,7 @@ -import {THandler} from './THandler' -import {TPromiseHandler} from './TPromiseHandler' +import { Handler } from './Handler' +import { PromiseHandler } from './PromiseHandler' -export function handlePromise(endpoint: TPromiseHandler): THandler { +export function handlePromise(endpoint: PromiseHandler): Handler { return (req, res, next) => { const promise = endpoint(req, res, next) promise diff --git a/packages/server/src/middleware/index.ts b/packages/server/src/middleware/index.ts index cabfa71..e8e0a7d 100644 --- a/packages/server/src/middleware/index.ts +++ b/packages/server/src/middleware/index.ts @@ -2,12 +2,12 @@ export * from './Authenticator' export * from './CSRFMiddleware' export * from './ErrorApiHandler' export * from './ErrorPageHandler' -export * from './IMiddleware' +export * from './Middleware' export * from './RequestLogger' export * from './SessionMiddleware' -export * from './TErrorHandler' -export * from './THandler' -export * from './TPromiseHandler' -export * from './Transaction' +export * from './ErrorHandler' +export * from './Handler' +export * from './PromiseHandler' +export * from './TransactionMiddleware' export * from './ensureLoggedIn' export * from './handlePromise' diff --git a/packages/server/src/router/AsyncRouter.test.ts b/packages/server/src/router/AsyncRouter.test.ts index a069eb3..5a3b049 100644 --- a/packages/server/src/router/AsyncRouter.test.ts +++ b/packages/server/src/router/AsyncRouter.test.ts @@ -4,32 +4,32 @@ import {AsyncRouter} from './AsyncRouter' describe('AsyncRouter', () => { - interface IResponse { + interface Response { value: string } - interface IParam { + interface Param { param: string } - interface IHandler { - params: IParam, - response: IResponse, + interface Handler { + params: Param + response: Response } - interface IMyApi { + interface MyApi { '/test/:param': { - get: IHandler - post: IHandler - put: IHandler - delete: IHandler - options: IHandler - patch: IHandler - head: {}, + get: Handler + post: Handler + put: Handler + delete: Handler + options: Handler + patch: Handler + head: {} } '/middleware': { get: { - response: IResponse + response: Response } } } @@ -37,7 +37,7 @@ describe('AsyncRouter', () => { const app = express() const router = express.Router() app.use(router) - const asyncRouter = new AsyncRouter(router) + const asyncRouter = new AsyncRouter(router) asyncRouter.get('/test/:param', async req => { return {value: req.params.param} @@ -72,7 +72,7 @@ describe('AsyncRouter', () => { }) it('creates its own router when not provided', () => { - const r = new AsyncRouter() + const r = new AsyncRouter() expect(r.router).toBeTruthy() }) diff --git a/packages/server/src/router/AsyncRouter.ts b/packages/server/src/router/AsyncRouter.ts index 3e88ca2..8ee6900 100644 --- a/packages/server/src/router/AsyncRouter.ts +++ b/packages/server/src/router/AsyncRouter.ts @@ -1,8 +1,8 @@ +import { Method, Routes } from '@rondo.dev/http-types' import express from 'express' -import {IRoutes, TMethod} from '@rondo.dev/http-types' -import {TTypedHandler, TTypedMiddleware} from './TTypedHandler' +import { TypedHandler, TypedMiddleware } from './TypedHandler' -export class AsyncRouter { +export class AsyncRouter { readonly router: express.Router readonly use: express.IRouterHandler & express.IRouterMatcher @@ -11,12 +11,12 @@ export class AsyncRouter { this.use = this.router.use.bind(this.router) as any } - protected addRoute( + protected addRoute( method: M, path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { const addRoute = this.router[method].bind(this.router as any) @@ -31,8 +31,8 @@ export class AsyncRouter { } - protected wrapHandler( - handler: TTypedHandler, + protected wrapHandler( + handler: TypedHandler, ): express.RequestHandler { return (req, res, next) => { handler(req, res, next) @@ -45,9 +45,9 @@ export class AsyncRouter { get

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ): void { this.addRoute('get', path, ...handlers) @@ -55,9 +55,9 @@ export class AsyncRouter { post

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('post', path, ...handlers) @@ -65,9 +65,9 @@ export class AsyncRouter { put

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('put', path, ...handlers) @@ -75,9 +75,9 @@ export class AsyncRouter { delete

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('delete', path, ...handlers) @@ -85,9 +85,9 @@ export class AsyncRouter { head

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('head', path, ...handlers) @@ -95,9 +95,9 @@ export class AsyncRouter { options

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('options', path, ...handlers) @@ -105,9 +105,9 @@ export class AsyncRouter { patch

( path: P, - ...handlers: [TTypedHandler] | [ - Array>, - TTypedHandler, + ...handlers: [TypedHandler] | [ + Array>, + TypedHandler, ] ) { this.addRoute('patch', path, ...handlers) diff --git a/packages/server/src/router/ITypedRequest.ts b/packages/server/src/router/ITypedRequest.ts deleted file mode 100644 index d6ba841..0000000 --- a/packages/server/src/router/ITypedRequest.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from 'express' -import {IRoute} from '@rondo.dev/http-types' - -export interface ITypedRequest extends express.Request { - body: T['body'] - params: T['params'] - query: T['query'] -} diff --git a/packages/server/src/router/TTypedHandler.ts b/packages/server/src/router/TTypedHandler.ts deleted file mode 100644 index e3a77a8..0000000 --- a/packages/server/src/router/TTypedHandler.ts +++ /dev/null @@ -1,23 +0,0 @@ -import express from 'express' -import {IRoutes, TMethod} from '@rondo.dev/http-types' -import {ITypedRequest} from './ITypedRequest' - -export type TTypedMiddleware< - R extends IRoutes, - P extends keyof R, - M extends TMethod -> = ( - req: ITypedRequest, - res: express.Response, - next: express.NextFunction, -) => void - -export type TTypedHandler< - R extends IRoutes, - P extends keyof R, - M extends TMethod -> = ( - req: ITypedRequest, - res: express.Response, - next: express.NextFunction, -) => Promise diff --git a/packages/server/src/router/TransactionalRouter.ts b/packages/server/src/router/TransactionalRouter.ts index 6d8d4f0..281dcde 100644 --- a/packages/server/src/router/TransactionalRouter.ts +++ b/packages/server/src/router/TransactionalRouter.ts @@ -1,16 +1,16 @@ +import { Method, Routes } from '@rondo.dev/http-types' import express from 'express' -import {AsyncRouter} from './AsyncRouter' -import {IRoutes, TMethod} from '@rondo.dev/http-types' -import {ITransactionManager} from '../database/ITransactionManager' -import {TTypedHandler} from './TTypedHandler' +import { TransactionManager } from '../database/TransactionManager' +import { AsyncRouter } from './AsyncRouter' +import { TypedHandler } from './TypedHandler' -export class TransactionalRouter extends AsyncRouter { - constructor(readonly transactionManager: ITransactionManager) { +export class TransactionalRouter extends AsyncRouter { + constructor(readonly transactionManager: TransactionManager) { super() } - protected wrapHandler( - handler: TTypedHandler, + protected wrapHandler( + handler: TypedHandler, ): express.RequestHandler { return async (req, res, next) => { await this.transactionManager diff --git a/packages/server/src/router/TypedHandler.ts b/packages/server/src/router/TypedHandler.ts new file mode 100644 index 0000000..d9befa7 --- /dev/null +++ b/packages/server/src/router/TypedHandler.ts @@ -0,0 +1,23 @@ +import { Method, Routes } from '@rondo.dev/http-types' +import express from 'express' +import { TypedRequest } from './TypedRequest' + +export type TypedMiddleware< + R extends Routes, + P extends keyof R, + M extends Method +> = ( + req: TypedRequest, + res: express.Response, + next: express.NextFunction, +) => void + +export type TypedHandler< + R extends Routes, + P extends keyof R, + M extends Method +> = ( + req: TypedRequest, + res: express.Response, + next: express.NextFunction, +) => Promise diff --git a/packages/server/src/router/TypedRequest.ts b/packages/server/src/router/TypedRequest.ts new file mode 100644 index 0000000..fc3ddeb --- /dev/null +++ b/packages/server/src/router/TypedRequest.ts @@ -0,0 +1,8 @@ +import { Route } from '@rondo.dev/http-types' +import express from 'express' + +export interface TypedRequest extends express.Request { + body: T['body'] + params: T['params'] + query: T['query'] +} diff --git a/packages/server/src/router/index.ts b/packages/server/src/router/index.ts index ed89c82..24b2cbe 100644 --- a/packages/server/src/router/index.ts +++ b/packages/server/src/router/index.ts @@ -1,4 +1,4 @@ export * from './AsyncRouter' -export * from './TTypedHandler' -export * from './ITypedRequest' export * from './TransactionalRouter' +export * from './TypedHandler' +export * from './TypedRequest' diff --git a/packages/server/src/routes/AuthRoutes.ts b/packages/server/src/routes/AuthRoutes.ts deleted file mode 100644 index 9d72a34..0000000 --- a/packages/server/src/routes/AuthRoutes.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {AsyncRouter} from '../router' -import {BaseRoute} from './BaseRoute' -import {IAPIDef} from '@rondo.dev/common' -import {IAuthService} from '../services' -import {Authenticator} from '../middleware' -import {ensureLoggedInApi} from '../middleware' - -export class AuthRoutes extends BaseRoute { - constructor( - protected readonly authService: IAuthService, - protected readonly authenticator: Authenticator, - protected readonly t: AsyncRouter, - ) { - super(t) - } - - setup(t: AsyncRouter) { - t.post('/auth/register', async (req, res) => { - const user = await this.authService.createUser({ - username: req.body.username, - password: req.body.password, - firstName: req.body.firstName, - lastName: req.body.lastName, - }) - await req.logInPromise(user) - return user - }) - - t.post('/auth/login', async (req, res, next) => { - const user = await this.authenticator - .authenticate('local')(req, res, next) - - if (!user) { - res.status(401) - return - } - await req.logInPromise(user) - return user - }) - - t.post('/auth/password', [ensureLoggedInApi], async req => { - await this.authService.changePassword({ - userId: req.user!.id, - oldPassword: req.body.oldPassword, - newPassword: req.body.newPassword, - }) - }) - - t.get('/auth/logout', async (req, res) => { - req.logout() - }) - } -} diff --git a/packages/server/src/routes/BaseRoute.ts b/packages/server/src/routes/BaseRoute.ts deleted file mode 100644 index fdabe1d..0000000 --- a/packages/server/src/routes/BaseRoute.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {THandler} from '../middleware/THandler' -import {AsyncRouter} from '../router' -import {IRoutes} from '@rondo.dev/http-types' - -export abstract class BaseRoute { - readonly handle: THandler - - constructor(protected readonly t: AsyncRouter) { - this.handle = t.router - this.setup(t) - } - - protected abstract setup(t: AsyncRouter): void -} diff --git a/packages/server/src/routes/AuthRoutes.test.ts b/packages/server/src/routes/configureAuthRoutes.test.ts similarity index 100% rename from packages/server/src/routes/AuthRoutes.test.ts rename to packages/server/src/routes/configureAuthRoutes.test.ts diff --git a/packages/server/src/routes/configureAuthRoutes.ts b/packages/server/src/routes/configureAuthRoutes.ts new file mode 100644 index 0000000..bad2fbb --- /dev/null +++ b/packages/server/src/routes/configureAuthRoutes.ts @@ -0,0 +1,46 @@ +import { APIDef, AuthService } from '@rondo.dev/common' +import { Authenticator, ensureLoggedInApi } from '../middleware' +import { AsyncRouter } from '../router' + +export function configureAuthRoutes( + authService: AuthService, + authenticator: Authenticator, + t: AsyncRouter, +) { + t.post('/auth/register', async (req, res) => { + const user = await authService.createUser({ + username: req.body.username, + password: req.body.password, + firstName: req.body.firstName, + lastName: req.body.lastName, + }) + await req.logInPromise(user) + return user + }) + + t.post('/auth/login', async (req, res, next) => { + const user = await authenticator + .authenticate('local')(req, res, next) + + if (!user) { + res.status(401) + return + } + await req.logInPromise(user) + return user + }) + + t.post('/auth/password', [ensureLoggedInApi], async req => { + await authService.changePassword({ + userId: req.user!.id, + oldPassword: req.body.oldPassword, + newPassword: req.body.newPassword, + }) + }) + + t.get('/auth/logout', async (req, res) => { + req.logout() + }) + + return t.router +} diff --git a/packages/server/src/routes/index.ts b/packages/server/src/routes/index.ts index 829d0d1..2fa6f16 100644 --- a/packages/server/src/routes/index.ts +++ b/packages/server/src/routes/index.ts @@ -1,3 +1,2 @@ export * from './application' -export * from './AuthRoutes' -export * from './BaseRoute' +export * from './configureAuthRoutes' diff --git a/packages/server/src/rpc/RPC.ts b/packages/server/src/rpc/RPC.ts index 075a21c..57d32fb 100644 --- a/packages/server/src/rpc/RPC.ts +++ b/packages/server/src/rpc/RPC.ts @@ -1,10 +1,10 @@ -import { IContext } from '@rondo.dev/common' +import { Context } from '@rondo.dev/common' import { WithContext, ensure } from '@rondo.dev/jsonrpc' -export { IContext } -export type RPC = WithContext +export { Context } +export type RPC = WithContext -export const ensureLoggedIn = ensure( +export const ensureLoggedIn = ensure( c => !!c.user && !!c.user.id, 'You must be logged in to perform this action', ) diff --git a/packages/server/src/rpc/TeamService.test.ts b/packages/server/src/rpc/SQLTeamService.test.ts similarity index 97% rename from packages/server/src/rpc/TeamService.test.ts rename to packages/server/src/rpc/SQLTeamService.test.ts index 636bdec..2a62f94 100644 --- a/packages/server/src/rpc/TeamService.test.ts +++ b/packages/server/src/rpc/SQLTeamService.test.ts @@ -1,4 +1,4 @@ -import { TeamServiceMethods, ITeamService } from '@rondo.dev/common' +import { TeamServiceMethods, TeamService } from '@rondo.dev/common' import { test } from '../test' describe('team', () => { @@ -15,7 +15,7 @@ describe('team', () => { }) const getClient = () => - test.rpc( + test.rpc( '/rpc/teamService', TeamServiceMethods, headers, diff --git a/packages/server/src/rpc/TeamService.ts b/packages/server/src/rpc/SQLTeamService.ts similarity index 73% rename from packages/server/src/rpc/TeamService.ts rename to packages/server/src/rpc/SQLTeamService.ts index af9e80f..2edfe2e 100644 --- a/packages/server/src/rpc/TeamService.ts +++ b/packages/server/src/rpc/SQLTeamService.ts @@ -1,20 +1,19 @@ -import { ITeamAddUserParams, ITeamCreateParams, ITeamRemoveParams, ITeamService, ITeamUpdateParams, trim } from '@rondo.dev/common' -import { IUserInTeam } from '@rondo.dev/common/lib/team/IUserInTeam' +import { TeamAddUserParams, TeamCreateParams, TeamRemoveParams, TeamService, TeamUpdateParams, trim, UserPermissions } from '@rondo.dev/common' +import { UserInTeam } from '@rondo.dev/common/lib/team/UserInTeam' import Validator from '@rondo.dev/validator' -import { IDatabase } from '../database/IDatabase' +import { Database } from '../database/Database' import { Team } from '../entities/Team' import { UserTeam } from '../entities/UserTeam' -import { IUserPermissions } from '../services/IUserPermissions' -import { ensureLoggedIn, IContext, RPC } from './RPC' +import { ensureLoggedIn, Context, RPC } from './RPC' @ensureLoggedIn -export class TeamService implements RPC { +export class SQLTeamService implements RPC { constructor( - protected readonly db: IDatabase, - protected readonly permissions: IUserPermissions, + protected readonly db: Database, + protected readonly permissions: UserPermissions, ) {} - async create(context: IContext, params: ITeamCreateParams) { + async create(context: Context, params: TeamCreateParams) { const userId = context.user!.id const name = trim(params.name) @@ -38,7 +37,7 @@ export class TeamService implements RPC { return (await this.findOne(context, team.id))! } - async remove(context: IContext, {id}: ITeamRemoveParams) { + async remove(context: Context, {id}: TeamRemoveParams) { const userId = context.user!.id await this.permissions.belongsToTeam({ @@ -55,7 +54,7 @@ export class TeamService implements RPC { return {id} } - async update(context: IContext, {id, name}: ITeamUpdateParams) { + async update(context: Context, {id, name}: TeamUpdateParams) { const userId = context.user!.id await this.permissions.belongsToTeam({ @@ -73,7 +72,7 @@ export class TeamService implements RPC { return (await this.findOne(context, id))! } - async addUser(context: IContext, params: ITeamAddUserParams) { + async addUser(context: Context, params: TeamAddUserParams) { const {userId, teamId, roleId} = params await this.db.getRepository(UserTeam) .save({userId, teamId, roleId}) @@ -89,7 +88,7 @@ export class TeamService implements RPC { return this._mapUserInTeam(userTeam!) } - async removeUser(context: IContext, params: ITeamAddUserParams) { + async removeUser(context: Context, params: TeamAddUserParams) { const {teamId, userId, roleId} = params await this.permissions.belongsToTeam({ @@ -104,11 +103,11 @@ export class TeamService implements RPC { return {teamId, userId, roleId} } - async findOne(context: IContext, id: number) { + async findOne(context: Context, id: number) { return this.db.getRepository(Team).findOne(id) } - async find(context: IContext) { + async find(context: Context) { const userId = context.user!.id return this.db.getRepository(Team) @@ -119,7 +118,7 @@ export class TeamService implements RPC { .getMany() } - async findUsers(context: IContext, teamId: number) { + async findUsers(context: Context, teamId: number) { const userId = context.user!.id await this.permissions.belongsToTeam({ @@ -139,7 +138,7 @@ export class TeamService implements RPC { } } - protected _mapUserInTeam(ut: UserTeam): IUserInTeam { + protected _mapUserInTeam(ut: UserTeam): UserInTeam { return { teamId: ut.teamId, userId: ut.userId, diff --git a/packages/server/src/rpc/UserService.test.ts b/packages/server/src/rpc/SQLUserService.test.ts similarity index 87% rename from packages/server/src/rpc/UserService.test.ts rename to packages/server/src/rpc/SQLUserService.test.ts index ac18b44..00ab0b8 100644 --- a/packages/server/src/rpc/UserService.test.ts +++ b/packages/server/src/rpc/SQLUserService.test.ts @@ -1,7 +1,7 @@ import {test} from '../test' -import { IUserService, UserServiceMethods } from '@rondo.dev/common' +import { UserService, UserServiceMethods } from '@rondo.dev/common' -describe('user', () => { +describe('SQLUserService', () => { test.withDatabase() @@ -12,7 +12,7 @@ describe('user', () => { }) const createService = () => { - return test.rpc( + return test.rpc( '/rpc/userService', UserServiceMethods, headers, diff --git a/packages/server/src/rpc/UserService.ts b/packages/server/src/rpc/SQLUserService.ts similarity index 67% rename from packages/server/src/rpc/UserService.ts rename to packages/server/src/rpc/SQLUserService.ts index 970374d..862c43f 100644 --- a/packages/server/src/rpc/UserService.ts +++ b/packages/server/src/rpc/SQLUserService.ts @@ -1,19 +1,18 @@ -import { IUserService } from '@rondo.dev/common' -import { compare, hash } from 'bcrypt' -import createError from 'http-errors' -import { IDatabase } from '../database/IDatabase' +import { UserService } from '@rondo.dev/common' +import { hash } from 'bcrypt' +import { Database } from '../database/Database' import { User } from '../entities/User' import { UserEmail } from '../entities/UserEmail' -import { ensureLoggedIn, IContext, RPC } from './RPC' +import { Context, ensureLoggedIn, RPC } from './RPC' const SALT_ROUNDS = 10 -const MIN_PASSWORD_LENGTH = 10 +// const MIN_PASSWORD_LENGTH = 10 @ensureLoggedIn -export class UserService implements RPC { - constructor(protected readonly db: IDatabase) {} +export class SQLUserService implements RPC { + constructor(protected readonly db: Database) {} - async getProfile(context: IContext) { + async getProfile(context: Context) { const userId = context.user!.id // current user should always exist in the database @@ -29,7 +28,7 @@ export class UserService implements RPC { } } - async findUserByEmail(context: IContext, email: string) { + async findUserByEmail(context: Context, email: string) { const userEmail = await this.db.getRepository(UserEmail) .findOne({ email }, { relations: ['user'], diff --git a/packages/server/src/rpc/index.ts b/packages/server/src/rpc/index.ts index fff2e94..c4d9078 100644 --- a/packages/server/src/rpc/index.ts +++ b/packages/server/src/rpc/index.ts @@ -1,3 +1,3 @@ export * from './RPC' -export * from './TeamService' -export * from './UserService' +export * from './SQLTeamService' +export * from './SQLUserService' diff --git a/packages/server/src/services/IAuthService.ts b/packages/server/src/services/IAuthService.ts deleted file mode 100644 index f92c02e..0000000 --- a/packages/server/src/services/IAuthService.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ICredentials, INewUser, IUser} from '@rondo.dev/common' - -export interface IAuthService { - createUser(credentials: INewUser): Promise - changePassword(params: { - userId: number, - oldPassword: string, - newPassword: string, - }): Promise - validateCredentials(credentials: ICredentials): Promise - findOne(id: number): Promise - findUserByEmail(email: string): Promise -} diff --git a/packages/server/src/services/IUserPermissions.ts b/packages/server/src/services/IUserPermissions.ts deleted file mode 100644 index ad8c31d..0000000 --- a/packages/server/src/services/IUserPermissions.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IUserPermissions { - // TODO check for role too - belongsToTeam(params: {userId: number, teamId: number}): Promise -} diff --git a/packages/server/src/services/AuthService.test.ts b/packages/server/src/services/SQLAuthService.test.ts similarity index 93% rename from packages/server/src/services/AuthService.test.ts rename to packages/server/src/services/SQLAuthService.test.ts index a964eb6..88cd2b6 100644 --- a/packages/server/src/services/AuthService.test.ts +++ b/packages/server/src/services/SQLAuthService.test.ts @@ -1,14 +1,14 @@ -import {test} from '../test' -import {AuthService} from './AuthService' +import { test } from '../test' +import { SQLAuthService } from './SQLAuthService' -describe('AuthService', () => { +describe('SQLAuthService', () => { test.withDatabase() const username = test.username const password = '1234567890' - const authService = new AuthService(test.bootstrap.database) + const authService = new SQLAuthService(test.bootstrap.database) async function createUser(u = username, p = password) { return authService.createUser({ diff --git a/packages/server/src/services/AuthService.ts b/packages/server/src/services/SQLAuthService.ts similarity index 87% rename from packages/server/src/services/AuthService.ts rename to packages/server/src/services/SQLAuthService.ts index 95bc2cf..e78f94f 100644 --- a/packages/server/src/services/AuthService.ts +++ b/packages/server/src/services/SQLAuthService.ts @@ -1,20 +1,19 @@ -import { ICredentials, INewUser, IUser, trim } from '@rondo.dev/common' +import { AuthService, Credentials, NewUser, UserProfile, trim } from '@rondo.dev/common' import Validator from '@rondo.dev/validator' import { compare, hash } from 'bcrypt' import { validate as validateEmail } from 'email-validator' import createError from 'http-errors' -import { IDatabase } from '../database/IDatabase' +import { Database } from '../database/Database' import { User } from '../entities/User' import { UserEmail } from '../entities/UserEmail' -import { IAuthService } from './IAuthService' const SALT_ROUNDS = 10 const MIN_PASSWORD_LENGTH = 10 -export class AuthService implements IAuthService { - constructor(protected readonly db: IDatabase) {} +export class SQLAuthService implements AuthService { + constructor(protected readonly db: Database) {} - async createUser(payload: INewUser): Promise { + async createUser(payload: NewUser): Promise { const newUser = { username: trim(payload.username), firstName: trim(payload.firstName), @@ -88,9 +87,9 @@ export class AuthService implements IAuthService { } async changePassword(params: { - userId: number, - oldPassword: string, - newPassword: string, + userId: number + oldPassword: string + newPassword: string }) { const {userId, oldPassword, newPassword} = params const userRepository = this.db.getRepository(User) @@ -109,7 +108,7 @@ export class AuthService implements IAuthService { .update(userId, { password }) } - async validateCredentials(credentials: ICredentials) { + async validateCredentials(credentials: Credentials) { const {username, password} = credentials const user = await this.db.getRepository(User) .createQueryBuilder('user') diff --git a/packages/server/src/services/UserPermissions.ts b/packages/server/src/services/SQLUserPermissions.ts similarity index 59% rename from packages/server/src/services/UserPermissions.ts rename to packages/server/src/services/SQLUserPermissions.ts index 89d9b0e..203f9f3 100644 --- a/packages/server/src/services/UserPermissions.ts +++ b/packages/server/src/services/SQLUserPermissions.ts @@ -1,10 +1,10 @@ +import { UserPermissions } from '@rondo.dev/common' import createError from 'http-errors' -import {IDatabase} from '../database/IDatabase' -import {UserTeam} from '../entities/UserTeam' -import {IUserPermissions} from './IUserPermissions' +import { Database } from '../database/Database' +import { UserTeam } from '../entities/UserTeam' -export class UserPermissions implements IUserPermissions { - constructor(protected readonly db: IDatabase) {} +export class SQLUserPermissions implements UserPermissions { + constructor(protected readonly db: Database) {} async belongsToTeam(params: {userId: number, teamId: number}) { const {userId, teamId} = params diff --git a/packages/server/src/services/index.ts b/packages/server/src/services/index.ts index cc06f13..45457be 100644 --- a/packages/server/src/services/index.ts +++ b/packages/server/src/services/index.ts @@ -1,4 +1,2 @@ -export * from './IAuthService' -export * from './AuthService' -export * from './IUserPermissions' -export * from './UserPermissions' +export * from './SQLAuthService' +export * from './SQLUserPermissions' diff --git a/packages/server/src/session/ISession.ts b/packages/server/src/session/DefaultSession.ts similarity index 70% rename from packages/server/src/session/ISession.ts rename to packages/server/src/session/DefaultSession.ts index 56a53fb..08d43c4 100644 --- a/packages/server/src/session/ISession.ts +++ b/packages/server/src/session/DefaultSession.ts @@ -1,4 +1,4 @@ -export interface ISession { +export interface DefaultSession { // TODO use timestamp field expiredAt: number id: string diff --git a/packages/server/src/session/SessionStore.test.ts b/packages/server/src/session/SessionStore.test.ts index a8fc691..07cf880 100644 --- a/packages/server/src/session/SessionStore.test.ts +++ b/packages/server/src/session/SessionStore.test.ts @@ -1,16 +1,13 @@ -import express, {Application} from 'express' -import request from 'supertest' -import {SessionStore} from './SessionStore' -import {ISession} from './ISession' -import ExpressSession from 'express-session' import loggerFactory from '@rondo.dev/logger' -import { - createConnection, Column, Connection, Entity, Index, PrimaryColumn, - Repository, -} from 'typeorm' +import express, { Application } from 'express' +import ExpressSession from 'express-session' +import request from 'supertest' +import { Column, Connection, createConnection, Entity, Index, PrimaryColumn, Repository } from 'typeorm' +import { DefaultSession } from './DefaultSession' +import { SessionStore } from './SessionStore' @Entity() -class Session implements ISession { +class SessionEntity implements DefaultSession { @PrimaryColumn() id!: string @@ -28,15 +25,15 @@ class Session implements ISession { describe('SessionStore', () => { let connection!: Connection - let repository!: Repository + let repository!: Repository beforeEach(async () => { connection = await createConnection({ type: 'sqlite', database: ':memory:', - entities: [Session], + entities: [SessionEntity], synchronize: true, }) - repository = connection.getRepository(Session) + repository = connection.getRepository(SessionEntity) }) afterEach(() => connection!.close()) @@ -88,7 +85,7 @@ describe('SessionStore', () => { .expect(200) } - function getSession(app: Application, cookie: string = '') { + function getSession(app: Application, cookie = '') { return request(app) .get('/session') .set('cookie', cookie) diff --git a/packages/server/src/session/SessionStore.ts b/packages/server/src/session/SessionStore.ts index e0452b1..2303cd1 100644 --- a/packages/server/src/session/SessionStore.ts +++ b/packages/server/src/session/SessionStore.ts @@ -1,31 +1,31 @@ -import {Store} from 'express-session' -import {ISession} from './ISession' -import {Repository, LessThan} from 'typeorm' -import {debounce} from '@rondo.dev/tasq' -import { ILogger } from '@rondo.dev/logger' +import { Logger } from '@rondo.dev/logger' +import { debounce } from '@rondo.dev/tasq' +import { Store } from 'express-session' +import { LessThan, Repository } from 'typeorm' +import { DefaultSession } from './DefaultSession' type SessionData = Express.SessionData type Callback = (err?: any, session?: SessionData) => void type CallbackErr = (err?: any) => void -export interface ISessionStoreOptions { +export interface SessionStoreOptions { readonly ttl: number readonly cleanupDelay: number - readonly getRepository: TRepositoryFactory - readonly logger: ILogger, - buildSession(sessionData: SessionData, session: ISession): S + readonly getRepository: RepositoryFactory + readonly logger: Logger + buildSession(sessionData: SessionData, session: DefaultSession): S } -export type TRepositoryFactory = () => Repository +export type RepositoryFactory = () => Repository // TODO casting as any because TypeScript complains. Looks like this is a // bug in TypeScript 3.2.2 // // https://github.com/typeorm/typeorm/issues/1544 // https://github.com/Microsoft/TypeScript/issues/21592 -export class SessionStore extends Store { +export class SessionStore extends Store { - protected readonly getRepository: TRepositoryFactory + protected readonly getRepository: RepositoryFactory readonly cleanup = debounce(async () => { try { @@ -43,7 +43,7 @@ export class SessionStore extends Store { }, 1000) constructor( - protected readonly options: ISessionStoreOptions, + protected readonly options: SessionStoreOptions, ) { super() this.getRepository = options.getRepository diff --git a/packages/server/src/session/index.ts b/packages/server/src/session/index.ts index 13dd9d1..709cca6 100644 --- a/packages/server/src/session/index.ts +++ b/packages/server/src/session/index.ts @@ -1,2 +1,2 @@ -export * from './ISession' +export * from './DefaultSession' export * from './SessionStore' diff --git a/packages/server/src/test-utils/NamespaceMock.ts b/packages/server/src/test-utils/NamespaceMock.ts index 17e84db..39eda0e 100644 --- a/packages/server/src/test-utils/NamespaceMock.ts +++ b/packages/server/src/test-utils/NamespaceMock.ts @@ -1,4 +1,4 @@ -import {Namespace} from 'cls-hooked' +import { Namespace } from 'cls-hooked' export class NamespaceMock implements Namespace { readonly context: {[key: string]: any} = {} diff --git a/packages/server/src/test-utils/RequestTester.test.ts b/packages/server/src/test-utils/RequestTester.test.ts index 8324fdb..2f7416a 100644 --- a/packages/server/src/test-utils/RequestTester.test.ts +++ b/packages/server/src/test-utils/RequestTester.test.ts @@ -3,11 +3,11 @@ import {RequestTester} from './RequestTester' describe('RequestTest', () => { - interface IAPI { + interface API { '/test': { 'get': { - response: {id: number}, - }, + response: {id: number} + } } } @@ -18,14 +18,14 @@ describe('RequestTest', () => { describe('constructor', () => { it('creates a blank baseUrl', () => { - const t = new RequestTester(app) + const t = new RequestTester(app) expect(t.baseUrl).toEqual('') }) }) describe('RequestTester.request', () => { it('creates a response', async () => { - const t = new RequestTester(app, '/api') + const t = new RequestTester(app, '/api') const result = await t.request('get', '/test') expect(result.body.id).toBe(1) }) diff --git a/packages/server/src/test-utils/RequestTester.ts b/packages/server/src/test-utils/RequestTester.ts index e9a12d4..5fd5205 100644 --- a/packages/server/src/test-utils/RequestTester.ts +++ b/packages/server/src/test-utils/RequestTester.ts @@ -1,29 +1,32 @@ -import { URLFormatter } from '@rondo.dev/http-client' -import { IRoutes, TMethod } from '@rondo.dev/http-types' +/* eslint @typescript-eslint/no-explicit-any: 0 */ +import { Headers, URLFormatter } from '@rondo.dev/http-client' +import { Routes, Method } from '@rondo.dev/http-types' import supertest from 'supertest' // https://stackoverflow.com/questions/48215950/exclude-property-from-type type Omit = Pick> -interface ITest extends Omit {} +interface Test extends Omit {} -interface IResponse< - R extends IRoutes, +interface Response< + R extends Routes, P extends keyof R, - M extends TMethod, + M extends Method, > extends supertest.Response { body: R[P][M]['response'] header: {[key: string]: string} } -interface IRequest< - R extends IRoutes, +interface Request< + R extends Routes, P extends keyof R, - M extends TMethod, -> extends ITest, Promise> { + M extends Method, +> extends Test, Promise> { send(value: R[P][M]['body'] | string): this expect(status: number, body?: any): this - expect(body: string | RegExp | object | ((res: Response) => any)): this + expect( + body: string | RegExp | object | ((res: Response, + ) => any)): this expect(field: string, val: string | RegExp): this // any other method that's called will return "this" from supertest's // or superagent's type definition and afterwards the promise will no longer @@ -31,22 +34,18 @@ interface IRequest< // type definition } -interface IRequestOptions< - R extends IRoutes, +interface RequestOptions< + R extends Routes, P extends keyof R, - M extends TMethod, + M extends Method, > { - params?: R[P][M]['params'], - query?: R[P][M]['query'], + params?: R[P][M]['params'] + query?: R[P][M]['query'] } -export interface IHeaders { - [key: string]: string -} +export class RequestTester { -export class RequestTester { - - protected headers: IHeaders = {} + protected headers: Headers = {} protected formatter: URLFormatter = new URLFormatter() constructor( @@ -54,15 +53,14 @@ export class RequestTester { readonly baseUrl = '', ) {} - setHeaders(headers: IHeaders): this { + setHeaders(headers: Headers): this { this.headers = headers return this } - request( - method: M, path: P, options: IRequestOptions = {}, - ) - : IRequest { + request( + method: M, path: P, options: RequestOptions = {}, + ): Request { const url = this.formatter.format(path, options.params, options.query) const test = supertest(this.app)[method](`${this.baseUrl}${url}`) Object.keys(this.headers).forEach(key => { @@ -73,28 +71,28 @@ export class RequestTester { get

( path: P, - options?: IRequestOptions, + options?: RequestOptions, ) { return this.request('get', path, options) } post

( path: P, - options?: IRequestOptions, + options?: RequestOptions, ) { return this.request('post', path, options) } put

( path: P, - options?: IRequestOptions, + options?: RequestOptions, ) { return this.request('put', path, options) } delete

( path: P, - options?: IRequestOptions, + options?: RequestOptions, ) { return this.request('delete', path, options) } diff --git a/packages/server/src/test-utils/TestUtils.ts b/packages/server/src/test-utils/TestUtils.ts index a1786d1..08dcb79 100644 --- a/packages/server/src/test-utils/TestUtils.ts +++ b/packages/server/src/test-utils/TestUtils.ts @@ -1,29 +1,26 @@ -import express from 'express' -import supertest from 'supertest' -import {Connection, QueryRunner} from 'typeorm' -import { - ENTITY_MANAGER, ITransactionManager, TRANSACTION_ID, -} from '../database/ITransactionManager' -import {IRoutes} from '@rondo.dev/http-types' -import {IBootstrap} from '../application/IBootstrap' -import {RequestTester} from './RequestTester' -import {Role} from '../entities/Role' -import {CORRELATION_ID} from '../middleware' -import shortid from 'shortid' -import { AddressInfo } from 'net' +/* eslint @typescript-eslint/no-explicit-any: 0 */ +import { Routes } from '@rondo.dev/http-types' import { createRemoteClient, FunctionPropertyNames, RPCClient } from '@rondo.dev/jsonrpc' -import {Server} from 'http' -import { IAppServer } from '../application/IAppServer' +import { Server } from 'http' +import { AddressInfo } from 'net' +import shortid from 'shortid' +import supertest from 'supertest' +import { Connection, QueryRunner } from 'typeorm' +import { AppServer } from '../application/AppServer' +import { Bootstrap } from '../application/Bootstrap' +import { ENTITY_MANAGER, TransactionManager, TRANSACTION_ID } from '../database/TransactionManager' +import { Role } from '../entities/Role' +import { RequestTester } from './RequestTester' -export class TestUtils { +export class TestUtils { readonly username = this.createTestUsername() readonly password = 'Password10' - readonly app: IAppServer + readonly app: AppServer readonly context: string - readonly transactionManager: ITransactionManager + readonly transactionManager: TransactionManager - constructor(readonly bootstrap: IBootstrap) { + constructor(readonly bootstrap: Bootstrap) { this.app = bootstrap.application.server this.context = this.bootstrap.getConfig().app.context this.transactionManager = this.bootstrap.database.transactionManager @@ -82,7 +79,7 @@ export class TestUtils { .save({name}) } - async getError(promise: Promise): Promise { + async getError(promise: Promise): Promise { let error!: Error try { await promise diff --git a/packages/server/src/test-utils/index.ts b/packages/server/src/test-utils/index.ts index 838d955..356ee80 100644 --- a/packages/server/src/test-utils/index.ts +++ b/packages/server/src/test-utils/index.ts @@ -1,3 +1,3 @@ -export * from './TestUtils' -export * from './RequestTester' export * from './NamespaceMock' +export * from './RequestTester' +export * from './TestUtils' diff --git a/packages/server/src/test.ts b/packages/server/src/test.ts index c1685f7..9085ad4 100644 --- a/packages/server/src/test.ts +++ b/packages/server/src/test.ts @@ -1,13 +1,13 @@ -import {Bootstrap} from './application/Bootstrap' -import {configureServer} from './application/configureServer' -import {IAPIDef} from '@rondo.dev/common' -import {TestUtils} from './test-utils' -import {config} from './config' -import {createNamespace} from 'cls-hooked' +import { APIDef } from '@rondo.dev/common' +import { createNamespace } from 'cls-hooked' +import { CLIBootstrap } from './application' +import { configureServer } from './application/configureServer' +import { config } from './config' +import { TestUtils } from './test-utils' export const exit = jest.fn() -export const bootstrap = new Bootstrap({ +export const bootstrap = new CLIBootstrap({ config, configureServer, namespace: createNamespace('test'), @@ -15,5 +15,5 @@ export const bootstrap = new Bootstrap({ }) // TODO separate IAPIDef between projects -export const test = new TestUtils(bootstrap) +export const test = new TestUtils(bootstrap) export const {request} = test