Skip to content

Commit 713d533

Browse files
authored
Added injectable websocket to repotFactor.createListener() (#583)
* Fixed #582 * Moved injectedWebsocket to RepoFactory's constructor * Fixed #581 - Changed listener to use NewBlock rather than sharing with blockInfo * Added missing stateHashSubCacheMerkleRoots * Fixed #584 - removed unused class * Added test * - Added webSocketUrl in RepoFactory - Moved optional params in RepoFactory to RepositoryFactoryConfig interface - Changed Listener to use actual ws url instead of rest url. * fixed doc * removed console.log * Fixed bug in listener
1 parent b706ada commit 713d533

File tree

13 files changed

+472
-33
lines changed

13 files changed

+472
-33
lines changed

src/infrastructure/BlockHttp.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class BlockHttp extends Http implements BlockRepository {
5555
* @returns Observable<BlockInfo>
5656
*/
5757
public getBlockByHeight(height: UInt64): Observable<BlockInfo> {
58-
return this.call(this.blockRoutesApi.getBlockByHeight(height.toString()), (body) => BlockHttp.toBlockInfo(body));
58+
return this.call(this.blockRoutesApi.getBlockByHeight(height.toString()), (body) => this.toBlockInfo(body));
5959
}
6060

6161
/**
@@ -87,7 +87,7 @@ export class BlockHttp extends Http implements BlockRepository {
8787
*/
8888
public getBlocksByHeightWithLimit(height: UInt64, limit: number): Observable<BlockInfo[]> {
8989
return this.call(this.blockRoutesApi.getBlocksByHeightWithLimit(height.toString(), limit), (body) =>
90-
body.map((blockDTO) => BlockHttp.toBlockInfo(blockDTO)),
90+
body.map((blockDTO) => this.toBlockInfo(blockDTO)),
9191
);
9292
}
9393

@@ -98,12 +98,13 @@ export class BlockHttp extends Http implements BlockRepository {
9898
* @param {BlockInfoDTO} dto the dto object from rest.
9999
* @returns {BlockInfo} a BlockInfo model
100100
*/
101-
public static toBlockInfo(dto: BlockInfoDTO): BlockInfo {
101+
private toBlockInfo(dto: BlockInfoDTO): BlockInfo {
102102
const networkType = dto.block.network.valueOf();
103103
return new BlockInfo(
104104
dto.meta.hash,
105105
dto.meta.generationHash,
106106
UInt64.fromNumericString(dto.meta.totalFee),
107+
dto.meta.stateHashSubCacheMerkleRoots,
107108
dto.meta.numTransactions,
108109
dto.block.signature,
109110
PublicAccount.createFromPublicKey(dto.block.signerPublicKey, networkType),

src/infrastructure/IListener.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616

1717
import { Observable } from 'rxjs';
1818
import { Address } from '../model/account/Address';
19-
import { BlockInfo } from '../model/blockchain/BlockInfo';
2019
import { AggregateTransaction } from '../model/transaction/AggregateTransaction';
2120
import { CosignatureSignedTransaction } from '../model/transaction/CosignatureSignedTransaction';
2221
import { Transaction } from '../model/transaction/Transaction';
2322
import { TransactionStatusError } from '../model/transaction/TransactionStatusError';
23+
import { NewBlock } from '../model/blockchain/NewBlock';
2424

2525
/**
2626
* Listener service
2727
*/
2828
export interface IListener {
29+
/**
30+
* Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws)
31+
*/
32+
url: string;
2933
/**
3034
* Open web socket connection.
3135
* @returns Promise<Void>
@@ -49,9 +53,9 @@ export interface IListener {
4953
* Each time a new Block is added into the blockchain,
5054
* it emits a new BlockInfo in the event stream.
5155
*
52-
* @return an observable stream of BlockInfo
56+
* @return an observable stream of NewBlock
5357
*/
54-
newBlock(): Observable<BlockInfo>;
58+
newBlock(): Observable<NewBlock>;
5559

5660
/**
5761
* Returns an observable stream of Transaction for a specific address.

src/infrastructure/Listener.ts

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ import { Observable, of, OperatorFunction, Subject } from 'rxjs';
1818
import { filter, flatMap, map, share } from 'rxjs/operators';
1919
import * as WebSocket from 'ws';
2020
import { Address } from '../model/account/Address';
21-
import { BlockInfo } from '../model/blockchain/BlockInfo';
2221
import { NamespaceName } from '../model/namespace/NamespaceName';
2322
import { AggregateTransaction } from '../model/transaction/AggregateTransaction';
2423
import { CosignatureSignedTransaction } from '../model/transaction/CosignatureSignedTransaction';
2524
import { Deadline } from '../model/transaction/Deadline';
2625
import { Transaction } from '../model/transaction/Transaction';
2726
import { TransactionStatusError } from '../model/transaction/TransactionStatusError';
28-
import { BlockHttp } from './BlockHttp';
2927
import { IListener } from './IListener';
3028
import { NamespaceRepository } from './NamespaceRepository';
3129
import { CreateTransactionFromDTO } from './transaction/CreateTransactionFromDTO';
30+
import { BlockInfoDTO } from 'symbol-openapi-typescript-node-client/dist/model/blockInfoDTO';
31+
import { NewBlock } from '../model/blockchain/NewBlock';
32+
import { PublicAccount } from '../model/account/PublicAccount';
33+
import { UInt64 } from '../model/UInt64';
3234

3335
export enum ListenerChannelName {
3436
block = 'block',
@@ -44,14 +46,13 @@ export enum ListenerChannelName {
4446

4547
interface ListenerMessage {
4648
readonly channelName: ListenerChannelName;
47-
readonly message: Transaction | string | BlockInfo | TransactionStatusError | CosignatureSignedTransaction;
49+
readonly message: Transaction | string | NewBlock | TransactionStatusError | CosignatureSignedTransaction;
4850
}
4951

5052
/**
5153
* Listener service
5254
*/
5355
export class Listener implements IListener {
54-
public readonly url: string;
5556
/**
5657
* @internal
5758
* WebSocket connector
@@ -70,14 +71,15 @@ export class Listener implements IListener {
7071

7172
/**
7273
* Constructor
73-
* @param config - Listener configuration
74-
* @param websocketInjected - (Optional) WebSocket injected when using listeners in client
74+
* @param url - Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws).
75+
* @param namespaceRepository - NamespaceRepository interface for resolving alias.
76+
* @param websocketInjected - (Optional) WebSocket injected when using listeners in client.
7577
*/
7678
constructor(
7779
/**
78-
* Listener configuration.
80+
* Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws)
7981
*/
80-
private config: string,
82+
public readonly url: string,
8183
/**
8284
* Namespace repository for resolving account alias
8385
*/
@@ -87,8 +89,7 @@ export class Listener implements IListener {
8789
*/
8890
private websocketInjected?: any,
8991
) {
90-
this.config = config.replace(/\/$/, '');
91-
this.url = `${this.config}/ws`;
92+
this.url = url.replace(/\/$/, '');
9293
this.messageSubject = new Subject();
9394
}
9495

@@ -139,7 +140,7 @@ export class Listener implements IListener {
139140
} else if (message.block) {
140141
this.messageSubject.next({
141142
channelName: ListenerChannelName.block,
142-
message: BlockHttp.toBlockInfo(message),
143+
message: this.toNewBlock(message),
143144
});
144145
} else if (message.code) {
145146
this.messageSubject.next({
@@ -192,13 +193,13 @@ export class Listener implements IListener {
192193
*
193194
* @return an observable stream of BlockInfo
194195
*/
195-
public newBlock(): Observable<BlockInfo> {
196+
public newBlock(): Observable<NewBlock> {
196197
this.subscribeTo('block');
197198
return this.messageSubject.asObservable().pipe(
198199
share(),
199200
filter((_) => _.channelName === ListenerChannelName.block),
200-
filter((_) => _.message instanceof BlockInfo),
201-
map((_) => _.message as BlockInfo),
201+
filter((_) => _.message instanceof NewBlock),
202+
map((_) => _.message as NewBlock),
202203
);
203204
}
204205

@@ -406,4 +407,36 @@ export class Listener implements IListener {
406407
};
407408
this.webSocket.send(JSON.stringify(subscriptionMessage));
408409
}
410+
411+
/**
412+
* This method maps a BlockInfoDTO from rest to the SDK's BlockInfo model object.
413+
*
414+
* @internal
415+
* @param {BlockInfoDTO} dto the dto object from rest.
416+
* @returns {NewBlock} a BlockInfo model
417+
*/
418+
private toNewBlock(dto: BlockInfoDTO): NewBlock {
419+
const networkType = dto.block.network.valueOf();
420+
return new NewBlock(
421+
dto.meta.hash,
422+
dto.meta.generationHash,
423+
dto.block.signature,
424+
PublicAccount.createFromPublicKey(dto.block.signerPublicKey, networkType),
425+
networkType,
426+
dto.block.version,
427+
dto.block.type,
428+
UInt64.fromNumericString(dto.block.height),
429+
UInt64.fromNumericString(dto.block.timestamp),
430+
UInt64.fromNumericString(dto.block.difficulty),
431+
dto.block.feeMultiplier,
432+
dto.block.previousBlockHash,
433+
dto.block.transactionsHash,
434+
dto.block.receiptsHash,
435+
dto.block.stateHash,
436+
dto.block.proofGamma,
437+
dto.block.proofScalar,
438+
dto.block.proofVerificationHash,
439+
dto.block.beneficiaryPublicKey ? PublicAccount.createFromPublicKey(dto.block.beneficiaryPublicKey, networkType) : undefined,
440+
);
441+
}
409442
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2020 NEM
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { NetworkType } from '../model/network/NetworkType';
18+
19+
export interface RepositoryFactoryConfig {
20+
/**
21+
* optional network type if you don't want to load it from the server.
22+
*/
23+
networkType?: NetworkType;
24+
/**
25+
* optional node generation hash if you don't want to load it from the server.
26+
*/
27+
generationHash?: string;
28+
/**
29+
* optional websocket url. If not provided, Default: Rest-Gateway url with ''/ws'' suffix (e.g. http://localhost:3000/ws).
30+
*/
31+
websocketUrl?: string;
32+
/**
33+
* optional injected websocket instance when using listeners in client.
34+
*/
35+
websocketInjected?: any;
36+
}

src/infrastructure/RepositoryFactoryHttp.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { RestrictionMosaicHttp } from './RestrictionMosaicHttp';
4646
import { RestrictionMosaicRepository } from './RestrictionMosaicRepository';
4747
import { TransactionHttp } from './TransactionHttp';
4848
import { TransactionRepository } from './TransactionRepository';
49+
import { RepositoryFactoryConfig } from './RepositoryFactoryConfig';
4950

5051
/**
5152
* Receipt http repository.
@@ -55,22 +56,27 @@ export class RepositoryFactoryHttp implements RepositoryFactory {
5556
private readonly url: string;
5657
private readonly networkType: Observable<NetworkType>;
5758
private readonly generationHash: Observable<string>;
59+
private readonly websocketUrl: string;
60+
private readonly websocketInjected?: any;
5861

5962
/**
6063
* Constructor
6164
* @param url the server url.
62-
* @param networkType optional network type if you don't want to load it from the server.
63-
* @param generationHash optional node generation hash if you don't want to load it from the server.
65+
* @param configs optional repository factory configs
6466
*/
65-
constructor(url: string, networkType?: NetworkType, generationHash?: string) {
67+
constructor(url: string, configs?: RepositoryFactoryConfig) {
6668
this.url = url;
67-
this.networkType = networkType ? observableOf(networkType) : this.createNetworkRepository().getNetworkType().pipe(shareReplay(1));
68-
this.generationHash = generationHash
69-
? observableOf(generationHash)
69+
this.networkType = configs?.networkType
70+
? observableOf(configs.networkType)
71+
: this.createNetworkRepository().getNetworkType().pipe(shareReplay(1));
72+
this.generationHash = configs?.generationHash
73+
? observableOf(configs?.generationHash)
7074
: this.createNodeRepository()
7175
.getNodeInfo()
7276
.pipe(map((b) => b.networkGenerationHashSeed))
7377
.pipe(shareReplay(1));
78+
this.websocketUrl = configs?.websocketUrl ? configs?.websocketUrl : `${url.replace(/\/$/, '')}/ws`;
79+
this.websocketInjected = configs?.websocketInjected;
7480
}
7581

7682
createAccountRepository(): AccountRepository {
@@ -134,6 +140,6 @@ export class RepositoryFactoryHttp implements RepositoryFactory {
134140
}
135141

136142
createListener(): IListener {
137-
return new Listener(this.url, this.createNamespaceRepository());
143+
return new Listener(this.websocketUrl, this.createNamespaceRepository(), this.websocketInjected);
138144
}
139145
}

src/model/blockchain/BlockInfo.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class BlockInfo {
2626
* @param hash
2727
* @param generationHash
2828
* @param totalFee
29+
* @param stateHashSubCacheMerkleRoots
2930
* @param numTransactions
3031
* @param signature
3132
* @param signer
@@ -59,6 +60,10 @@ export class BlockInfo {
5960
* The sum of all transaction fees included in the block.
6061
*/
6162
public readonly totalFee: UInt64,
63+
/**
64+
* State hash sub cache merkle roots
65+
*/
66+
public readonly stateHashSubCacheMerkleRoots: string[],
6267
/**
6368
* The number of transactions included.
6469
*/

0 commit comments

Comments
 (0)