@dialectlabs/monitor
Advanced tools
Comparing version 2.0.3-beta to 2.0.4-beta
@@ -11,6 +11,6 @@ import * as web3 from '@solana/web3.js'; | ||
Member, | ||
programs, | ||
sleep, | ||
Wallet_, | ||
} from '@dialectlabs/web3'; | ||
import { programs } from '@dialectlabs/web3'; | ||
@@ -17,0 +17,0 @@ const SOLANA_ENDPOINT = process.env.RPC_URL || 'http://localhost:8899'; |
@@ -40,2 +40,3 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; | ||
healthRatio: number; | ||
resourceId: ResourceId; | ||
}; | ||
@@ -54,4 +55,5 @@ | ||
healthRatio: Math.random(), | ||
resourceId, | ||
}, | ||
resourceId, | ||
groupingKey: resourceId.toString(), | ||
}), | ||
@@ -77,8 +79,13 @@ ); | ||
.notify() | ||
.dialectThread(({ value }) => ({ | ||
message: `Your cratio = ${value} below warning threshold`, | ||
})) | ||
.dialectThread( | ||
({ value }) => ({ | ||
message: `Your cratio = ${value} below warning threshold`, | ||
}), | ||
{ | ||
dispatch: 'unicast', | ||
to: ({ origin: { resourceId } }) => resourceId, | ||
}, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
dataSourceMonitor.start(); |
@@ -16,2 +16,3 @@ import { | ||
healthRatio: number; | ||
subscribers: ResourceId[]; | ||
}; | ||
@@ -34,4 +35,5 @@ | ||
healthRatio: Math.random() * 10, | ||
subscribers, | ||
}, | ||
resourceId, | ||
groupingKey: resourceId.toBase58(), | ||
}), | ||
@@ -56,4 +58,5 @@ ); | ||
consoleNotificationSink, | ||
{ dispatch: 'broadcast' }, | ||
) | ||
.and() | ||
.also() | ||
.transform<number, number>({ | ||
@@ -74,7 +77,6 @@ keys: ['cratio'], | ||
consoleNotificationSink, | ||
() => Math.random() > 0.5, | ||
{ dispatch: 'broadcast' }, | ||
) | ||
.and() | ||
.dispatch('broadcast') | ||
.build(); | ||
monitor.start(); |
@@ -24,13 +24,21 @@ import { | ||
.custom<DialectNotification>( | ||
({ context }) => ({ | ||
message: `Hey ${context.resourceId}, welcome!`, | ||
({ | ||
context: { | ||
origin: { resourceId }, | ||
}, | ||
}) => ({ | ||
message: `Hey ${resourceId}, welcome!`, | ||
}), | ||
consoleNotificationSink, | ||
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId }, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
monitor.start().then(() => { | ||
dummySubscriberRepository.addNewSubscriber(new Keypair().publicKey); | ||
}); | ||
monitor.start(); | ||
const pk = new Keypair().publicKey; | ||
setTimeout(() => { | ||
dummySubscriberRepository.addNewSubscriber(pk); | ||
}, 100); |
@@ -5,2 +5,4 @@ import { | ||
SubscriberRepository, | ||
Web2Subscriber, | ||
Web2SubscriberRepository, | ||
} from '../src'; | ||
@@ -43,1 +45,11 @@ import { Keypair } from '@solana/web3.js'; | ||
} | ||
export class DummyWeb2SubscriberRepository implements Web2SubscriberRepository { | ||
async findAll(): Promise<Web2Subscriber[]> { | ||
return Promise.resolve([]); | ||
} | ||
async findBy(resourceIds: ResourceId[]): Promise<Web2Subscriber[]> { | ||
return Promise.resolve([]); | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import { NotificationSink, ResourceId, Notification } from '..'; | ||
import { Notification, NotificationSink, ResourceId } from '../src'; | ||
@@ -3,0 +3,0 @@ export class ConsoleNotificationSink<N extends Notification> |
@@ -15,2 +15,3 @@ import { | ||
import { Duration } from 'luxon'; | ||
import { PublicKey } from '@solana/web3.js'; | ||
@@ -20,2 +21,3 @@ type DataType = { | ||
healthRatio: number; | ||
resourceId: PublicKey; | ||
}; | ||
@@ -37,4 +39,5 @@ | ||
healthRatio: Math.random() * 10, | ||
resourceId, | ||
}, | ||
resourceId, | ||
groupingKey: resourceId.toBase58(), | ||
}), | ||
@@ -82,6 +85,6 @@ ); | ||
consoleNotificationSink, | ||
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId }, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
monitor.start(); |
@@ -16,2 +16,3 @@ import { | ||
share: number; | ||
resourceId: ResourceId; | ||
}; | ||
@@ -33,2 +34,3 @@ | ||
.poll((subscribers: ResourceId[]) => { | ||
// subscribers are only those users who created a dialect thread! | ||
const sourceData: SourceData<DataPool>[] = subscribers.map( | ||
@@ -38,4 +40,5 @@ (resourceId) => ({ | ||
share: counter * counter, | ||
resourceId, | ||
}, | ||
resourceId, | ||
groupingKey: resourceId.toBase58(), | ||
}), | ||
@@ -61,6 +64,6 @@ ); | ||
consoleDataSink, | ||
{ dispatch: 'broadcast' }, | ||
) | ||
.and() | ||
.dispatch('broadcast') | ||
.build(); | ||
monitor.start(); |
@@ -6,2 +6,3 @@ import { | ||
Pipelines, | ||
ResourceId, | ||
SourceData, | ||
@@ -11,3 +12,3 @@ } from '../src'; | ||
import { ConsoleNotificationSink } from './004-custom-notification-sink'; | ||
import { Observable } from 'rxjs'; | ||
import { Subject } from 'rxjs'; | ||
import { Keypair } from '@solana/web3.js'; | ||
@@ -18,2 +19,3 @@ | ||
healthRatio: number; | ||
resourceId: ResourceId; | ||
}; | ||
@@ -26,2 +28,4 @@ | ||
const subject = new Subject<SourceData<DataType>>(); | ||
const monitor: Monitor<DataType> = Monitors.builder({ | ||
@@ -31,17 +35,3 @@ subscriberRepository: new DummySubscriberRepository(1), | ||
.defineDataSource<DataType>() | ||
.push( | ||
new Observable((subscriber) => { | ||
const publicKey = Keypair.generate().publicKey; | ||
const d1: SourceData<DataType> = { | ||
data: { cratio: 0, healthRatio: 2 }, | ||
resourceId: publicKey, | ||
}; | ||
const d2: SourceData<DataType> = { | ||
data: { cratio: 1, healthRatio: 0 }, | ||
resourceId: publicKey, | ||
}; | ||
subscriber.next(d1); | ||
subscriber.next(d2); | ||
}), | ||
) | ||
.push(subject) | ||
.transform<number, number>({ | ||
@@ -62,6 +52,29 @@ keys: ['cratio'], | ||
consoleNotificationSink, | ||
{ | ||
dispatch: 'multicast', | ||
to: ({ origin: { resourceId } }) => [ | ||
resourceId, | ||
Keypair.generate().publicKey, | ||
], | ||
}, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
monitor.start(); | ||
const publicKey = Keypair.generate().publicKey; | ||
const d1: SourceData<DataType> = { | ||
data: { cratio: 0, healthRatio: 2, resourceId: publicKey }, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
setTimeout(() => { | ||
subject.next(d1); | ||
}, 100); | ||
const d2: SourceData<DataType> = { | ||
data: { cratio: 1, healthRatio: 0, resourceId: publicKey }, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
setTimeout(() => { | ||
subject.next(d2); | ||
}, 200); |
@@ -7,2 +7,3 @@ import { | ||
Pipelines, | ||
ResourceId, | ||
SourceData, | ||
@@ -12,3 +13,3 @@ } from '../src'; | ||
import { ConsoleNotificationSink } from './004-custom-notification-sink'; | ||
import { AsyncSubject, Observable, Subject } from 'rxjs'; | ||
import { Subject } from 'rxjs'; | ||
import { Keypair, PublicKey } from '@solana/web3.js'; | ||
@@ -18,2 +19,3 @@ | ||
attribute: NestedObject[]; | ||
resourceId: ResourceId; | ||
}; | ||
@@ -47,4 +49,5 @@ | ||
consoleNotificationSink, | ||
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId }, | ||
) | ||
.and() | ||
.also() | ||
.transform<NestedObject[], NestedObject[]>({ | ||
@@ -60,5 +63,5 @@ keys: ['attribute'], | ||
consoleNotificationSink, | ||
{ dispatch: 'unicast', to: ({ origin: { resourceId } }) => resourceId }, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
@@ -74,8 +77,14 @@ monitor.start(); | ||
const d1: SourceData<DataType> = { | ||
data: { attribute: [{ publicKey: pk1 }, { publicKey: pk2 }] }, | ||
resourceId: publicKey, | ||
data: { | ||
attribute: [{ publicKey: pk1 }, { publicKey: pk2 }], | ||
resourceId: publicKey, | ||
}, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
const d2: SourceData<DataType> = { | ||
data: { attribute: [{ publicKey: pk3 }] }, | ||
resourceId: publicKey, | ||
data: { | ||
attribute: [{ publicKey: pk3 }], | ||
resourceId: publicKey, | ||
}, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
@@ -82,0 +91,0 @@ setTimeout(() => { |
@@ -6,8 +6,9 @@ import { | ||
Pipelines, | ||
ResourceEmail, | ||
ResourceEmailRepository, | ||
ResourceId, | ||
SourceData, | ||
} from '../src'; | ||
import { DummySubscriberRepository } from './003-custom-subscriber-repository'; | ||
import { | ||
DummySubscriberRepository, | ||
DummyWeb2SubscriberRepository, | ||
} from './003-custom-subscriber-repository'; | ||
import { ConsoleNotificationSink } from './004-custom-notification-sink'; | ||
@@ -20,2 +21,3 @@ import { Observable } from 'rxjs'; | ||
healthRatio: number; | ||
resourceId: ResourceId; | ||
}; | ||
@@ -28,14 +30,5 @@ | ||
class DummyResourceEmailRepository implements ResourceEmailRepository { | ||
findBy(resourceIds: ResourceId[]): Promise<ResourceEmail[]> { | ||
return Promise.resolve([ | ||
{ | ||
resourceId: resourceIds[0], | ||
email: process.env.RECEIVER_EMAIL!, | ||
}, | ||
]); | ||
} | ||
} | ||
const monitor: Monitor<DataType> = Monitors.builder({ | ||
subscriberRepository: new DummySubscriberRepository(1), | ||
web2SubscriberRepository: new DummyWeb2SubscriberRepository(), | ||
sinks: { | ||
@@ -45,3 +38,2 @@ email: { | ||
apiToken: process.env.EMAIL_SINK_TOKEN!, | ||
resourceEmailRepository: new DummyResourceEmailRepository(), | ||
}, | ||
@@ -55,8 +47,8 @@ }, | ||
const d1: SourceData<DataType> = { | ||
data: { cratio: 0, healthRatio: 2 }, | ||
resourceId: publicKey, | ||
data: { cratio: 0, healthRatio: 2, resourceId: publicKey }, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
const d2: SourceData<DataType> = { | ||
data: { cratio: 1, healthRatio: 0 }, | ||
resourceId: publicKey, | ||
data: { cratio: 1, healthRatio: 0, resourceId: publicKey }, | ||
groupingKey: publicKey.toBase58(), | ||
}; | ||
@@ -77,9 +69,15 @@ subscriber.next(d1); | ||
.notify() | ||
.email(({ value }) => ({ | ||
subject: '[WARNING] Cratio above warning threshold', | ||
text: `Your cratio = ${value} above warning threshold`, | ||
})) | ||
.dialectThread(({ value }) => ({ | ||
message: `Your cratio = ${value} above warning threshold`, | ||
})) | ||
.email( | ||
({ value }) => ({ | ||
subject: '[WARNING] Cratio above warning threshold', | ||
text: `Your cratio = ${value} above warning threshold`, | ||
}), | ||
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId }, | ||
) | ||
.dialectThread( | ||
({ value }) => ({ | ||
message: `Your cratio = ${value} above warning threshold`, | ||
}), | ||
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId }, | ||
) | ||
.custom<DialectNotification>( | ||
@@ -90,6 +88,6 @@ ({ value }) => ({ | ||
consoleNotificationSink, | ||
{ dispatch: 'unicast', to: ({ origin }) => origin.resourceId }, | ||
) | ||
.and() | ||
.dispatch('unicast') | ||
.build(); | ||
monitor.start(); |
@@ -12,4 +12,3 @@ import { PublicKey } from '@solana/web3.js'; | ||
data: T; | ||
resourceId: ResourceId; | ||
groupingKey?: string; | ||
groupingKey: string; | ||
} | ||
@@ -28,5 +27,6 @@ /** | ||
export interface Context<T extends object> { | ||
resourceId: ResourceId; | ||
origin: T; | ||
groupingKey: string; | ||
trace: Trace[]; | ||
subscribers: ResourceId[]; | ||
} | ||
@@ -51,4 +51,5 @@ /** | ||
export interface SubscriberEvent { | ||
resourceId: ResourceId; | ||
state: SubscriberState; | ||
} | ||
export declare type SubscriberState = 'added' | 'removed'; |
@@ -6,3 +6,2 @@ "use strict"; | ||
const web3_1 = require("@dialectlabs/web3"); | ||
const ts_retry_1 = require("ts-retry"); | ||
class DialectNotificationSink { | ||
@@ -18,6 +17,9 @@ constructor(dialectProgram, monitorKeypair, subscriberRepository) { | ||
const recipientsFiltered = recipients.filter((it) => !!subscriberPkToSubscriber[it.toBase58()]); | ||
const results = await Promise.allSettled(recipientsFiltered.map(async (it) => (0, ts_retry_1.retryAsync)(async () => { | ||
const dialectAccount = await (0, dialect_extensions_1.getDialectAccount)(this.dialectProgram, [this.monitorKeypair.publicKey, it]); | ||
const results = await Promise.allSettled(recipientsFiltered.map(async (it) => async () => { | ||
const dialectAccount = await (0, dialect_extensions_1.getDialectAccount)(this.dialectProgram, [ | ||
this.monitorKeypair.publicKey, | ||
it, | ||
]); | ||
return (0, web3_1.sendMessage)(this.dialectProgram, dialectAccount, this.monitorKeypair, message); | ||
}, { delay: 100, maxTry: 5 }))); | ||
}, { delay: 100, maxTry: 5 })); | ||
const failedSends = results | ||
@@ -24,0 +26,0 @@ .filter((it) => it.status === 'rejected') |
@@ -9,2 +9,5 @@ export * from './data-model'; | ||
export * from './sengrid-email-notification-sink'; | ||
export * from './telegram-notification-sink'; | ||
export * from './dialect-notification-sink'; | ||
export * from './twilio-sms-notification-sink'; | ||
export * from './web-subscriber.repository'; |
@@ -21,2 +21,5 @@ "use strict"; | ||
__exportStar(require("./sengrid-email-notification-sink"), exports); | ||
__exportStar(require("./telegram-notification-sink"), exports); | ||
__exportStar(require("./dialect-notification-sink"), exports); | ||
__exportStar(require("./twilio-sms-notification-sink"), exports); | ||
__exportStar(require("./web-subscriber.repository"), exports); |
import { Duration } from 'luxon'; | ||
import { MonitorFactory } from '../monitor-factory'; | ||
import { DataSource, DataSourceTransformationPipeline, PollableDataSource } from '../ports'; | ||
import { DataSource, DataSourceTransformationPipeline } from '../ports'; | ||
import { Monitor, MonitorProps } from '../monitor-api'; | ||
@@ -8,10 +8,9 @@ import { SubscriberEvent } from '../data-model'; | ||
private readonly subscriberRepository; | ||
private readonly web2SubscriberRepository; | ||
private readonly shutdownHooks; | ||
constructor({ dialectProgram, monitorKeypair, subscriberRepository, }: MonitorProps); | ||
constructor({ dialectProgram, monitorKeypair, subscriberRepository, web2SubscriberRepository, }: MonitorProps); | ||
shutdown(): Promise<any[]>; | ||
createUnicastMonitor<T extends object>(dataSource: DataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>; | ||
private decorateWithPushyDataSource; | ||
createBroadcastMonitor<T extends object>(dataSource: PollableDataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>; | ||
createDefaultMonitor<T extends object>(dataSource: DataSource<T>, datasourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval?: Duration): Monitor<T>; | ||
createSubscriberEventMonitor(dataSourceTransformationPipelines: DataSourceTransformationPipeline<SubscriberEvent, any>[]): Monitor<SubscriberEvent>; | ||
private toPushyDataSource; | ||
} |
@@ -7,8 +7,9 @@ "use strict"; | ||
const luxon_1 = require("luxon"); | ||
const unicast_monitor_1 = require("./unicast-monitor"); | ||
const rxjs_1 = require("rxjs"); | ||
const broadcast_monitor_1 = require("./broadcast-monitor"); | ||
const operators_1 = require("rxjs/operators"); | ||
const web_subscriber_repository_1 = require("../web-subscriber.repository"); | ||
const subsbscriber_repository_utilts_1 = require("./subsbscriber-repository-utilts"); | ||
const default_monitor_1 = require("./default-monitor"); | ||
class DefaultMonitorFactory { | ||
constructor({ dialectProgram, monitorKeypair, subscriberRepository, }) { | ||
constructor({ dialectProgram, monitorKeypair, subscriberRepository, web2SubscriberRepository, }) { | ||
this.shutdownHooks = []; | ||
@@ -20,2 +21,4 @@ if (dialectProgram && monitorKeypair) { | ||
} | ||
this.web2SubscriberRepository = | ||
web2SubscriberRepository !== null && web2SubscriberRepository !== void 0 ? web2SubscriberRepository : new web_subscriber_repository_1.NoopWeb2SubscriberRepository(); | ||
if (subscriberRepository) { | ||
@@ -32,38 +35,30 @@ this.subscriberRepository = subscriberRepository; | ||
} | ||
createUnicastMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) { | ||
const pushyDataSource = this.decorateWithPushyDataSource(dataSource, pollInterval); | ||
const unicastMonitor = new unicast_monitor_1.UnicastMonitor(pushyDataSource, datasourceTransformationPipelines); | ||
this.shutdownHooks.push(() => unicastMonitor.stop()); | ||
return unicastMonitor; | ||
createDefaultMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) { | ||
const pushyDataSource = !('subscribe' in dataSource) | ||
? this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository, this.web2SubscriberRepository) | ||
: dataSource; | ||
const monitor = new default_monitor_1.DefaultMonitor(pushyDataSource, datasourceTransformationPipelines, this.subscriberRepository, this.web2SubscriberRepository); | ||
this.shutdownHooks.push(() => monitor.stop()); | ||
return monitor; | ||
} | ||
decorateWithPushyDataSource(dataSource, pollInterval) { | ||
if ('subscribe' in dataSource) { | ||
return dataSource; | ||
} | ||
return this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository); | ||
} | ||
createBroadcastMonitor(dataSource, datasourceTransformationPipelines, pollInterval = luxon_1.Duration.fromObject({ seconds: 10 })) { | ||
const pushyDataSource = this.toPushyDataSource(dataSource, pollInterval, this.subscriberRepository); | ||
const broadcastMonitor = new broadcast_monitor_1.BroadcastMonitor(pushyDataSource, datasourceTransformationPipelines, this.subscriberRepository); | ||
this.shutdownHooks.push(() => broadcastMonitor.stop()); | ||
return broadcastMonitor; | ||
} | ||
createSubscriberEventMonitor(dataSourceTransformationPipelines) { | ||
const dataSource = new rxjs_1.Observable((subscriber) => this.subscriberRepository.subscribe((resourceId) => subscriber.next({ | ||
resourceId, | ||
groupingKey: resourceId.toBase58(), | ||
data: { | ||
resourceId, | ||
state: 'added', | ||
}, | ||
}), (resourceId) => subscriber.next({ | ||
resourceId, | ||
groupingKey: resourceId.toBase58(), | ||
data: { | ||
resourceId, | ||
state: 'removed', | ||
}, | ||
}))); | ||
const unicastMonitor = new unicast_monitor_1.UnicastMonitor(dataSource, dataSourceTransformationPipelines); | ||
this.shutdownHooks.push(() => unicastMonitor.stop()); | ||
return unicastMonitor; | ||
const monitor = new default_monitor_1.DefaultMonitor(dataSource, dataSourceTransformationPipelines, this.subscriberRepository, this.web2SubscriberRepository); | ||
this.shutdownHooks.push(() => monitor.stop()); | ||
return monitor; | ||
} | ||
toPushyDataSource(dataSource, pollInterval, subscriberRepository, pollTimeout = luxon_1.Duration.fromObject({ minutes: 5 })) { | ||
return (0, rxjs_1.timer)(0, pollInterval.toMillis()).pipe((0, rxjs_1.exhaustMap)(() => subscriberRepository.findAll()), (0, rxjs_1.exhaustMap)((resources) => (0, rxjs_1.from)(dataSource(resources))), (0, operators_1.timeout)(pollTimeout.toMillis()), (0, rxjs_1.catchError)((error) => { | ||
toPushyDataSource(dataSource, pollInterval, subscriberRepository, web2SubscriberRepository, pollTimeout = luxon_1.Duration.fromObject({ minutes: 5 })) { | ||
return (0, rxjs_1.timer)(0, pollInterval.toMillis()).pipe((0, rxjs_1.exhaustMap)(() => (0, rxjs_1.from)((0, subsbscriber_repository_utilts_1.findAllDistinct)(subscriberRepository, web2SubscriberRepository))), (0, rxjs_1.exhaustMap)((resources) => (0, rxjs_1.from)(dataSource(resources))), (0, operators_1.timeout)(pollTimeout.toMillis()), (0, rxjs_1.catchError)((error) => { | ||
if (error instanceof rxjs_1.TimeoutError) { | ||
@@ -73,5 +68,5 @@ return (0, rxjs_1.throwError)(new Error(`Poll timeout of ${pollTimeout.toISO()} reached. ` + error)); | ||
return (0, rxjs_1.throwError)(error); | ||
}), (0, rxjs_1.concatMap)((it) => it)); | ||
}), (0, rxjs_1.mergeMap)((it) => it)); | ||
} | ||
} | ||
exports.DefaultMonitorFactory = DefaultMonitorFactory; |
@@ -8,3 +8,2 @@ import { ResourceId } from '../data-model'; | ||
static decorate(other: SubscriberRepository): InMemorySubscriberRepository; | ||
static decorateBlocking(other: SubscriberRepository): Promise<InMemorySubscriberRepository>; | ||
findAll(): Promise<ResourceId[]>; | ||
@@ -11,0 +10,0 @@ findByResourceId(resourceId: ResourceId): Promise<ResourceId | null>; |
@@ -14,7 +14,2 @@ "use strict"; | ||
} | ||
static async decorateBlocking(other) { | ||
const repository = new InMemorySubscriberRepository(other); | ||
await repository.initialize(); | ||
return repository; | ||
} | ||
async findAll() { | ||
@@ -21,0 +16,0 @@ return Array(...this.subscribers.values()); |
@@ -1,2 +0,2 @@ | ||
import { AddSinksStep, AddTransformationsStep, BuildStep, ChooseDataSourceStep, DefineDataSourceStep, DispatchStrategy, NotifyStep, Transformation } from '../monitor-builder'; | ||
import { AddSinksStep, AddTransformationsStep, ChooseDataSourceStep, DefineDataSourceStep, NotifyStep, Transformation } from '../monitor-builder'; | ||
import { SubscriberEvent } from '../data-model'; | ||
@@ -8,2 +8,4 @@ import { DataSourceTransformationPipeline, PollableDataSource, PushyDataSource } from '../ports'; | ||
import { SengridEmailNotificationSink } from '../sengrid-email-notification-sink'; | ||
import { TwilioSmsNotificationSink } from '../twilio-sms-notification-sink'; | ||
import { TelegramNotificationSink } from '../telegram-notification-sink'; | ||
/** | ||
@@ -19,2 +21,4 @@ * A set of factory methods to create monitors | ||
emailNotificationSink?: SengridEmailNotificationSink; | ||
smsNotificationSink?: TwilioSmsNotificationSink; | ||
telegramNotificationSink?: TelegramNotificationSink; | ||
constructor(monitorProps: MonitorProps); | ||
@@ -42,10 +46,8 @@ } | ||
declare class AddTransformationsStepImpl<T extends object> implements AddTransformationsStep<T> { | ||
private readonly monitorBuilderState; | ||
readonly monitorBuilderState: MonitorsBuilderState<T>; | ||
dataSourceTransformationPipelines: DataSourceTransformationPipeline<T, any>[]; | ||
dispatchStrategy?: DispatchStrategy; | ||
constructor(monitorBuilderState: MonitorsBuilderState<T>); | ||
notify(): AddSinksStep<T, T>; | ||
dispatch(strategy: DispatchStrategy): BuildStep<T>; | ||
transform<V, R>(transformation: Transformation<T, V, R>): NotifyStep<T, R>; | ||
} | ||
export {}; |
@@ -10,4 +10,8 @@ "use strict"; | ||
const sengrid_email_notification_sink_1 = require("../sengrid-email-notification-sink"); | ||
const twilio_sms_notification_sink_1 = require("../twilio-sms-notification-sink"); | ||
const telegram_notification_sink_1 = require("../telegram-notification-sink"); | ||
const on_chain_subscriber_repository_1 = require("./on-chain-subscriber.repository"); | ||
const in_memory_subscriber_repository_1 = require("./in-memory-subscriber.repository"); | ||
const rest_web2_subscriber_repository_1 = require("./rest-web2-subscriber.repository"); | ||
const web_subscriber_repository_1 = require("../web-subscriber.repository"); | ||
/** | ||
@@ -18,3 +22,11 @@ * A set of factory methods to create monitors | ||
constructor(monitorProps) { | ||
var _a, _b, _c; | ||
this.monitorProps = monitorProps; | ||
if (monitorProps.web2SubscriberRepositoryUrl && | ||
!monitorProps.web2SubscriberRepository) { | ||
const postgresWeb2ResourceRepository = new rest_web2_subscriber_repository_1.RestWeb2SubscriberRepository(monitorProps.web2SubscriberRepositoryUrl, (_a = monitorProps.monitorKeypair) === null || _a === void 0 ? void 0 : _a.publicKey); | ||
monitorProps.web2SubscriberRepository = | ||
new rest_web2_subscriber_repository_1.InMemoryWeb2SubscriberRepository((_b = monitorProps.monitorKeypair) === null || _b === void 0 ? void 0 : _b.publicKey, postgresWeb2ResourceRepository); | ||
} | ||
const web2SubscriberRepository = (_c = monitorProps.web2SubscriberRepository) !== null && _c !== void 0 ? _c : new web_subscriber_repository_1.NoopWeb2SubscriberRepository(); | ||
if (monitorProps.dialectProgram && monitorProps.monitorKeypair) { | ||
@@ -26,2 +38,3 @@ if (!monitorProps.subscriberRepository) { | ||
} | ||
// TODO inspect | ||
this.dialectNotificationSink = new dialect_notification_sink_1.DialectNotificationSink(monitorProps.dialectProgram, monitorProps.monitorKeypair, monitorProps.subscriberRepository); | ||
@@ -31,4 +44,13 @@ } | ||
if (sinks === null || sinks === void 0 ? void 0 : sinks.email) { | ||
this.emailNotificationSink = new sengrid_email_notification_sink_1.SengridEmailNotificationSink(sinks.email.apiToken, sinks.email.senderEmail, sinks.email.resourceEmailRepository); | ||
this.emailNotificationSink = new sengrid_email_notification_sink_1.SengridEmailNotificationSink(sinks.email.apiToken, sinks.email.senderEmail, web2SubscriberRepository); | ||
} | ||
if (sinks === null || sinks === void 0 ? void 0 : sinks.sms) { | ||
this.smsNotificationSink = new twilio_sms_notification_sink_1.TwilioSmsNotificationSink({ | ||
username: sinks.sms.twilioUsername, | ||
password: sinks.sms.twilioPassword, | ||
}, sinks.sms.senderSmsNumber, web2SubscriberRepository); | ||
} | ||
if (sinks === null || sinks === void 0 ? void 0 : sinks.telegram) { | ||
this.telegramNotificationSink = new telegram_notification_sink_1.TelegramNotificationSink(sinks.telegram.telegramBotToken, web2SubscriberRepository); | ||
} | ||
} | ||
@@ -80,17 +102,7 @@ } | ||
notify() { | ||
const identityTransformation = (dataSource) => dataSource.pipe((0, operators_1.map)(({ data: value, resourceId }) => ({ | ||
context: { | ||
origin: value, | ||
resourceId, | ||
trace: [], | ||
}, | ||
value, | ||
}))); | ||
const identityTransformation = (dataSource) => dataSource; | ||
// > = (dataSource) => dataSource.pipe(tap(console.log(t))); | ||
this.dataSourceTransformationPipelines.push(identityTransformation); | ||
return new AddSinksStepImpl(this, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink); | ||
} | ||
dispatch(strategy) { | ||
this.dispatchStrategy = strategy; | ||
return new BuildStepImpl(this.monitorBuilderState); | ||
} | ||
transform(transformation) { | ||
@@ -100,9 +112,5 @@ const dataSourceTransformationPipelines = []; | ||
const adaptedToDataSourceTypePipelines = keys.flatMap((key) => pipelines.map((pipeline) => { | ||
const adaptedToDataSourceType = (dataSource) => pipeline(dataSource.pipe((0, operators_1.map)(({ data: origin, resourceId }) => ({ | ||
context: { | ||
origin, | ||
resourceId, | ||
trace: [], | ||
}, | ||
value: origin[key], | ||
const adaptedToDataSourceType = (dataSource) => pipeline(dataSource.pipe((0, operators_1.map)((it) => ({ | ||
...it, | ||
value: it.value[key], | ||
})))); | ||
@@ -122,7 +130,7 @@ return adaptedToDataSourceType; | ||
notify() { | ||
return new AddSinksStepImpl(this.addTransformationsStep, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink); | ||
return new AddSinksStepImpl(this.addTransformationsStep, this.dataSourceTransformationPipelines, this.monitorBuilderState.dialectNotificationSink, this.monitorBuilderState.emailNotificationSink, this.monitorBuilderState.smsNotificationSink, this.monitorBuilderState.telegramNotificationSink); | ||
} | ||
} | ||
class AddSinksStepImpl { | ||
constructor(addTransformationsStep, dataSourceTransformationPipelines, dialectNotificationSink, emailNotificationSink) { | ||
constructor(addTransformationsStep, dataSourceTransformationPipelines, dialectNotificationSink, emailNotificationSink, smsNotificationSink, telegramNotificationSink) { | ||
this.addTransformationsStep = addTransformationsStep; | ||
@@ -132,21 +140,20 @@ this.dataSourceTransformationPipelines = dataSourceTransformationPipelines; | ||
this.emailNotificationSink = emailNotificationSink; | ||
this.smsNotificationSink = smsNotificationSink; | ||
this.telegramNotificationSink = telegramNotificationSink; | ||
this.sinkWriters = []; | ||
} | ||
and() { | ||
const transformAndLoadPipelines = this.dataSourceTransformationPipelines.map((dataSourceTransformationPipeline) => { | ||
const transformAndLoadPipeline = (dataSource, targets) => dataSourceTransformationPipeline(dataSource, targets).pipe((0, rxjs_1.exhaustMap)((event) => (0, rxjs_1.from)(Promise.all(this.sinkWriters.map((it) => it(event, targets)))))); | ||
return transformAndLoadPipeline; | ||
}); | ||
this.addTransformationsStep.dataSourceTransformationPipelines.push(...transformAndLoadPipelines); | ||
also() { | ||
this.populateDataSourceTransformationPipelines(); | ||
return this.addTransformationsStep; | ||
} | ||
dialectThread(adapter, recipientsSelector, recipientPredicate) { | ||
dialectThread(adapter, dispatchStrategy) { | ||
if (!this.dialectNotificationSink) { | ||
throw new Error('Dialect notification sink must be initialized before using'); | ||
} | ||
const sinkWriter = (data, resources) => { | ||
const toBeNotified = recipientsSelector | ||
? recipientsSelector(data) | ||
: resources; | ||
return this.dialectNotificationSink.push(adapter(data), toBeNotified.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true)); | ||
return this.custom(adapter, this.dialectNotificationSink, dispatchStrategy); | ||
} | ||
custom(adapter, sink, dispatchStrategy) { | ||
const sinkWriter = (data) => { | ||
const toBeNotified = this.selectResources(dispatchStrategy, data.context.subscribers, data); | ||
return sink.push(adapter(data), toBeNotified); | ||
}; | ||
@@ -156,15 +163,44 @@ this.sinkWriters.push(sinkWriter); | ||
} | ||
custom(adapter, sink, recipientPredicate) { | ||
const sinkWriter = (data, resources) => sink.push(adapter(data), resources.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true)); | ||
this.sinkWriters.push(sinkWriter); | ||
return this; | ||
} | ||
email(adapter, recipientPredicate) { | ||
email(adapter, dispatchStrategy) { | ||
if (!this.emailNotificationSink) { | ||
throw new Error('Email notification sink must be initialized before using'); | ||
} | ||
const sinkWriter = (data, resources) => this.emailNotificationSink.push(adapter(data), resources.filter((it) => recipientPredicate ? recipientPredicate(data, it) : true)); | ||
this.sinkWriters.push(sinkWriter); | ||
return this; | ||
return this.custom(adapter, this.emailNotificationSink, dispatchStrategy); | ||
} | ||
sms(adapter, dispatchStrategy) { | ||
if (!this.smsNotificationSink) { | ||
throw new Error('SMS notification sink must be initialized before using'); | ||
} | ||
return this.custom(adapter, this.smsNotificationSink, dispatchStrategy); | ||
} | ||
telegram(adapter, dispatchStrategy) { | ||
if (!this.telegramNotificationSink) { | ||
throw new Error('Telegram notification sink must be initialized before using'); | ||
} | ||
return this.custom(adapter, this.telegramNotificationSink, dispatchStrategy); | ||
} | ||
and() { | ||
this.populateDataSourceTransformationPipelines(); | ||
return new BuildStepImpl(this.addTransformationsStep.monitorBuilderState); | ||
} | ||
populateDataSourceTransformationPipelines() { | ||
const transformAndLoadPipelines = this.dataSourceTransformationPipelines.map((dataSourceTransformationPipeline) => { | ||
const transformAndLoadPipeline = (dataSource) => dataSourceTransformationPipeline(dataSource).pipe((0, rxjs_1.exhaustMap)((event) => (0, rxjs_1.from)(Promise.all(this.sinkWriters.map((it) => it(event)))))); | ||
return transformAndLoadPipeline; | ||
}); | ||
this.addTransformationsStep.dataSourceTransformationPipelines.push(...transformAndLoadPipelines); | ||
} | ||
selectResources(dispatchStrategy, resources, { context }) { | ||
switch (dispatchStrategy.dispatch) { | ||
case 'broadcast': { | ||
return resources; | ||
} | ||
case 'unicast': { | ||
return [dispatchStrategy.to(context)]; | ||
} | ||
case 'multicast': { | ||
return dispatchStrategy.to(context); | ||
} | ||
} | ||
} | ||
} | ||
@@ -196,4 +232,4 @@ class BuildStepImpl { | ||
buildSubscriberEventMonitor(addTransformationsStep, monitorProps) { | ||
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep; | ||
if (!dataSourceTransformationPipelines || !dispatchStrategy) { | ||
const { dataSourceTransformationPipelines } = addTransformationsStep; | ||
if (!dataSourceTransformationPipelines) { | ||
throw new Error('Expected [dataSourceTransformationPipelines, dispatchStrategy] to be defined'); | ||
@@ -216,29 +252,18 @@ } | ||
const { pollableDataSource, pollInterval } = defineDataSourceStep; | ||
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep; | ||
const { dataSourceTransformationPipelines } = addTransformationsStep; | ||
if (!pollableDataSource || | ||
!pollInterval || | ||
!dataSourceTransformationPipelines || | ||
!dispatchStrategy) { | ||
throw new Error('Expected [pollableDataSource, pollInterval, dataSourceTransformationPipelines, dispatchStrategy] to be defined'); | ||
!dataSourceTransformationPipelines) { | ||
throw new Error('Expected [pollableDataSource, pollInterval, dataSourceTransformationPipelines] to be defined'); | ||
} | ||
switch (addTransformationsStep.dispatchStrategy) { | ||
case 'broadcast': | ||
return monitor_api_1.Monitors.factory(monitorProps).createBroadcastMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval); | ||
case 'unicast': | ||
return monitor_api_1.Monitors.factory(monitorProps).createUnicastMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval); | ||
default: | ||
throw new Error('Unknown dispatchStrategy: ' + | ||
addTransformationsStep.dispatchStrategy); | ||
} | ||
return monitor_api_1.Monitors.factory(monitorProps).createDefaultMonitor(pollableDataSource, dataSourceTransformationPipelines, pollInterval); | ||
} | ||
createForPushy(defineDataSourceStep, addTransformationsStep, monitorProps) { | ||
const { pushyDataSource } = defineDataSourceStep; | ||
const { dataSourceTransformationPipelines, dispatchStrategy } = addTransformationsStep; | ||
if (!pushyDataSource || | ||
!dataSourceTransformationPipelines || | ||
!dispatchStrategy) { | ||
throw new Error('Expected [pushyDataSource, dataSourceTransformationPipelines, dispatchStrategy] to be defined'); | ||
const { dataSourceTransformationPipelines } = addTransformationsStep; | ||
if (!pushyDataSource || !dataSourceTransformationPipelines) { | ||
throw new Error('Expected [pushyDataSource, dataSourceTransformationPipelines] to be defined'); | ||
} | ||
return monitor_api_1.Monitors.factory(monitorProps).createUnicastMonitor(pushyDataSource, dataSourceTransformationPipelines, luxon_1.Duration.fromObject({ seconds: 1 })); | ||
return monitor_api_1.Monitors.factory(monitorProps).createDefaultMonitor(pushyDataSource, dataSourceTransformationPipelines, luxon_1.Duration.fromObject({ seconds: 1 })); | ||
} | ||
} |
@@ -6,3 +6,3 @@ import { ChooseDataSourceStep } from './monitor-builder'; | ||
import { SubscriberRepository } from './ports'; | ||
import { ResourceEmailRepository } from './sengrid-email-notification-sink'; | ||
import { Web2SubscriberRepository } from './web-subscriber.repository'; | ||
/** | ||
@@ -27,2 +27,10 @@ * Please specify either | ||
/** | ||
* Allows to set custom web2 subscriber repository | ||
*/ | ||
web2SubscriberRepositoryUrl?: string; | ||
/** | ||
* Allows to set custom web2 subscriber repository | ||
*/ | ||
web2SubscriberRepository?: Web2SubscriberRepository; | ||
/** | ||
* Allows to set sinks configuration to send notifications | ||
@@ -34,2 +42,4 @@ */ | ||
email?: EmailSinkConfiguration; | ||
sms?: SmsSinkConfiguration; | ||
telegram?: TelegramSinkConfiguration; | ||
} | ||
@@ -39,4 +49,11 @@ export interface EmailSinkConfiguration { | ||
senderEmail: string; | ||
resourceEmailRepository: ResourceEmailRepository; | ||
} | ||
export interface SmsSinkConfiguration { | ||
twilioUsername: string; | ||
twilioPassword: string; | ||
senderSmsNumber: string; | ||
} | ||
export interface TelegramSinkConfiguration { | ||
telegramBotToken: string; | ||
} | ||
/** | ||
@@ -43,0 +60,0 @@ * A monitor is an entity that is responsible for execution of unbounded streaming ETL (Extract, Transform, Load) |
import { Duration } from 'luxon'; | ||
import { NotificationSink, PollableDataSource, PushyDataSource, TransformationPipeline } from './ports'; | ||
import { Monitor } from './monitor-api'; | ||
import { Data, ResourceId, SubscriberEvent } from './data-model'; | ||
import { Context, Data, ResourceId, SubscriberEvent } from './data-model'; | ||
import { DialectNotification } from './dialect-notification-sink'; | ||
import { EmailNotification } from './sengrid-email-notification-sink'; | ||
import { SmsNotification } from './twilio-sms-notification-sink'; | ||
import { TelegramNotification } from './telegram-notification-sink'; | ||
export interface ChooseDataSourceStep { | ||
@@ -54,6 +56,16 @@ /** | ||
*/ | ||
export declare type DispatchStrategy = 'unicast' | 'broadcast'; | ||
export declare type DispatchStrategy<T extends object> = BroadcastDispatchStrategy | UnicastDispatchStrategy<T> | MulticastDispatchStrategy<T>; | ||
export declare type BroadcastDispatchStrategy = { | ||
dispatch: 'broadcast'; | ||
}; | ||
export declare type UnicastDispatchStrategy<T extends object> = { | ||
dispatch: 'unicast'; | ||
to: (ctx: Context<T>) => ResourceId; | ||
}; | ||
export declare type MulticastDispatchStrategy<T extends object> = { | ||
dispatch: 'multicast'; | ||
to: (ctx: Context<T>) => ResourceId[]; | ||
}; | ||
export interface AddTransformationsStep<T extends object> { | ||
transform<V, R>(transformation: Transformation<T, V, R>): NotifyStep<T, R>; | ||
dispatch(strategy: DispatchStrategy): BuildStep<T>; | ||
notify(): AddSinksStep<T, T>; | ||
@@ -68,6 +80,9 @@ } | ||
export interface AddSinksStep<T extends object, R> { | ||
dialectThread(adapter: (data: Data<R, T>) => DialectNotification, recipientsSelector?: (data: Data<R, T>) => ResourceId[], recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>; | ||
email(adapter: (data: Data<R, T>) => EmailNotification, recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>; | ||
custom<N>(adapter: (data: Data<R, T>) => N, sink: NotificationSink<N>, recipientPredicate?: (data: Data<R, T>, recipient: ResourceId) => boolean): AddSinksStep<T, R>; | ||
and(): AddTransformationsStep<T>; | ||
dialectThread(adapter: (data: Data<R, T>) => DialectNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>; | ||
email(adapter: (data: Data<R, T>) => EmailNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>; | ||
sms(adapter: (data: Data<R, T>) => SmsNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>; | ||
telegram(adapter: (data: Data<R, T>) => TelegramNotification, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>; | ||
custom<N>(adapter: (data: Data<R, T>) => N, sink: NotificationSink<N>, dispatchStrategy: DispatchStrategy<T>): AddSinksStep<T, R>; | ||
also(): AddTransformationsStep<T>; | ||
and(): BuildStep<T>; | ||
} | ||
@@ -74,0 +89,0 @@ export interface BuildStep<T extends object> { |
@@ -6,5 +6,4 @@ import { Duration } from 'luxon'; | ||
export interface MonitorFactory { | ||
createUnicastMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>; | ||
createBroadcastMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>; | ||
createDefaultMonitor<T extends object>(dataSource: DataSource<T>, transformationPipelines: DataSourceTransformationPipeline<T, any>[], pollInterval: Duration): Monitor<T>; | ||
createSubscriberEventMonitor(eventDetectionPipelines: DataSourceTransformationPipeline<SubscriberEvent, any>[]): Monitor<SubscriberEvent>; | ||
} |
import { Observable } from 'rxjs'; | ||
import { Data, ResourceId, SourceData, Notification } from './data-model'; | ||
import { Data, Notification, ResourceId, SourceData } from './data-model'; | ||
/** | ||
@@ -17,2 +17,3 @@ * An abstraction that represents a source of data, bound to specific type | ||
export declare type PushyDataSource<T extends object> = Observable<SourceData<T>>; | ||
export declare type ContextEnrichedPushyDataSource<T extends object> = Observable<Data<T, T>>; | ||
/** | ||
@@ -22,3 +23,3 @@ * A set of transformations that are executed on-top of unbound pushy data source | ||
*/ | ||
export declare type DataSourceTransformationPipeline<T extends Object, R> = (dataSource: PushyDataSource<T>, targets: ResourceId[]) => Observable<R>; | ||
export declare type DataSourceTransformationPipeline<T extends object, R> = (dataSource: ContextEnrichedPushyDataSource<T>) => Observable<R>; | ||
/** | ||
@@ -25,0 +26,0 @@ * A set of transformations that are executed on-top of a specific key from unbound pushy data source |
import { Notification, ResourceId } from './data-model'; | ||
import { NotificationSink } from './ports'; | ||
import { Web2SubscriberRepository } from './web-subscriber.repository'; | ||
/** | ||
@@ -10,16 +11,8 @@ * Email notification | ||
} | ||
export declare type Email = string; | ||
export declare type ResourceEmail = { | ||
resourceId: ResourceId; | ||
email: Email; | ||
}; | ||
export interface ResourceEmailRepository { | ||
findBy(resourceIds: ResourceId[]): Promise<ResourceEmail[]>; | ||
} | ||
export declare class SengridEmailNotificationSink implements NotificationSink<EmailNotification> { | ||
private readonly sengridApiKey; | ||
private readonly senderEmail; | ||
private readonly resourceIdToReceiverEmailMapper; | ||
constructor(sengridApiKey: string, senderEmail: string, resourceIdToReceiverEmailMapper: ResourceEmailRepository); | ||
private readonly web2SubscriberRepository; | ||
constructor(sengridApiKey: string, senderEmail: string, web2SubscriberRepository: Web2SubscriberRepository); | ||
push(notification: EmailNotification, recipients: ResourceId[]): Promise<void>; | ||
} |
@@ -9,11 +9,15 @@ "use strict"; | ||
class SengridEmailNotificationSink { | ||
constructor(sengridApiKey, senderEmail, resourceIdToReceiverEmailMapper) { | ||
constructor(sengridApiKey, senderEmail, web2SubscriberRepository) { | ||
this.sengridApiKey = sengridApiKey; | ||
this.senderEmail = senderEmail; | ||
this.resourceIdToReceiverEmailMapper = resourceIdToReceiverEmailMapper; | ||
this.web2SubscriberRepository = web2SubscriberRepository; | ||
mail_1.default.setApiKey(sengridApiKey); | ||
} | ||
async push(notification, recipients) { | ||
const recipientEmails = await this.resourceIdToReceiverEmailMapper.findBy(recipients); | ||
const emails = recipientEmails.map(({ email }) => ({ | ||
const recipientEmails = await this.web2SubscriberRepository.findBy(recipients); | ||
console.log('sendgrid-notif-sink, recipients:\n'); | ||
console.log(recipientEmails); | ||
const emails = recipientEmails | ||
.filter(({ email }) => email) | ||
.map(({ email }) => ({ | ||
...notification, | ||
@@ -23,5 +27,14 @@ from: this.senderEmail, | ||
})); | ||
return mail_1.default.send(emails).then(() => { }); | ||
const results = await Promise.allSettled(await mail_1.default.send(emails)); | ||
const failedSends = results | ||
.filter((it) => it.status === 'rejected') | ||
.map((it) => it); | ||
if (failedSends.length > 0) { | ||
console.log(`Failed to send dialect email notification to ${failedSends.length} recipient(s), reasons: | ||
${failedSends.map((it) => it.reason)} | ||
`); | ||
} | ||
return; | ||
} | ||
} | ||
exports.SengridEmailNotificationSink = SengridEmailNotificationSink; |
@@ -70,3 +70,3 @@ "use strict"; | ||
Operators.Window.fixedSizeSliding(2), | ||
(0, rxjs_1.filter)((it) => it.length == 2 && | ||
(0, rxjs_1.filter)((it) => it.length === 2 && | ||
it[0].value <= threshold && | ||
@@ -80,3 +80,3 @@ threshold < it[1].value), | ||
Operators.Window.fixedSizeSliding(2), | ||
(0, rxjs_1.filter)((data) => data.length == 2 && | ||
(0, rxjs_1.filter)((data) => data.length === 2 && | ||
data[0].value >= threshold && | ||
@@ -90,3 +90,3 @@ threshold > data[1].value), | ||
Operators.Window.fixedSizeSliding(2), | ||
(0, rxjs_1.filter)((data) => data.length == 2 && data[1].value - data[0].value >= threshold), | ||
(0, rxjs_1.filter)((data) => data.length === 2 && data[1].value - data[0].value >= threshold), | ||
(0, operators_1.map)(([fst, snd]) => ({ | ||
@@ -111,3 +111,3 @@ ...snd, | ||
Operators.Window.fixedSizeSliding(2), | ||
(0, rxjs_1.filter)((data) => data.length == 2 && data[0].value - data[1].value >= threshold), | ||
(0, rxjs_1.filter)((data) => data.length === 2 && data[0].value - data[1].value >= threshold), | ||
(0, operators_1.map)(([_, snd]) => snd), | ||
@@ -114,0 +114,0 @@ ]; |
@@ -25,3 +25,3 @@ "use strict"; | ||
.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => { | ||
@@ -43,3 +43,3 @@ const added = d2.value.filter((e2) => !d1.value.find((e1) => compareBy(e1, e2))); | ||
.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => { | ||
@@ -61,3 +61,3 @@ const removed = d1.value.filter((e1) => !d2.value.find((e2) => compareBy(e1, e2))); | ||
.pipe(transformation_pipeline_operators_1.Operators.Window.fixedSizeSliding(2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length == 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.filter((it) => it.length === 2)) | ||
.pipe(transformation_pipeline_operators_1.Operators.Transform.map(([d1, d2]) => { | ||
@@ -64,0 +64,0 @@ const added = d2.value.filter((e2) => !d1.value.find((e1) => compareBy(e1, e2))); |
{ | ||
"name": "@dialectlabs/monitor", | ||
"version": "2.0.3-beta", | ||
"version": "2.0.4-beta", | ||
"repository": "git@github.com:dialectlabs/monitor.git", | ||
@@ -18,5 +18,7 @@ "author": "dialectlabs", | ||
"@solana/web3.js": "^1.27.0", | ||
"axios": "^0.27.2", | ||
"luxon": "^2.3.0", | ||
"rxjs": "^7.5.2", | ||
"ts-retry": "^2.4.1" | ||
"telegraf": "^4.7.0", | ||
"twilio": "^3.76.1" | ||
}, | ||
@@ -23,0 +25,0 @@ "devDependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133378
71
2847
9
2
+ Addedaxios@^0.27.2
+ Addedtelegraf@^4.7.0
+ Addedtwilio@^3.76.1
+ Added@telegraf/types@7.1.0(transitive)
+ Addedabort-controller@3.0.0(transitive)
+ Addedagent-base@6.0.2(transitive)
+ Addedasap@2.0.6(transitive)
+ Addedasynckit@0.4.0(transitive)
+ Addedaxios@0.27.2(transitive)
+ Addedbuffer-alloc@1.2.0(transitive)
+ Addedbuffer-alloc-unsafe@1.1.0(transitive)
+ Addedbuffer-equal-constant-time@1.0.1(transitive)
+ Addedbuffer-fill@1.0.0(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addeddayjs@1.11.13(transitive)
+ Addeddebug@4.3.7(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedecdsa-sig-formatter@1.0.11(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedevent-target-shim@5.0.1(transitive)
+ Addedform-data@4.0.1(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhttps-proxy-agent@5.0.1(transitive)
+ Addedjsonwebtoken@8.5.1(transitive)
+ Addedjwa@1.4.1(transitive)
+ Addedjws@3.2.2(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlodash.includes@4.3.0(transitive)
+ Addedlodash.isboolean@3.0.3(transitive)
+ Addedlodash.isinteger@4.0.4(transitive)
+ Addedlodash.isnumber@3.0.3(transitive)
+ Addedlodash.isplainobject@4.0.6(transitive)
+ Addedlodash.isstring@4.0.1(transitive)
+ Addedlodash.once@4.1.1(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedmri@1.2.0(transitive)
+ Addedobject-inspect@1.13.3(transitive)
+ Addedp-timeout@4.1.0(transitive)
+ Addedpop-iterate@1.0.1(transitive)
+ Addedq@2.0.3(transitive)
+ Addedqs@6.13.1(transitive)
+ Addedquerystringify@2.2.0(transitive)
+ Addedrequires-port@1.0.0(transitive)
+ Addedrootpath@0.1.2(transitive)
+ Addedsafe-compare@1.1.4(transitive)
+ Addedsandwich-stream@2.0.2(transitive)
+ Addedscmp@2.1.0(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedside-channel@1.0.6(transitive)
+ Addedtelegraf@4.16.3(transitive)
+ Addedtwilio@3.84.1(transitive)
+ Addedurl-parse@1.5.10(transitive)
+ Addedweak-map@1.0.8(transitive)
+ Addedxmlbuilder@13.0.2(transitive)
- Removedts-retry@^2.4.1
- Removedts-retry@2.5.0(transitive)