Skip to content

Commit 8f05f1f

Browse files
timdeschryverbrandonroberts
authored andcommitted
feat(entity): log a warning message when selectId returns undefined in dev mode (#1169)
Closes #1133
1 parent 28e2cc9 commit 8f05f1f

File tree

4 files changed

+85
-7
lines changed

4 files changed

+85
-7
lines changed

modules/entity/spec/utils.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as ngCore from '@angular/core';
2+
import { selectIdValue } from '../src/utils';
3+
import { BookModel, AClockworkOrange } from './fixtures/book';
4+
5+
describe('Entity utils', () => {
6+
describe(`selectIdValue()`, () => {
7+
it('should not warn when key does exist', () => {
8+
const spy = spyOn(console, 'warn');
9+
10+
const key = selectIdValue(AClockworkOrange, book => book.id);
11+
12+
expect(spy).not.toHaveBeenCalled();
13+
});
14+
15+
it('should warn when key does not exist in dev mode', () => {
16+
const spy = spyOn(console, 'warn');
17+
18+
const key = selectIdValue(AClockworkOrange, (book: any) => book.foo);
19+
20+
expect(spy).toHaveBeenCalled();
21+
});
22+
23+
it('should warn when key is undefined in dev mode', () => {
24+
const spy = spyOn(console, 'warn');
25+
26+
const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined };
27+
const key = selectIdValue(
28+
undefinedAClockworkOrange,
29+
(book: any) => book.id
30+
);
31+
32+
expect(spy).toHaveBeenCalled();
33+
});
34+
35+
it('should not warn when key does not exist in prod mode', () => {
36+
spyOn(ngCore, 'isDevMode').and.returnValue(false);
37+
const spy = spyOn(console, 'warn');
38+
39+
const key = selectIdValue(AClockworkOrange, (book: any) => book.foo);
40+
41+
expect(spy).not.toHaveBeenCalled();
42+
});
43+
44+
it('should not warn when key is undefined in prod mode', () => {
45+
spyOn(ngCore, 'isDevMode').and.returnValue(false);
46+
const spy = spyOn(console, 'warn');
47+
48+
const undefinedAClockworkOrange = { ...AClockworkOrange, id: undefined };
49+
const key = selectIdValue(
50+
undefinedAClockworkOrange,
51+
(book: any) => book.id
52+
);
53+
54+
expect(spy).not.toHaveBeenCalled();
55+
});
56+
});
57+
});

modules/entity/src/sorted_state_adapter.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from './models';
99
import { createStateOperator, DidMutate } from './state_adapter';
1010
import { createUnsortedStateAdapter } from './unsorted_state_adapter';
11+
import { selectIdValue } from './utils';
1112

1213
export function createSortedStateAdapter<T>(
1314
selectId: IdSelector<T>,
@@ -28,7 +29,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
2829
function addManyMutably(newModels: T[], state: R): DidMutate;
2930
function addManyMutably(newModels: any[], state: any): DidMutate {
3031
const models = newModels.filter(
31-
model => !(selectId(model) in state.entities)
32+
model => !(selectIdValue(model, selectId) in state.entities)
3233
);
3334

3435
if (models.length === 0) {
@@ -62,7 +63,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
6263

6364
const original = state.entities[update.id];
6465
const updated = Object.assign({}, original, update.changes);
65-
const newKey = selectId(updated);
66+
const newKey = selectIdValue(updated, selectId);
6667

6768
delete state.entities[update.id];
6869

@@ -117,7 +118,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
117118
const updated: any[] = [];
118119

119120
for (const entity of entities) {
120-
const id = selectId(entity);
121+
const id = selectIdValue(entity, selectId);
121122
if (id in state.entities) {
122123
updated.push({ id, changes: entity });
123124
} else {
@@ -151,7 +152,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
151152

152153
while (i < models.length && j < state.ids.length) {
153154
const model = models[i];
154-
const modelId = selectId(model);
155+
const modelId = selectIdValue(model, selectId);
155156
const entityId = state.ids[j];
156157
const entity = state.entities[entityId];
157158

modules/entity/src/unsorted_state_adapter.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { EntityState, EntityStateAdapter, IdSelector, Update } from './models';
22
import { createStateOperator, DidMutate } from './state_adapter';
3+
import { selectIdValue } from './utils';
34

45
export function createUnsortedStateAdapter<T>(
56
selectId: IdSelector<T>
@@ -9,7 +10,7 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
910

1011
function addOneMutably(entity: T, state: R): DidMutate;
1112
function addOneMutably(entity: any, state: any): DidMutate {
12-
const key = selectId(entity);
13+
const key = selectIdValue(entity, selectId);
1314

1415
if (key in state.entities) {
1516
return DidMutate.None;
@@ -81,7 +82,7 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
8182
): boolean {
8283
const original = state.entities[update.id];
8384
const updated: T = Object.assign({}, original, update.changes);
84-
const newKey = selectId(updated);
85+
const newKey = selectIdValue(updated, selectId);
8586
const hasNewKey = newKey !== update.id;
8687

8788
if (hasNewKey) {
@@ -133,7 +134,7 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
133134
const updated: any[] = [];
134135

135136
for (const entity of entities) {
136-
const id = selectId(entity);
137+
const id = selectIdValue(entity, selectId);
137138
if (id in state.entities) {
138139
updated.push({ id, changes: entity });
139140
} else {

modules/entity/src/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { isDevMode } from '@angular/core';
2+
import { IdSelector } from './models';
3+
4+
export function selectIdValue<T>(entity: T, selectId: IdSelector<T>) {
5+
const key = selectId(entity);
6+
7+
if (isDevMode() && key === undefined) {
8+
console.warn(
9+
'@ngrx/entity: The entity passed to the `selectId` implementation returned undefined.',
10+
'You should probably provide your own `selectId` implementation.',
11+
'The entity that was passed:',
12+
entity,
13+
'The `selectId` implementation:',
14+
selectId.toString()
15+
);
16+
}
17+
18+
return key;
19+
}

0 commit comments

Comments
 (0)