Skip to content

Commit 6e8a17b

Browse files
committed
wip
1 parent 842e5f4 commit 6e8a17b

File tree

2 files changed

+141
-20
lines changed

2 files changed

+141
-20
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { EventListener, EventName } from '../../../src/api';
2+
import { EventStream, Payload, PayloadReader } from '../../../src/internal/fdv2/payloadReader';
3+
4+
class MockEventStreamm implements EventStream {
5+
private _listeners: {
6+
[event: EventName]: EventListener;
7+
} = {};
8+
9+
addEventListener(eventName: EventName, listener: EventListener): void {
10+
this._listeners[eventName] = listener;
11+
}
12+
13+
simulateEvent(eventName: EventName, event: { data?: string }) {
14+
this._listeners[eventName](event);
15+
}
16+
}
17+
18+
it('it sets basis to true when intent code is xfer-full', () => {
19+
const mockStream = new MockEventStreamm();
20+
const receivedPayloads: Payload[] = [];
21+
const readerUnderTest = new PayloadReader(mockStream, {
22+
mockKind: (it) => it, // obj processor that just returns the same obj
23+
});
24+
readerUnderTest.addPayloadListener((it) => {
25+
receivedPayloads.push(it);
26+
});
27+
28+
mockStream.simulateEvent('server-intent', {
29+
data: '{"data": {"payloads": [{"intentCode": "xfer-full", "id": "mockId"}]}}',
30+
});
31+
mockStream.simulateEvent('payload-transferred', {
32+
data: '{"data": {"state": "mockState", "version": 1}}',
33+
});
34+
expect(receivedPayloads.length).toEqual(1);
35+
expect(receivedPayloads[0].id).toEqual('mockId');
36+
expect(receivedPayloads[0].state).toEqual('mockState');
37+
expect(receivedPayloads[0].basis).toEqual(true);
38+
});
39+
40+
it('it sets basis to false when intent code is xfer-changes', () => {
41+
const mockStream = new MockEventStreamm();
42+
const receivedPayloads: Payload[] = [];
43+
const readerUnderTest = new PayloadReader(mockStream, {
44+
mockKind: (it) => it, // obj processor that just returns the same obj
45+
});
46+
readerUnderTest.addPayloadListener((it) => {
47+
receivedPayloads.push(it);
48+
});
49+
50+
mockStream.simulateEvent('server-intent', {
51+
data: '{"data": {"payloads": [{"intentCode": "xfer-changes", "id": "mockId"}]}}',
52+
});
53+
mockStream.simulateEvent('payload-transferred', {
54+
data: '{"data": {"state": "mockState", "version": 1}}',
55+
});
56+
expect(receivedPayloads.length).toEqual(1);
57+
expect(receivedPayloads[0].id).toEqual('mockId');
58+
expect(receivedPayloads[0].state).toEqual('mockState');
59+
expect(receivedPayloads[0].basis).toEqual(false);
60+
});
61+
62+
it('it includes multiple types of updates in payload', () => {
63+
const mockStream = new MockEventStreamm();
64+
const receivedPayloads: Payload[] = [];
65+
const readerUnderTest = new PayloadReader(mockStream, {
66+
mockKind: (it) => it, // obj processor that just returns the same obj
67+
});
68+
readerUnderTest.addPayloadListener((it) => {
69+
receivedPayloads.push(it);
70+
});
71+
72+
mockStream.simulateEvent('server-intent', {
73+
data: '{"data": {"payloads": [{"intentCode": "xfer-full", "id": "mockId"}]}}',
74+
});
75+
mockStream.simulateEvent('put-object', {
76+
data: '{"data": {"kind": "mockKind", "key": "flagA", "version": 123, "object": {"objectFieldA": "objectValueA"}}}',
77+
});
78+
mockStream.simulateEvent('delete-object', {
79+
data: '{"data": {"kind": "mockKind", "key": "flagB", "version": 123, "object": {"objectFieldB": "objectValueB"}}}',
80+
});
81+
mockStream.simulateEvent('put-object', {
82+
data: '{"data": {"kind": "mockKind", "key": "flagC", "version": 123, "object": {"objectFieldC": "objectValueC"}}}',
83+
});
84+
mockStream.simulateEvent('payload-transferred', {
85+
data: '{"data": {"state": "mockState", "version": 1}}',
86+
});
87+
expect(receivedPayloads.length).toEqual(1);
88+
expect(receivedPayloads[0].id).toEqual('mockId');
89+
expect(receivedPayloads[0].state).toEqual('mockState');
90+
expect(receivedPayloads[0].basis).toEqual(true);
91+
expect(receivedPayloads[0].updates.length).toEqual(3);
92+
expect(receivedPayloads[0].updates[0].object).toEqual({ objectFieldA: 'objectValueA' });
93+
expect(receivedPayloads[0].updates[0].deleted).toEqual(false); // TODO: resume at deciding if deleted should be optional (it is at the moment and causing this to fail)
94+
expect(receivedPayloads[0].updates[1].object).toEqual({ objectFieldB: 'objectValueB' });
95+
expect(receivedPayloads[0].updates[1].deleted).toEqual(true);
96+
expect(receivedPayloads[0].updates[2].object).toEqual({ objectFieldC: 'objectValueC' });
97+
});
98+
99+
// it('it does not include messages thats are not between server-intent and payloader-transferred', () => {
100+
101+
// });
102+
103+
// it('logs prescribed message when goodbye event is encountered', () => {
104+
105+
// });
106+
107+
// it('logs prescribed message when error event is encountered', () => {
108+
109+
// });
110+
111+
// it('discards partially transferred data when an error is encountered', () => {
112+
113+
// });
114+
115+
// it('silently ignores unrecognized kinds', () => {
116+
117+
// });
118+
119+
// it('ignores additional payloads beyond the first payload in the server-intent message', () => {
120+
121+
// });

