Skip to content

Commit

Permalink
feat(ref-imp): #989 - added event emitter support
Browse files Browse the repository at this point in the history
  • Loading branch information
thehenrytsai authored Dec 19, 2020
1 parent fa7a4fd commit 099e52a
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 11 deletions.
2 changes: 1 addition & 1 deletion lib/bitcoin/BitcoinClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ export default class BitcoinClient {
private static createBitcoinOutputModel (bitcoreOutput: Transaction.Output): BitcoinOutputModel {
return {
satoshis: bitcoreOutput.satoshis,
// Some transaction outputs do not have a script, such as coinbase transactions.
scriptAsmAsString: bitcoreOutput.script ? bitcoreOutput.script.toASM() : ''
};
}
Expand All @@ -742,7 +743,6 @@ export default class BitcoinClient {

private async getUnspentOutputs (address: Address): Promise<Transaction.UnspentOutput[]> {

// Retrieve all transactions by addressToSearch via BCoin Node API /tx/address/$address endpoint
const addressToSearch = address.toString();
Logger.info(`Getting unspent coins for ${addressToSearch}`);
const request = {
Expand Down
31 changes: 31 additions & 0 deletions lib/common/EventEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import IEventEmitter from './interfaces/IEventEmitter';
import LogColor from './LogColor';

/**
* Event emitter used in Sidetree.
* Intended to be machine readable for triggering custom handlers.
*/
export default class EventEmitter {
// Default to basic console log.
private static singleton: IEventEmitter = {
emit: async (eventCode) => {
console.log(LogColor.lightBlue(`Event emitted: ${LogColor.green(eventCode)}`));
}
};

/**
* Overrides the default event emitter if given.
*/
static initialize (customEventEmitter?: IEventEmitter) {
if (customEventEmitter !== undefined) {
EventEmitter.singleton = customEventEmitter;
}
}

/**
* Emits an event.
*/
public static async emit (eventName: string, eventData?: {[property: string]: any}): Promise<void> {
await EventEmitter.singleton.emit(eventName, eventData);
}
}
1 change: 1 addition & 0 deletions lib/common/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ILogger from './interfaces/ILogger';

/**
* Logger used in Sidetree.
* Intended to be human readable for debugging.
*/
export default class Logger {
private static singleton: ILogger = new ConsoleLogger();
Expand Down
9 changes: 9 additions & 0 deletions lib/common/interfaces/IEventEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Custom event emitter interface.
*/
export default interface IEventEmitter {
/**
* Emits an event.
*/
emit (eventName: string, eventData?: {[property: string]: any}): Promise<void>;
}
2 changes: 1 addition & 1 deletion lib/common/interfaces/ILogger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Logging interface used in Sidetree.
* Custom logger interface.
*/
export default interface ILogger {
/**
Expand Down
4 changes: 4 additions & 0 deletions lib/core/BatchScheduler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as timeSpan from 'time-span';
import EventCode from './EventCode';
import EventEmitter from '../common/EventEmitter';
import IBlockchain from './interfaces/IBlockchain';
import IVersionManager from './interfaces/IVersionManager';
import Logger from '../common/Logger';
Expand Down Expand Up @@ -50,6 +52,8 @@ export default class BatchScheduler {
const batchWriter = this.versionManager.getBatchWriter(currentTime);

await batchWriter.write();

EventEmitter.emit(EventCode.BatchWriterProcessingLoopSuccess);
} catch (error) {
Logger.error('Unexpected and unhandled error during batch writing, investigate and fix:');
Logger.error(error);
Expand Down
6 changes: 4 additions & 2 deletions lib/core/Core.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as timeSpan from 'time-span';
import { ISidetreeCas, ISidetreeLogger } from '..';
import { ISidetreeCas, ISidetreeEventEmitter, ISidetreeLogger } from '..';
import BatchScheduler from './BatchScheduler';
import Blockchain from './Blockchain';
import Config from './models/Config';
import DownloadManager from './DownloadManager';
import EventEmitter from '../common/EventEmitter';
import LogColor from '../common/LogColor';
import Logger from '../common/Logger';
import MongoDbOperationStore from './MongoDbOperationStore';
Expand Down Expand Up @@ -66,8 +67,9 @@ export default class Core {
* The initialization method that must be called before consumption of this core object.
* The method starts the Observer and Batch Writer.
*/
public async initialize (customLogger?: ISidetreeLogger) {
public async initialize (customLogger?: ISidetreeLogger, customEventEmitter?: ISidetreeEventEmitter) {
Logger.initialize(customLogger);
EventEmitter.initialize(customEventEmitter);

// DB initializations.
await this.serviceStateStore.initialize();
Expand Down
7 changes: 7 additions & 0 deletions lib/core/EventCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Event codes used by Sidetree core service.
*/
export default {
BatchWriterProcessingLoopSuccess: 'batch_writer_processing_loop_success',
ObserverProcessingLoopSuccess: 'observer_processing_loop_success'
};
6 changes: 5 additions & 1 deletion lib/core/Observer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as timeSpan from 'time-span';
import TransactionUnderProcessingModel, { TransactionProcessingStatus } from './models/TransactionUnderProcessingModel';
import EventCode from './EventCode';
import EventEmitter from '../common/EventEmitter';
import IBlockchain from './interfaces/IBlockchain';
import IOperationStore from './interfaces/IOperationStore';
import ITransactionProcessor from './interfaces/ITransactionProcessor';
Expand Down Expand Up @@ -121,7 +123,7 @@ export default class Observer {

// NOTE: Blockchain reorg has happened for sure only if `invalidTransactionNumberOrTimeHash` AND
// latest transaction time is less or equal to blockchain service time.
// This check will prevent Core from reverting transactions if/when blockchain service is reinitializing its data itself.
// This check will prevent Core from reverting transactions if/when blockchain service is re-initializing its data itself.
let blockReorganizationDetected = false;
if (invalidTransactionNumberOrTimeHash) {
if (lastKnownTransactionTime <= this.blockchain.approximateTime.time) {
Expand Down Expand Up @@ -161,6 +163,8 @@ export default class Observer {

// Continue onto processing unresolvable transactions if any.
await this.processUnresolvableTransactions();

EventEmitter.emit(EventCode.ObserverProcessingLoopSuccess);
} catch (error) {
Logger.error(`Encountered unhandled and possibly fatal Observer error, must investigate and fix:`);
Logger.error(error);
Expand Down
2 changes: 2 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ISidetreeBitcoinConfig from './bitcoin/IBitcoinConfig';
import ISidetreeBitcoinWallet from './bitcoin/interfaces/IBitcoinWallet';
import ISidetreeCas from './core/interfaces/ICas';
import ISidetreeEventEmitter from './common/interfaces/IEventEmitter';
import ISidetreeLogger from './common/interfaces/ILogger';
import SidetreeBitcoinProcessor from './bitcoin/BitcoinProcessor';
import SidetreeBitcoinVersionModel from './bitcoin/models/BitcoinVersionModel';
Expand Down Expand Up @@ -32,5 +33,6 @@ export {

// Common exports.
export {
ISidetreeEventEmitter,
ISidetreeLogger
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
"lib/bitcoin/versions/[0-9]**/**",
"lib/core/versions/[0-9]**/**",
"lib/core/versions/**/VersionMetadata.ts",
"lib/**/**ErrorCode.ts"
"lib/**/**ErrorCode.ts",
"lib/**/**EventCode.ts"
],
"reporter": [
"text",
Expand Down
20 changes: 15 additions & 5 deletions tests/core/Core.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Config from '../../lib/core/models/Config';
import Core from '../../lib/core/Core';
import EventEmitter from '../../lib/common/EventEmitter';
import IRequestHandler from '../../lib/core/interfaces/IRequestHandler';
import Logger from '../../lib/common/Logger';
import MockCas from '../mocks/MockCas';
Expand Down Expand Up @@ -58,7 +59,7 @@ describe('Core', async () => {
expect(downloadManagerStartSpy).toHaveBeenCalled();
});

it('should override the default logger if custom logger is given.', async () => {
it('should override the default logger/event emitter if custom logger/event emitter is given.', async () => {
const core = new Core(testConfig, testVersionConfig, mockCas);

spyOn(core['serviceStateStore'], 'initialize');
Expand All @@ -75,17 +76,26 @@ describe('Core', async () => {

let customLoggerInvoked = false;
const customLogger = {
info: (_data: any) => { customLoggerInvoked = true; },
warn: (_data: any) => { },
error: (_data: any) => { }
info: () => { customLoggerInvoked = true; },
warn: () => { },
error: () => { }
};

await core.initialize(customLogger);
let customEvenEmitterInvoked = false;
const customEvenEmitter = {
emit: async () => { customEvenEmitterInvoked = true; }
};

await core.initialize(customLogger, customEvenEmitter);

// Invoke logger to trigger the custom logger's method defined above.
Logger.info('anything');

// Invoke event emitter to trigger the custom emitter's method defined above.
await EventEmitter.emit('anything');

expect(customLoggerInvoked).toBeTruthy();
expect(customEvenEmitterInvoked).toBeTruthy();
});

it('should not start the Batch Writer and Observer if they are disabled.', async () => {
Expand Down

0 comments on commit 099e52a

Please sign in to comment.