Skip to content

Commit

Permalink
static mergeStrategy method on Resource
Browse files Browse the repository at this point in the history
Will clone the existing entity, and assign the new value to only the fields that should be overridden.
  • Loading branch information
skoging committed Mar 16, 2019
1 parent f280078 commit 4b36fa1
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
46 changes: 46 additions & 0 deletions src/resource/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ const getEntitySchema: <T extends typeof Resource>(M: T) => schemas.Entity<Abstr
return e;
},
) as any;

function filterObject<T extends object>(
obj: T,
predicate: (key: string, value: any) => boolean,
): Partial<T> {
return Object.keys(obj)
.filter(key => predicate(key, obj[key as keyof T]))
.reduce(
(ret, key) => {
ret[key as keyof T] = obj[key as keyof T];
return ret;
},
{} as Partial<T>
);
}

/** Represents an entity to be retrieved from a server. Typically 1:1 with a url endpoint. */
export default abstract class Resource {
// typescript todo: require subclasses to implement
Expand Down Expand Up @@ -55,6 +71,36 @@ export default abstract class Resource {
return instance;
}

/** Merge strategy used when merging normalized response entities into existing entities in cache. */
static mergeStrategy<T extends typeof Resource>(
this: T,
a: Partial<AbstractInstanceType<T>>,
b: Partial<AbstractInstanceType<T>>,
shouldOverrideField: (
fieldname: string,
newValue: any,
oldValue: any,
) => boolean = (k, n, o) => n !== undefined && n !== o,
) {
const bToMerge = filterObject(b, (k, v) =>
shouldOverrideField(k, v, a[k as keyof AbstractInstanceType<T>])
);

if (Object.keys(bToMerge).length === 0) {
// Nothing to merge to a
return a;
}
if (Object.keys(bToMerge).length === Object.keys(b).length) {
// Nothing to keep from a
return b;
}

const aClone = this.fromJS(a);
const merged = Object.assign(aClone, bToMerge);

return merged;
}

static toString<T extends typeof Resource>(this: T) {
return `${this.name}::${this.urlRoot}`;
}
Expand Down
6 changes: 3 additions & 3 deletions src/state/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ type Writable<T> = {
}

function resourceCustomizer(a: any, b: any): any {
if (b instanceof Resource) {
const merged = mergeWith({ ...a }, b, resourceCustomizer);
return Object.assign(b, merged);
if ((b instanceof Resource) && a !== undefined && b !== undefined) {
const S = b.constructor as typeof Resource;
return S.mergeStrategy(a, b);
}
}

Expand Down

0 comments on commit 4b36fa1

Please sign in to comment.