packages/shared/common/src/internal/fdv2/payloadReader.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ export class PayloadReader {
3636
tempUpdates: Update[] = [];
3737

3838
constructor(
39-
eventSource: EventStream,
39+
eventStream: EventStream,
4040
private readonly _jsonObjConverters: JsonObjConverters,
4141
private readonly _errorHandler?: (errorKind: DataSourceErrorKind, message: string) => void,
4242
private readonly _logger?: LDLogger,
4343
) {
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);
44+
this._attachHandler(eventStream, 'server-intent', this._processServerIntent);
45+
this._attachHandler(eventStream, 'put-object', this._processPutObject);
46+
this._attachHandler(eventStream, 'delete-object', this._processDeleteObject);
47+
this._attachHandler(eventStream, 'payload-transferred', this._processPayloadTransferred);
4848
}
4949

5050
addPayloadListener(listener: PayloadListener) {
@@ -79,8 +79,8 @@ export class PayloadReader {
7979
});
8080
}
8181

82-
private _convertJsonObj(jsonObj: any): any {
83-
return this._jsonObjConverters[jsonObj.kind]?.(jsonObj);
82+
private _processObj(kind: string, jsonObj: any): any {
83+
return this._jsonObjConverters[kind]?.(jsonObj);
8484
}
8585

8686
// 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
@@ -111,53 +111,53 @@ export class PayloadReader {
111111
this.tempId = payload?.id;
112112
};
113113

114-
private _processPutObject = (jsonObj: any) => {
114+
private _processPutObject = (event?: { data?: DataObject }) => {
115115
// if the following properties haven't been provided by now, we're in an invalid state
116-
if (!jsonObj.kind || !jsonObj.key || !jsonObj.version || !jsonObj.object) {
116+
if (!event?.data?.kind || !event?.data?.key || !event?.data?.version || !event?.data?.object) {
117117
this._resetState();
118118
return;
119119
}
120120

121-
const obj = this._convertJsonObj(jsonObj);
121+
const obj = this._processObj(event.data.kind, event.data.object);
122122
if (!obj) {
123123
// ignore unrecognized kinds
124124
return;
125125
}
126126

127127
this.tempUpdates.push({
128-
kind: jsonObj.kind,
129-
key: jsonObj.key,
130-
version: jsonObj.version,
128+
kind: event.data.kind,
129+
key: event.data.key,
130+
version: event.data.version,
131131
object: obj,
132132
// intentionally omit deleted for this put
133133
});
134134
};
135135

136-
private _processDeleteObject = (jsonObj: any) => {
136+
private _processDeleteObject = (event?: { data?: DataObject }) => {
137137
// if the following properties haven't been provided by now, we're in an invalid state
138-
if (!jsonObj.kind || !jsonObj.key || !jsonObj.version || !jsonObj.object) {
138+
if (!event?.data?.kind || !event?.data?.key || !event?.data?.version || !event?.data?.object) {
139139
this._resetState();
140140
return;
141141
}
142142

143-
const obj = this._convertJsonObj(jsonObj);
143+
const obj = this._processObj(event.data.kind, event.data.object);
144144
if (!obj) {
145145
// ignore unrecognized kinds
146146
return;
147147
}
148148

149149
this.tempUpdates.push({
150-
kind: jsonObj.kind,
151-
key: jsonObj.key,
152-
version: jsonObj.version,
150+
kind: event.data.kind,
151+
key: event.data.key,
152+
version: event.data.version,
153153
object: obj,
154154
deleted: true,
155155
});
156156
};
157157

158158
private _processPayloadTransferred = (event?: { data?: PayloadTransferred }) => {
159159
// if the following properties haven't been provided by now, we're in an invalid state
160-
if (!event?.data?.state || !event.data.version || !this.tempBasis) {
160+
if (!event?.data?.state || !event.data.version || this.tempBasis === undefined) {
161161
this._resetState();
162162
return;
163163
}

0 commit comments

Comments
 (0)