Skip to content

Commit

Permalink
feat(Entity): Add upsertOne and upsertMany functions to entity adapte…
Browse files Browse the repository at this point in the history
…rs (#780)

Closes #421
  • Loading branch information
sandangel authored and MikeRyanDev committed Feb 6, 2018
1 parent c11504f commit f871540
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 1 deletion.
70 changes: 69 additions & 1 deletion modules/entity/spec/sorted_state_adapter.spec.ts
@@ -1,4 +1,4 @@
import { EntityStateAdapter, EntityState } from '../src/models';
import { EntityStateAdapter, EntityState, Update } from '../src/models';
import { createEntityAdapter } from '../src/create_adapter';
import {
BookModel,
Expand Down Expand Up @@ -277,4 +277,72 @@ describe('Sorted State Adapter', () => {
},
});
});

it('should let you add one entity to the state with upsert()', () => {
const withOneEntity = adapter.upsertOne(
{
id: TheGreatGatsby.id,
changes: TheGreatGatsby,
},
state
);

expect(withOneEntity).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: TheGreatGatsby,
},
});
});

it('should let you update an entity in the state with upsert()', () => {
const withOne = adapter.addOne(TheGreatGatsby, state);
const changes = { title: 'A New Hope' };

const withUpdates = adapter.upsertOne(
{
id: TheGreatGatsby.id,
changes,
},
withOne
);

expect(withUpdates).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...changes,
},
},
});
});

it('should let you upsert many entities in the state', () => {
const firstChange = { title: 'Zack' };
const secondChange = { title: 'Aaron' };
const withMany = adapter.addAll([TheGreatGatsby], state);

const withUpserts = adapter.upsertMany(
[
{ id: TheGreatGatsby.id, changes: firstChange },
{ id: AClockworkOrange.id, changes: secondChange },
],
withMany
);

expect(withUpserts).toEqual({
ids: [AClockworkOrange.id, TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...firstChange,
},
[AClockworkOrange.id]: {
...AClockworkOrange,
...secondChange,
},
},
});
});
});
68 changes: 68 additions & 0 deletions modules/entity/spec/unsorted_state_adapter.spec.ts
Expand Up @@ -217,4 +217,72 @@ describe('Unsorted State Adapter', () => {
},
});
});

it('should let you add one entity to the state with upsert()', () => {
const withOneEntity = adapter.upsertOne(
{
id: TheGreatGatsby.id,
changes: TheGreatGatsby,
},
state
);

expect(withOneEntity).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: TheGreatGatsby,
},
});
});

it('should let you update an entity in the state with upsert()', () => {
const withOne = adapter.addOne(TheGreatGatsby, state);
const changes = { title: 'A New Hope' };

const withUpdates = adapter.upsertOne(
{
id: TheGreatGatsby.id,
changes,
},
withOne
);

expect(withUpdates).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...changes,
},
},
});
});

it('should let you upsert many entities in the state', () => {
const firstChange = { title: 'First Change' };
const secondChange = { title: 'Second Change' };
const withMany = adapter.addAll([TheGreatGatsby], state);

const withUpserts = adapter.upsertMany(
[
{ id: TheGreatGatsby.id, changes: firstChange },
{ id: AClockworkOrange.id, changes: secondChange },
],
withMany
);

expect(withUpserts).toEqual({
ids: [TheGreatGatsby.id, AClockworkOrange.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...firstChange,
},
[AClockworkOrange.id]: {
...AClockworkOrange,
...secondChange,
},
},
});
});
});
3 changes: 3 additions & 0 deletions modules/entity/src/models.ts
Expand Up @@ -63,6 +63,9 @@ export interface EntityStateAdapter<T> {

updateOne<S extends EntityState<T>>(update: Update<T>, state: S): S;
updateMany<S extends EntityState<T>>(updates: Update<T>[], state: S): S;

upsertOne<S extends EntityState<T>>(update: Update<T>, state: S): S;
upsertMany<S extends EntityState<T>>(updates: Update<T>[], state: S): S;
}

export type EntitySelectors<T, V> = {
Expand Down
39 changes: 39 additions & 0 deletions modules/entity/src/sorted_state_adapter.ts
Expand Up @@ -106,6 +106,43 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
}
}

function upsertOneMutably(update: Update<T>, state: R): DidMutate;
function upsertOneMutably(update: any, state: any): DidMutate {
return upsertManyMutably([update], state);
}

function upsertManyMutably(updates: Update<T>[], state: R): DidMutate;
function upsertManyMutably(updates: any[], state: any): DidMutate {
const added: T[] = [];
const updated: Update<T>[] = [];

for (let index in updates) {
const update = updates[index];
if (update.id in state.entities) {
updated.push(update);
} else {
added.push({
...update.changes,
id: update.id,
});
}
}

const didMutateByUpdated = updateManyMutably(updated, state);
const didMutateByAdded = addManyMutably(added, state);

switch (true) {
case didMutateByAdded === DidMutate.None &&
didMutateByUpdated === DidMutate.None:
return DidMutate.None;
case didMutateByAdded === DidMutate.Both ||
didMutateByUpdated === DidMutate.Both:
return DidMutate.Both;
default:
return DidMutate.EntitiesOnly;
}
}

function merge(models: T[], state: R): void;
function merge(models: any[], state: any): void {
models.sort(sort);
Expand Down Expand Up @@ -147,8 +184,10 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
removeAll,
addOne: createStateOperator(addOneMutably),
updateOne: createStateOperator(updateOneMutably),
upsertOne: createStateOperator(upsertOneMutably),
addAll: createStateOperator(addAllMutably),
addMany: createStateOperator(addManyMutably),
updateMany: createStateOperator(updateManyMutably),
upsertMany: createStateOperator(upsertManyMutably),
};
}
39 changes: 39 additions & 0 deletions modules/entity/src/unsorted_state_adapter.ts
Expand Up @@ -123,13 +123,52 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
return DidMutate.None;
}

function upsertOneMutably(update: Update<T>, state: R): DidMutate;
function upsertOneMutably(update: any, state: any): DidMutate {
return upsertManyMutably([update], state);
}

function upsertManyMutably(updates: Update<T>[], state: R): DidMutate;
function upsertManyMutably(updates: any[], state: any): DidMutate {
const added: T[] = [];
const updated: any[] = [];

for (let index in updates) {
const update = updates[index];
if (update.id in state.entities) {
updated.push(update);
} else {
added.push({
...update.changes,
id: update.id,
});
}
}

const didMutateByUpdated = updateManyMutably(updated, state);
const didMutateByAdded = addManyMutably(added, state);

switch (true) {
case didMutateByAdded === DidMutate.None &&
didMutateByUpdated === DidMutate.None:
return DidMutate.None;
case didMutateByAdded === DidMutate.Both ||
didMutateByUpdated === DidMutate.Both:
return DidMutate.Both;
default:
return DidMutate.EntitiesOnly;
}
}

return {
removeAll,
addOne: createStateOperator(addOneMutably),
addMany: createStateOperator(addManyMutably),
addAll: createStateOperator(addAllMutably),
updateOne: createStateOperator(updateOneMutably),
updateMany: createStateOperator(updateManyMutably),
upsertOne: createStateOperator(upsertOneMutably),
upsertMany: createStateOperator(upsertManyMutably),
removeOne: createStateOperator(removeOneMutably),
removeMany: createStateOperator(removeManyMutably),
};
Expand Down

0 comments on commit f871540

Please sign in to comment.