Skip to content

Commit

Permalink
feat(core): add full support for events using emittery
Browse files Browse the repository at this point in the history
Install emittery, integrate emittery into the tensei core
  • Loading branch information
Frantz Kati committed Dec 17, 2020
1 parent e97096c commit 8db1d43
Show file tree
Hide file tree
Showing 23 changed files with 373 additions and 33 deletions.
21 changes: 13 additions & 8 deletions examples/blog/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
require('dotenv').config()
const { auth } = require('@tensei/auth')
const { rest } = require('@tensei/rest')
const { media } = require('@tensei/media')
const { graphql } = require('@tensei/graphql')
const { tensei, route } = require('@tensei/core')
const { ses, smtp } = require('@tensei/mail')
const { graphql } = require('@tensei/graphql')
const { auth, USER_EVENTS } = require('@tensei/auth')
const { tensei, route, event } = require('@tensei/core')

const Tag = require('./resources/Tag')
const Post = require('./resources/Post')
Expand All @@ -14,15 +14,12 @@ const Comment = require('./resources/Comment')
const Reaction = require('./resources/Reaction')

module.exports = tensei()
.dashboardPath('tensei')
// .dashboardPath('tensei')
.resources([Tag, Post, User, Comment, Editor, Reaction])
.clientUrl('https://google.com')
.serverUrl('http://localhost:5000')
.defaultStorageDriver('local')
// .defaultStorageDriver('local')
.graphQlQueries([])
.register(({ mailer }) => {
mailer.use('ethereal')
})
.routes([
route('Get products')
.get()
Expand Down Expand Up @@ -93,3 +90,11 @@ module.exports = tensei()
user: process.env.DATABASE_USER || 'mikrotensei',
password: process.env.DATABASE_PASSWORD || '',
})
.events([
event(USER_EVENTS.REGISTERED).listen(async ({ payload }) => {
console.log(
'#################',
await ctx.orm.em.count('Customer', {})
)
}),
])
9 changes: 9 additions & 0 deletions packages/auth/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,12 @@ export const defaultProviderScopes = (
twitter: [],
linkedin: ['r_liteprofile', 'r_emailaddress']
}[provider])

