Skip to content

Commit daa4d65

Browse files
committed
Implemented first pass at caching
1 parent 2058432 commit daa4d65

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

packages/data-connect/src/cache.ts

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
enum FieldType {
2+
scalar,
3+
customobject
4+
}
5+
interface Field {
6+
name: string;
7+
type: FieldType;
8+
}
9+
interface Scalar {
10+
fields: Field[];
11+
}
12+
13+
type TypeInformation = Scalar;
14+
15+
type NativeScalar = string | number | boolean | null;
16+
17+
interface ResultTree {
18+
[name: string]: StubObject | NativeScalar | Omit<StubObject[], 'typedKey'>;
19+
}
20+
21+
22+
interface BackingObjectListenerMap {
23+
[name: string]: StubObject[];
24+
}
25+
26+
type FDCScalarValue = string | number | boolean | null;
27+
28+
class BackingDataObject {
29+
typedKey: string;
30+
private serverValues = new Map<string, FDCScalarValue>();
31+
private listeners: BackingObjectListenerMap = {};
32+
updateFromServer(value: FDCScalarValue, key: string) {
33+
this.serverValues[key] = value;
34+
this.notifyListeners(key);
35+
}
36+
listenTo(keys: string[], listener: StubObject) {
37+
for (const key of keys) {
38+
if (this.listeners[key]) {
39+
this.listeners[key].push(listener);
40+
} else {
41+
this.listeners[key] = [listener];
42+
}
43+
}
44+
}
45+
notifyListeners(key: string) {
46+
if (this.listeners[key]) {
47+
this.listeners[key].forEach(listener =>
48+
listener.updateValue(key, this.serverValues[key])
49+
);
50+
}
51+
}
52+
53+
serverValue(key: string): FDCScalarValue {
54+
return this.serverValues[key];
55+
}
56+
toJson() {
57+
const toReturn: object = {};
58+
for (const [key, value] of this.serverValues) {
59+
toReturn[key] = value;
60+
}
61+
return toReturn;
62+
}
63+
}
64+
/**
65+
* StubObject: field: 'movies',
66+
* StubObject: Array of Movies:
67+
* StubObject(movie0)
68+
* BackingDataObject(movie0)
69+
*/
70+
71+
// interface StubObject {
72+
// [name: string]: FDCScalarValue;
73+
// }
74+
/**
75+
* StubObject({
76+
* 'movies': StubArray([
77+
* StubObject(BDO),
78+
* StubObject(BDO)
79+
* ])
80+
* })
81+
*/
82+
83+
class StubArray {
84+
_valueMap: (FDCScalarValue | StubObject)[] = [];
85+
constructor(
86+
private _values: FDCScalarValue[] | object[],
87+
private _bdoHandler: BackingDataObjectHandler
88+
) {
89+
for (const value of _values) {
90+
if (typeof value === 'object') {
91+
// TODO: What about arrays?
92+
const stubObject = new StubObject(value as CachedObject, _bdoHandler); // What about instances where there's no typename?
93+
this._valueMap.push(stubObject);
94+
} else {
95+
this._valueMap.push(value);
96+
}
97+
}
98+
}
99+
value(): (FDCScalarValue | object)[] {
100+
return this._valueMap.map(v => {
101+
// print
102+
if (v instanceof StubObject) {
103+
return v.toJson();
104+
} else {
105+
return v;
106+
}
107+
});
108+
}
109+
}
110+
111+
class BackingDataObjectHandler {
112+
private backingDataObjects = new Map<string, BackingDataObject>();
113+
114+
getOrCreate(typedKey: string): BackingDataObject {
115+
if (!this.backingDataObjects.has(typedKey)) {
116+
this.backingDataObjects.set(typedKey, new BackingDataObject());
117+
}
118+
return this.backingDataObjects.get(typedKey)!;
119+
}
120+
}
121+
122+
interface CachedObject {
123+
__typename: string;
124+
[field: string]: FDCScalarValue | CachedObject | FDCScalarValue[] | CachedObject[];
125+
}
126+
127+
class StubObject {
128+
constructor(
129+
value: CachedObject,
130+
public bdoHandler: BackingDataObjectHandler
131+
) {
132+
// TODO: Register value with typename system
133+
for(const [k, v] of Object.entries(value)) {
134+
if(typeof v === 'object') {
135+
this.addObject(k, v);
136+
} else {
137+
this.addField(k, v);
138+
}
139+
}
140+
}
141+
// TODO: Check whether object is right here.
142+
map: Map<string, BackingDataObject | FDCScalarValue | StubObject | StubArray> = new Map();
143+
144+
parentListeners: Map<
145+
string,
146+
((value: FDCScalarValue | FDCScalarValue[]) => void)[]
147+
> = new Map();
148+
149+
updateValue(key: string, value: FDCScalarValue | FDCScalarValue[]) {
150+
// TODO: Propagate to other listeners.
151+
// this.map.set(key, value);
152+
// if (this.parentListeners.has(key)) {
153+
// this.parentListeners.get(key).forEach(listener => listener(value));
154+
// }
155+
}
156+
157+
addField(key: string, value: FDCScalarValue) {
158+
this.map.set(key, value);
159+
}
160+
161+
addObject(key: string, value: object | FDCScalarValue) {
162+
/**
163+
* Every key maps to a backing data object.
164+
*/
165+
if(Array.isArray(value)) {
166+
this.map.set(key, new StubArray(value, this.bdoHandler));
167+
} else if(typeof value === 'object') {
168+
// Field is an object
169+
const toListenTo: string[] = [];
170+
for(const [k, v] of Object.entries(value)) {
171+
if(typeof v === 'object') {
172+
const stubObject = new StubObject(v, this.bdoHandler);
173+
this.map.set(k, stubObject);
174+
} else {
175+
const bdo = this.bdoHandler.getOrCreate((value as CachedObject).__typename as string);
176+
if('__typename' in value && typeof v !== 'object') {
177+
toListenTo.push(k);
178+
console.log('bdo for ' + k);
179+
this.map.set(k, bdo);
180+
bdo.updateFromServer(v as FDCScalarValue, k);
181+
}
182+
}
183+
}
184+
if(toListenTo.length > 0) {
185+
const bdo = this.bdoHandler.getOrCreate((value as CachedObject).__typename as string);
186+
bdo.listenTo(toListenTo, this);
187+
}
188+
} else {
189+
// TODO: throw an error
190+
throw new Error('Add Object called on non-object');
191+
}
192+
}
193+
194+
toJson(): object {
195+
const toReturn: object = {};
196+
for (const [key, value] of this.map) {
197+
let retV;
198+
if (value instanceof StubObject) {
199+
retV = value.toJson();
200+
} else if (value instanceof StubArray) {
201+
retV = value.value();
202+
} else if(value instanceof BackingDataObject) {
203+
retV = value.serverValue(key);
204+
} else {
205+
retV = value;
206+
}
207+
toReturn[key] = retV;
208+
}
209+
return toReturn;
210+
}
211+
}
212+
213+
const stubDataObject = new StubObject({
214+
__typename: '__moviesArray',
215+
movies: [
216+
{
217+
__typename: 'abc',
218+
id: 'abc',
219+
title: 'abc',
220+
nestedField: {
221+
a: 'abc',
222+
b: 'def',
223+
__typename: 'nestedField'
224+
},
225+
reviews: [
226+
{ __typename: 'abc', id: 'def', title: 'review!' }
227+
]
228+
}
229+
]
230+
},
231+
new BackingDataObjectHandler()
232+
);

0 commit comments

Comments
 (0)