1- import { EventListener , EventName } from '../../api' ;
1+ /* eslint-disable no-underscore-dangle */
2+ import { EventListener , EventName , LDLogger } from '../../api' ;
3+ import { DataSourceErrorKind } from '../../datasource' ;
24import { DataObject , PayloadTransferred , ServerIntentData } from './proto' ;
35
46// Facade interface to contain only ability to add event listeners
57export interface EventStream {
68 addEventListener ( type : EventName , listener : EventListener ) : void ;
79}
810
11+ export interface JsonObjConverters {
12+ [ kind : string ] : ( object : any ) => any ;
13+ }
14+
915export interface Update extends DataObject {
1016 deleted ?: boolean ;
1117}
@@ -29,13 +35,16 @@ export class PayloadReader {
2935 tempBasis ?: boolean = undefined ;
3036 tempUpdates : Update [ ] = [ ] ;
3137
32- constructor ( eventSource : EventStream , listeners : PayloadListener [ ] ) {
33- this . listeners = listeners . concat ( listeners ) ;
34-
35- eventSource . addEventListener ( 'server-intent' , this . _processServerIntent ) ;
36- eventSource . addEventListener ( 'put-object' , this . _processPutObject ) ;
37- eventSource . addEventListener ( 'delete-object' , this . _processDeleteObject ) ;
38- eventSource . addEventListener ( 'payload-transferred' , this . _processPayloadTransferred ) ;
38+ constructor (
39+ eventSource : EventStream ,
40+ private readonly _jsonObjConverters : JsonObjConverters ,
41+ private readonly _errorHandler ?: ( errorKind : DataSourceErrorKind , message : string ) => void ,
42+ private readonly _logger ?: LDLogger ,
43+ ) {
44+ this . _attachHandler ( eventSource , 'server-intent' , this . _processServerIntent ) ;
45+ this . _attachHandler ( eventSource , 'put-object' , this . _processPutObject ) ;
46+ this . _attachHandler ( eventSource , 'delete-object' , this . _processDeleteObject ) ;
47+ this . _attachHandler ( eventSource , 'payload-transferred' , this . _processPayloadTransferred ) ;
3948 }
4049
4150 addPayloadListener ( listener : PayloadListener ) {
@@ -49,6 +58,31 @@ export class PayloadReader {
4958 }
5059 }
5160
61+ private _attachHandler ( stream : EventStream , eventName : string , processor : ( obj : any ) => void ) {
62+ stream . addEventListener ( eventName , async ( event ?: { data ?: string } ) => {
63+ if ( event ?. data ) {
64+ this . _logger ?. debug ( `Received ${ eventName } event` ) ;
65+
66+ try {
67+ processor ( JSON . parse ( event . data ) ) ;
68+ } catch {
69+ this . _logger ?. error ( `Stream received invalid data in "${ eventName } " message` ) ;
70+ this . _logger ?. debug ( `Invalid JSON follows: ${ event . data } ` ) ;
71+ this . _errorHandler ?.(
72+ DataSourceErrorKind . InvalidData ,
73+ 'Malformed JSON data in event stream' ,
74+ ) ;
75+ }
76+ } else {
77+ this . _errorHandler ?.( DataSourceErrorKind . Unknown , 'Unexpected payload from event stream' ) ;
78+ }
79+ } ) ;
80+ }
81+
82+ private _convertJsonObj ( jsonObj : any ) : any {
83+ return this . _jsonObjConverters [ jsonObj . kind ] ?.( jsonObj ) ;
84+ }
85+
5286 // TODO: add valid state/reset handling if an invalid message is received part way through processing and to avoid starting prcessing put/deletes before server intent is received
5387 private _processServerIntent = ( event ?: { data ?: ServerIntentData } ) => {
5488 // clear state in prep for handling data
@@ -77,35 +111,46 @@ export class PayloadReader {
77111 this . tempId = payload ?. id ;
78112 } ;
79113
80- private _processPutObject = ( event ?: { data ?: DataObject } ) => {
114+ private _processPutObject = ( jsonObj : any ) => {
81115 // if the following properties haven't been provided by now, we're in an invalid state
82- if ( ! event ?. data ?. kind || ! event . data . key || ! event . data . version || ! event . data . object ) {
116+ if ( ! jsonObj . kind || ! jsonObj . key || ! jsonObj . version || ! jsonObj . object ) {
83117 this . _resetState ( ) ;
84118 return ;
85119 }
86120
121+ const obj = this . _convertJsonObj ( jsonObj ) ;
122+ if ( ! obj ) {
123+ // ignore unrecognized kinds
124+ return ;
125+ }
126+
87127 this . tempUpdates . push ( {
88- kind : event . data . kind ,
89- key : event . data . key ,
90- object : event . data . object ,
91- version : event . data . version ,
128+ kind : jsonObj . kind ,
129+ key : jsonObj . key ,
130+ version : jsonObj . version ,
131+ object : obj ,
92132 // intentionally omit deleted for this put
93133 } ) ;
94134 } ;
95135
96- // TODO: consider merging put and delete and having param for delete logic
97- private _processDeleteObject = ( event ?: { data ?: DataObject } ) => {
136+ private _processDeleteObject = ( jsonObj : any ) => {
98137 // if the following properties haven't been provided by now, we're in an invalid state
99- if ( ! event ?. data ?. kind || ! event . data . key || ! event . data . version || ! event . data . object ) {
138+ if ( ! jsonObj . kind || ! jsonObj . key || ! jsonObj . version || ! jsonObj . object ) {
100139 this . _resetState ( ) ;
101140 return ;
102141 }
103142
143+ const obj = this . _convertJsonObj ( jsonObj ) ;
144+ if ( ! obj ) {
145+ // ignore unrecognized kinds
146+ return ;
147+ }
148+
104149 this . tempUpdates . push ( {
105- kind : event . data . kind ,
106- key : event . data . key ,
107- object : event . data . object ,
108- version : event . data . version ,
150+ kind : jsonObj . kind ,
151+ key : jsonObj . key ,
152+ version : jsonObj . version ,
153+ object : obj ,
109154 deleted : true ,
110155 } ) ;
111156 } ;
0 commit comments