export const USER_EVENTS = {
REGISTERED: 'user::registered',
LOGGED_IN: 'user::logged::in',
FORGOT_PASSWORD: 'user::forgot::password',
RESET_PASSWORD: 'user::reset::password',
VERIFIED_EMAIL: 'user::verified::email',
RESENT_VERIFICATION_EMAIL: 'user::reset::verification::email'
}
18 changes: 14 additions & 4 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import {
hasMany,
boolean,
select,
Resolvers,
graphQlQuery,
GraphQlMiddleware,
GraphQLPluginContext,
route,
GraphQlQueryContract,
Expand All @@ -33,6 +31,7 @@ import {
} from '@tensei/common'

import {
USER_EVENTS,
AuthData,
TokenTypes,
GrantConfig,
Expand Down Expand Up @@ -1787,7 +1786,7 @@ class Auth {
}

private register = async (ctx: ApiContext) => {
const { manager, body } = ctx
const { manager, body, emitter } = ctx

const validator = Utils.validator(
this.resources.user,
Expand Down Expand Up @@ -1848,10 +1847,16 @@ class Auth {
}
}

emitter.emit(USER_EVENTS.REGISTERED, user)

return this.getUserPayload(ctx, await this.generateRefreshToken(ctx))
}

private resendVerificationEmail = async ({ manager, user }: ApiContext) => {
private resendVerificationEmail = async ({
manager,
user,
emitter
}: ApiContext) => {
if (!user.email_verification_token) {
return false
}
Expand All @@ -1862,6 +1867,8 @@ class Auth {

await manager.persistAndFlush(user)

emitter.emit(USER_EVENTS.RESENT_VERIFICATION_EMAIL, user)

return true
}

Expand All @@ -1880,6 +1887,8 @@ class Auth {

await manager.persistAndFlush(user)

ctx.emitter.emit(USER_EVENTS.VERIFIED_EMAIL, user)

return user.toJSON()
}

Expand Down Expand Up @@ -2518,3 +2527,4 @@ class Auth {
}

export const auth = () => new Auth()
export { USER_EVENTS } from './config'
26 changes: 26 additions & 0 deletions packages/common/src/events/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
EventContract,
EventConfigContract,
DataPayload,
EventListener
} from '@tensei/common'

export class Event<Payload = DataPayload> implements EventContract<Payload> {
config: EventConfigContract<Payload> = {
name: '',
listeners: []
}

constructor(name: string) {
this.config.name = name
}

listen(listener: EventListener<Payload>) {
this.config.listeners = [...this.config.listeners, listener]

return this
}
}

export const event = <Payload = DataPayload>(name: string) =>
new Event<Payload>(name)
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export { valueMetric, ValueMetrics } from './metrics/Value'

export { Utils } from './utils'
export { route, Route } from './api/Route'
export { event, Event } from './events/event'
export { graphQlQuery, GraphQlQuery } from './api/GraphQlQuery'
1 change: 1 addition & 0 deletions packages/common/typings/common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ declare module '@tensei/common' {
export * from '@tensei/common/plugins'
export * from '@tensei/common/filters'
export * from '@tensei/common/dashboards'
export * from '@tensei/common/events'
}
6 changes: 6 additions & 0 deletions packages/common/typings/config.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
declare module '@tensei/common/config' {
import { Logger } from 'pino'
import Emittery from 'emittery'
import { Request, Response } from 'express'
import { EntityManager } from '@mikro-orm/core'
import { sanitizer, validator } from 'indicative'
import { EventContract } from '@tensei/common/events'
import { ResourceContract } from '@tensei/common/resources'
import { ExecutionParams } from 'subscriptions-transport-ws'
import { DashboardContract } from '@tensei/common/dashboards'
Expand Down Expand Up @@ -290,6 +292,10 @@ declare module '@tensei/common/config' {
databaseClient: any
schemas: any
name: string
events: {
[key: string]: EventContract<DataPayload>
}
emitter: Emittery
serverUrl: string
clientUrl: string
viewsPath: string
Expand Down
18 changes: 18 additions & 0 deletions packages/common/typings/events.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
declare module '@tensei/common/events' {
import { DataPayload } from '@tensei/common/config'
type EventListener<Payload> = (payload: Payload) => void | Promise<void>

export interface EventConfigContract<Payload> {
name: string
listeners: EventListener<Payload>[]
}

export interface EventContract<Payload> {
config: EventConfigContract<Payload>
listen(listener: EventListener<Payload>): this
}

export const event: <Payload = DataPayload>(
name: string
) => EventContract<Payload>
}
1 change: 1 addition & 0 deletions packages/common/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/// <reference path="filters.d.ts" />
/// <reference path="express.d.ts" />
/// <reference path="dashboards.d.ts" />
/// <reference path="events.d.ts" />
5 changes: 4 additions & 1 deletion packages/common/typings/plugins.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ declare module '@tensei/common/plugins' {
RouteContract,
GraphQlQueryContract,
TensieContext,
RouteConfig
RouteConfig,
EventContract,
DataPayload
} from '@tensei/common/config'

type PluginSetupFunction = (
Expand Down Expand Up @@ -60,6 +62,7 @@ declare module '@tensei/common/plugins' {
driver: ExtendMailCallback,
config: any
) => void
extendEvents: (events: EventContract<DataPayload>) => void
}

export type ExtendMailCallback = (
Expand Down
56 changes: 49 additions & 7 deletions packages/core/Tensei.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Path from 'path'
import pino from 'pino'
import Emittery from 'emittery'
import BodyParser from 'body-parser'
import CookieParser from 'cookie-parser'
import { createServer, Server } from 'http'
Expand All @@ -20,7 +21,9 @@ import {
DashboardContract,
StorageConstructor,
SupportedStorageDrivers,
ExtendMailCallback
ExtendMailCallback,
EventContract,
DataPayload
} from '@tensei/common'
import Database from './database'
import {
Expand All @@ -32,8 +35,6 @@ import {
PluginSetupFunction
} from '@tensei/core'

import { MailDriverContract } from '@tensei/mail'

import ClientController from './controllers/client.controller'

export class Tensei implements TenseiContract {
Expand Down Expand Up @@ -73,6 +74,8 @@ export class Tensei implements TenseiContract {
this.ctx = {
schemas: [],
routes: [],
events: {},
emitter: new Emittery(),
name: process.env.APP_NAME || 'Tensei',
graphQlQueries: [],
graphQlTypeDefs: [],
Expand Down Expand Up @@ -233,13 +236,34 @@ export class Tensei implements TenseiContract {
await this.callPluginHook('boot')
await this.ctx.rootBoot(this.getPluginArguments())

this.registerEmitteryListeners()

this.registerAsyncErrorHandler()

this.registeredApplication = true

return this
}

private registerEmitteryListeners() {
Object.keys(this.ctx.events).forEach(eventName => {
const event = this.ctx.events[eventName]

event.config.listeners.forEach(listener => {
this.ctx.emitter.on(eventName as any, listener)
})
})

const originalEmit = this.ctx.emitter.emit.bind(this.ctx.emitter)

this.ctx.emitter.emit = async (eventName: string, data: any) => {
return originalEmit(eventName, {
payload: data,
ctx: this.ctx
})
}
}

public async start(fn?: (ctx: Config) => any, listen = true) {
if (!this.registeredApplication) {
await this.bootApplication()
Expand Down Expand Up @@ -324,10 +348,11 @@ export class Tensei implements TenseiContract {
this.routes(routes)
},
currentCtx: () => this.ctx,
storageDriver: this.storageDriver,
getQuery: this.getQuery,
getRoute: this.getRoute,
extendMailer: this.extendMailer.bind(this)
storageDriver: this.storageDriver.bind(this),
getQuery: this.getQuery.bind(this),
getRoute: this.getRoute.bind(this),
extendMailer: this.extendMailer.bind(this),
extendEvents: this.events.bind(this)
}
}

Expand Down Expand Up @@ -440,6 +465,7 @@ export class Tensei implements TenseiContract {
request.mailer = this.ctx.mailer
request.config = this.ctx
request.storage = this.ctx.storage
request.emitter = this.ctx.emitter

next()
}
Expand Down Expand Up @@ -638,6 +664,22 @@ export class Tensei implements TenseiContract {

return this
}

public events(events: EventContract<DataPayload>[]) {
events.forEach(event => {
const eventExists = this.ctx.events[event.config.name]

if (eventExists) {
event.config.listeners.concat(eventExists.config.listeners)
}

this.ctx.events[event.config.name] = event
})

return this
}

private emit(name: string) {}
}

export const tensei = () => new Tensei()
Expand Down
1 change: 1 addition & 0 deletions packages/core/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare global {
mailer: Mail
storage: Config['storage']
currentCtx: () => Config
emitter: Config['emitter']
scripts: Asset[]
styles: Asset[]
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"change-case": "^4.1.1",
"cookie-parser": "^1.4.5",
"date-fns": "^2.14.0",
"emittery": "^0.7.2",
"express": "^4.17.1",
"express-async-handler": "^1.1.4",
"express-response-formatter": "^2.0.2",
Expand Down
6 changes: 5 additions & 1 deletion packages/core/typings/tensei.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ declare module '@tensei/core' {
DatabaseConfiguration,
RouteContract,
GraphQlQueryContract,
TensieContext
TensieContext,
EventContract,
DataPayload
} from '@tensei/common'

export interface TenseiContract {
Expand All @@ -23,6 +25,7 @@ declare module '@tensei/core' {
graphQlQueries(routes: GraphQlQueryContract[]): this
graphQlTypeDefs(defs: TensieContext['graphQlTypeDefs']): this
db(databaseConfig: DatabaseConfiguration): this
events(events: EventContract<DataPayload>[]): this
serverUrl(url: string): this
clientUrl(url: string): this
viewsPath(path: string): this
Expand All @@ -42,6 +45,7 @@ declare module '@tensei/core' {
graphQlQueries(routes: GraphQlQueryContract[]): this
graphQlTypeDefs(defs: TensieContext['graphQlTypeDefs']): this
db(databaseConfig: DatabaseConfiguration): this
events(events: EventContract<DataPayload>[]): this
serverUrl(url: string): this
clientUrl(url: string): this
viewsPath(path: string): this
Expand Down
Loading

0 comments on commit 8db1d43

Please sign in to comment.