Skip to content

Commit

Permalink
feat(Entity): add updateMany by predicate
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver committed Sep 14, 2018
1 parent ec38181 commit 149042b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 7 deletions.
30 changes: 29 additions & 1 deletion modules/entity/spec/sorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ describe('Sorted State Adapter', () => {
});
});

it('should let you update many entities in the state', () => {
it('should let you update many entities by id in the state', () => {
const firstChange = { title: 'Zack' };
const secondChange = { title: 'Aaron' };
const withMany = adapter.addAll([TheGreatGatsby, AClockworkOrange], state);
Expand Down Expand Up @@ -292,6 +292,34 @@ describe('Sorted State Adapter', () => {
});
});

it('should let you update many entities by predicate in the state', () => {
const change = { title: 'Changed titled' };
const withMany = adapter.addAll(
[TheGreatGatsby, AClockworkOrange, AnimalFarm],
state
);

const withUpdates = adapter.updateMany(
{ predicate: p => p.id.startsWith('a'), changes: change },
withMany
);

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

it('should let you add one entity to the state with upsert()', () => {
const withOneEntity = adapter.upsertOne(TheGreatGatsby, state);
expect(withOneEntity).toEqual({
Expand Down
30 changes: 29 additions & 1 deletion modules/entity/spec/unsorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ describe('Unsorted State Adapter', () => {
});
});

it('should let you update many entities in the state', () => {
it('should let you update many entities by id in the state', () => {
const firstChange = { title: 'First Change' };
const secondChange = { title: 'Second Change' };
const withMany = adapter.addAll([TheGreatGatsby, AClockworkOrange], state);
Expand Down Expand Up @@ -232,6 +232,34 @@ describe('Unsorted State Adapter', () => {
});
});

it('should let you update many entities by predicate in the state', () => {
const change = { title: 'Changed titled' };
const withMany = adapter.addAll(
[TheGreatGatsby, AClockworkOrange, AnimalFarm],
state
);

const withUpdates = adapter.updateMany(
{ predicate: p => p.id.startsWith('a'), changes: change },
withMany
);

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

it('should let you add one entity to the state with upsert()', () => {
const withOneEntity = adapter.upsertOne(TheGreatGatsby, state);
expect(withOneEntity).toEqual({
Expand Down
10 changes: 10 additions & 0 deletions modules/entity/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ export type UpdateNum<T> = {

export type Update<T> = UpdateStr<T> | UpdateNum<T>;

export type Predicate<T> = {
(entity: T): boolean;
};

export type UpdatePredicate<T> = {
predicate: Predicate<T>;
changes: Partial<T>;
};

export interface EntityState<T> {
ids: string[] | number[];
entities: Dictionary<T>;
Expand All @@ -63,6 +72,7 @@ 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;
updateMany<S extends EntityState<T>>(update: UpdatePredicate<T>, state: S): S;

upsertOne<S extends EntityState<T>>(entity: T, state: S): S;
upsertMany<S extends EntityState<T>>(entities: T[], state: S): S;
Expand Down
28 changes: 27 additions & 1 deletion modules/entity/src/sorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Dictionary,
EntityStateAdapter,
Update,
UpdatePredicate,
} from './models';
import { createStateOperator, DidMutate } from './state_adapter';
import { createUnsortedStateAdapter } from './unsorted_state_adapter';
Expand Down Expand Up @@ -72,9 +73,34 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
return newKey !== update.id;
}

function decideIdsToUpdate(updates: Update<T>[], state: R): Update<T>[];
function decideIdsToUpdate(
updates: UpdatePredicate<T>,
state: R
): Update<T>[];
function decideIdsToUpdate(
updatesOrPredicate: any[] | any,
state: any
): any[] {
if (updatesOrPredicate instanceof Array) {
return updatesOrPredicate;
} else {
const { predicate, changes } = updatesOrPredicate;
const idsdToChange = state.ids.filter((key: string | number) =>
predicate(state.entities[key])
);
return idsdToChange.map((id: string | number) => ({ id, changes }));
}
}

function updateManyMutably(updates: Update<T>[], state: R): DidMutate;
function updateManyMutably(updates: any[], state: any): DidMutate {
function updateManyMutably(update: UpdatePredicate<T>, state: R): DidMutate;
function updateManyMutably(
updatesOrPredicate: any[] | any,
state: any
): DidMutate {
const models: T[] = [];
const updates = decideIdsToUpdate(updatesOrPredicate, state);

const didMutateIds =
updates.filter(update => takeUpdatedModel(models, update, state)).length >
Expand Down
39 changes: 35 additions & 4 deletions modules/entity/src/unsorted_state_adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { EntityState, EntityStateAdapter, IdSelector, Update } from './models';
import {
EntityState,
EntityStateAdapter,
IdSelector,
Update,
UpdatePredicate,
} from './models';
import { createStateOperator, DidMutate } from './state_adapter';
import { selectIdValue } from './utils';

Expand Down Expand Up @@ -95,16 +101,41 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
return hasNewKey;
}

function decideIdsToUpdate(updates: Update<T>[], state: R): Update<T>[];
function decideIdsToUpdate(
updates: UpdatePredicate<T>,
state: R
): Update<T>[];
function decideIdsToUpdate(
updatesOrPredicate: any[] | any,
state: any
): any[] {
if (updatesOrPredicate instanceof Array) {
return updatesOrPredicate;
} else {
const { predicate, changes } = updatesOrPredicate;
const idsdToChange = state.ids.filter((key: string | number) =>
predicate(state.entities[key])
);
return idsdToChange.map((id: string | number) => ({ id, changes }));
}
}

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

function updateManyMutably(updates: Update<T>[], state: R): DidMutate;
function updateManyMutably(updates: any[], state: any): DidMutate {
function updateManyMutably(update: UpdatePredicate<T>, state: R): DidMutate;
function updateManyMutably(
updatesOrPredicate: any[] | any,
state: any
): DidMutate {
const newKeys: { [id: string]: string } = {};

updates = updates.filter(update => update.id in state.entities);
const updates = decideIdsToUpdate(updatesOrPredicate, state).filter(
({ id }: { id: number | string }) => id in state.entities
);

const didMutateEntities = updates.length > 0;

Expand Down

0 comments on commit 149042b

Please sign in to comment.