Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Entity): Add support for string or number type for entity ID #441

Merged
merged 1 commit into from
Oct 5, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/entity/src/entity_state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EntityState } from './models';
import { EntityState, EntityStateStr, EntityStateNum } from './models';

export function getInitialEntityState<V>(): EntityState<V> {
return {
61 changes: 54 additions & 7 deletions modules/entity/src/models.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
export type Comparer<T> = {
export type ComparerStr<T> = {
(a: T, b: T): string;
};

export type ComparerNum<T> = {
(a: T, b: T): number;
};

export type IdSelector<T> = {
export type Comparer<T> = ComparerNum<T> | ComparerStr<T>;

export type IdSelectorStr<T> = {
(model: T): string;
};

export type Dictionary<T> = {
export type IdSelectorNum<T> = {
(model: T): number;
};

export type IdSelector<T> = IdSelectorStr<T> | IdSelectorNum<T>;

export type DictionaryStr<T> = {
[id: string]: T;
};

export type Update<T> = {
export type DictionaryNum<T> = {
[id: number]: T;
};

export type Dictionary<T> = DictionaryStr<T> | DictionaryNum<T>;

export type UpdateStr<T> = {
id: string;
changes: Partial<T>;
};

export interface EntityState<T> {
export type UpdateNum<T> = {
id: number;
changes: Partial<T>;
};

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

export interface EntityStateStr<T> {
ids: string[];
entities: Dictionary<T>;
}

export interface EntityStateNum<T> {
ids: number[];
entities: Dictionary<T>;
}

export type EntityState<T> = EntityStateStr<T> | EntityStateNum<T>;

export interface EntityDefinition<T> {
selectId: IdSelector<T>;
sortComparer: false | Comparer<T>;
@@ -31,20 +63,35 @@ export interface EntityStateAdapter<T> {
addAll<S extends EntityState<T>>(entities: T[], state: S): S;

removeOne<S extends EntityState<T>>(key: string, state: S): S;
removeOne<S extends EntityState<T>>(key: number, state: S): S;

removeMany<S extends EntityState<T>>(keys: string[], state: S): S;
removeMany<S extends EntityState<T>>(keys: number[], state: S): S;

removeAll<S extends EntityState<T>>(state: S): S;

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

export type EntitySelectors<T, V> = {
selectIds: (state: V) => string[];
export type EntitySelectorsBase<T, V> = {
selectEntities: (state: V) => Dictionary<T>;
selectAll: (state: V) => T[];
selectTotal: (state: V) => number;
};

export interface EntitySelectorsStr<T, V> extends EntitySelectorsBase<T, V> {
selectIds: (state: V) => string[];
}

export interface EntitySelectorsNum<T, V> extends EntitySelectorsBase<T, V> {
selectIds: (state: V) => number[];
}

export type EntitySelectors<T, V> =
| EntitySelectorsNum<T, V>
| EntitySelectorsStr<T, V>;

export interface EntityAdapter<T> extends EntityStateAdapter<T> {
getInitialState(): EntityState<T>;
getInitialState<S extends object>(state: S): EntityState<T> & S;
28 changes: 18 additions & 10 deletions modules/entity/src/sorted_state_adapter.ts
Original file line number Diff line number Diff line change
@@ -12,26 +12,30 @@ import { createUnsortedStateAdapter } from './unsorted_state_adapter';
export function createSortedStateAdapter<T>(
selectId: IdSelector<T>,
sort: Comparer<T>
): EntityStateAdapter<T> {
): EntityStateAdapter<T>;
export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
type R = EntityState<T>;

const { removeOne, removeMany, removeAll } = createUnsortedStateAdapter(
selectId
);

function addOneMutably(entity: T, state: R): boolean {
function addOneMutably(entity: T, state: R): boolean;
function addOneMutably(entity: any, state: any): boolean {
return addManyMutably([entity], state);
}

function addManyMutably(newModels: T[], state: R): boolean {
function addManyMutably(newModels: T[], state: R): boolean;
function addManyMutably(newModels: any[], state: any): boolean {
const models = newModels.filter(
model => !(selectId(model) in state.entities)
);

return merge(models, state);
}

function addAllMutably(models: T[], state: R): boolean {
function addAllMutably(models: T[], state: R): boolean;
function addAllMutably(models: any[], state: any): boolean {
state.entities = {};
state.ids = [];

@@ -40,11 +44,13 @@ export function createSortedStateAdapter<T>(
return true;
}

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

function takeUpdatedModel(models: T[], update: Update<T>, state: R): void {
function takeUpdatedModel(models: T[], update: Update<T>, state: R): void;
function takeUpdatedModel(models: any[], update: any, state: any): void {
if (!(update.id in state.entities)) {
return;
}
@@ -57,26 +63,28 @@ export function createSortedStateAdapter<T>(
models.push(updated);
}

function updateManyMutably(updates: Update<T>[], state: R): boolean {
function updateManyMutably(updates: Update<T>[], state: R): boolean;
function updateManyMutably(updates: any[], state: any): boolean {
const models: T[] = [];

updates.forEach(update => takeUpdatedModel(models, update, state));

if (models.length) {
state.ids = state.ids.filter(id => id in state.entities);
state.ids = state.ids.filter((id: any) => id in state.entities);
}

return merge(models, state);
}

function merge(models: T[], state: R): boolean {
function merge(models: T[], state: R): boolean;
function merge(models: any[], state: any): boolean {
if (models.length === 0) {
return false;
}

models.sort(sort);

const ids: string[] = [];
const ids: any[] = [];

let i = 0;
let j = 0;
7 changes: 5 additions & 2 deletions modules/entity/src/state_adapter.ts
Original file line number Diff line number Diff line change
@@ -2,8 +2,11 @@ import { EntityState, EntityStateAdapter } from './models';

export function createStateOperator<V, R>(
mutator: (arg: R, state: EntityState<V>) => boolean
) {
return function operation<S extends EntityState<V>>(arg: R, state: S): S {
): EntityState<V>;
export function createStateOperator<V, R>(
mutator: (arg: any, state: any) => boolean
): any {
return function operation<S extends EntityState<V>>(arg: R, state: any): S {
const clonedEntityState: EntityState<V> = {
ids: [...state.ids],
entities: { ...state.entities },
7 changes: 4 additions & 3 deletions modules/entity/src/state_selectors.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { createSelector } from '@ngrx/store';
import { EntityState, EntitySelectors } from './models';
import { EntityState, EntitySelectors, Dictionary } from './models';

export function createSelectorsFactory<T>() {
return {
getSelectors<V>(
selectState: (state: V) => EntityState<T>
): EntitySelectors<T, V> {
const selectIds = (state: EntityState<T>) => state.ids;
const selectIds = (state: any) => state.ids;
const selectEntities = (state: EntityState<T>) => state.entities;
const selectAll = createSelector(
selectIds,
selectEntities,
(ids, entities) => ids.map(id => entities[id])
(ids: T[], entities: Dictionary<T>): any =>
ids.map((id: any) => (entities as any)[id])
);

const selectTotal = createSelector(selectIds, ids => ids.length);
36 changes: 25 additions & 11 deletions modules/entity/src/unsorted_state_adapter.ts
Original file line number Diff line number Diff line change
@@ -3,10 +3,12 @@ import { createStateOperator } from './state_adapter';

export function createUnsortedStateAdapter<T>(
selectId: IdSelector<T>
): EntityStateAdapter<T> {
): EntityStateAdapter<T>;
export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
type R = EntityState<T>;

function addOneMutably(entity: T, state: R): boolean {
function addOneMutably(entity: T, state: R): boolean;
function addOneMutably(entity: any, state: any): boolean {
const key = selectId(entity);

if (key in state.entities) {
@@ -19,7 +21,8 @@ export function createUnsortedStateAdapter<T>(
return true;
}

function addManyMutably(entities: T[], state: R): boolean {
function addManyMutably(entities: T[], state: R): boolean;
function addManyMutably(entities: any[], state: any): boolean {
let didMutate = false;

for (let index in entities) {
@@ -29,7 +32,8 @@ export function createUnsortedStateAdapter<T>(
return didMutate;
}

function addAllMutably(entities: T[], state: R): boolean {
function addAllMutably(entities: T[], state: R): boolean;
function addAllMutably(entities: any[], state: any): boolean {
state.ids = [];
state.entities = {};

@@ -38,24 +42,27 @@ export function createUnsortedStateAdapter<T>(
return true;
}

function removeOneMutably(key: string, state: R): boolean {
function removeOneMutably(key: T, state: R): boolean;
function removeOneMutably(key: any, state: any): boolean {
return removeManyMutably([key], state);
}

function removeManyMutably(keys: string[], state: R): boolean {
function removeManyMutably(keys: T[], state: R): boolean;
function removeManyMutably(keys: any[], state: any): boolean {
const didMutate =
keys
.filter(key => key in state.entities)
.map(key => delete state.entities[key]).length > 0;

if (didMutate) {
state.ids = state.ids.filter(id => id in state.entities);
state.ids = state.ids.filter((id: any) => id in state.entities);
}

return didMutate;
}

function removeAll<S extends R>(state: S): S {
function removeAll<S extends R>(state: S): S;
function removeAll<S extends R>(state: any): S {
return Object.assign({}, state, {
ids: [],
entities: {},
@@ -66,6 +73,11 @@ export function createUnsortedStateAdapter<T>(
keys: { [id: string]: string },
update: Update<T>,
state: R
): void;
function takeNewKey(
keys: { [id: string]: any },
update: Update<T>,
state: any
): void {
const original = state.entities[update.id];
const updated: T = Object.assign({}, original, update.changes);
@@ -79,11 +91,13 @@ export function createUnsortedStateAdapter<T>(
state.entities[newKey] = updated;
}

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

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

const didMutate =
@@ -92,7 +106,7 @@ export function createUnsortedStateAdapter<T>(
.map(update => takeNewKey(newKeys, update, state)).length > 0;

if (didMutate) {
state.ids = state.ids.map(id => newKeys[id] || id);
state.ids = state.ids.map((id: any) => newKeys[id] || id);
}

return didMutate;