-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(entities): Introduce @ngrx/entities
- Loading branch information
1 parent
7f77693
commit f2d708b
Showing
22 changed files
with
1,032 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
@ngrx/entity | ||
======= | ||
|
||
The sources for this package are in the main [ngrx/platform](https://github.com/ngrx/platform) repo. Please file issues and pull requests against that repo. | ||
|
||
License: MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* DO NOT EDIT | ||
* | ||
* This file is automatically generated at build | ||
*/ | ||
|
||
export * from './public_api'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "@ngrx/entity", | ||
"version": "4.0.1", | ||
"description": "Common utilities for entity reducers", | ||
"module": "@ngrx/entity.es5.js", | ||
"es2015": "@ngrx/entity.js", | ||
"main": "bundles/entity.umd.js", | ||
"typings": "entity.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/ngrx/platform.git" | ||
}, | ||
"authors": [ | ||
"Mike Ryan" | ||
], | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"@angular/core": "^4.0.0", | ||
"@ngrx/store": "^4.0.0", | ||
"rxjs": "^5.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './src/index'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export default { | ||
entry: './dist/entity/@ngrx/entity.es5.js', | ||
dest: './dist/entity/bundles/entity.umd.js', | ||
format: 'umd', | ||
exports: 'named', | ||
moduleName: 'ngrx.entity', | ||
globals: { | ||
'@ngrx/store': 'ngrx.store' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { createEntityAdapter, EntityAdapter } from '../src'; | ||
import { BookModel } from './fixtures/book'; | ||
|
||
describe('Entity State', () => { | ||
let adapter: EntityAdapter<BookModel>; | ||
|
||
beforeEach(() => { | ||
adapter = createEntityAdapter({ | ||
selectId: (book: BookModel) => book.id, | ||
}); | ||
}); | ||
|
||
it('should let you get the initial state', () => { | ||
const initialState = adapter.getInitialState(); | ||
|
||
expect(initialState).toEqual({ | ||
ids: [], | ||
entities: {}, | ||
}); | ||
}); | ||
|
||
it('should let you provide additional initial state properties', () => { | ||
const additionalProperties = { isHydrated: true }; | ||
|
||
const initialState = adapter.getInitialState(additionalProperties); | ||
|
||
expect(initialState).toEqual({ | ||
...additionalProperties, | ||
ids: [], | ||
entities: {}, | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const deepFreeze = require('deep-freeze'); | ||
|
||
export interface BookModel { | ||
id: string; | ||
title: string; | ||
} | ||
|
||
export const AClockworkOrange: BookModel = deepFreeze({ | ||
id: 'aco', | ||
title: 'A Clockwork Orange', | ||
}); | ||
|
||
export const AnimalFarm: BookModel = deepFreeze({ | ||
id: 'af', | ||
title: 'Animal Farm', | ||
}); | ||
|
||
export const TheGreatGatsby: BookModel = deepFreeze({ | ||
id: 'tgg', | ||
title: 'The Great Gatsby', | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
import { EntityStateAdapter, EntityState } from '../src/models'; | ||
import { createEntityAdapter } from '../src/create_adapter'; | ||
import { | ||
BookModel, | ||
TheGreatGatsby, | ||
AClockworkOrange, | ||
AnimalFarm, | ||
} from './fixtures/book'; | ||
|
||
describe('Sorted State Adapter', () => { | ||
let adapter: EntityStateAdapter<BookModel>; | ||
let state: EntityState<BookModel>; | ||
|
||
beforeEach(() => { | ||
adapter = createEntityAdapter({ | ||
selectId: (book: BookModel) => book.id, | ||
sort: (a, b) => a.title.localeCompare(b.title), | ||
}); | ||
|
||
state = { ids: [], entities: {} }; | ||
}); | ||
|
||
it('should let you add one entity to the state', () => { | ||
const withOneEntity = adapter.addOne(TheGreatGatsby, state); | ||
|
||
expect(withOneEntity).toEqual({ | ||
ids: [TheGreatGatsby.id], | ||
entities: { | ||
[TheGreatGatsby.id]: TheGreatGatsby, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should not change state if you attempt to re-add an entity', () => { | ||
const withOneEntity = adapter.addOne(TheGreatGatsby, state); | ||
|
||
const readded = adapter.addOne(TheGreatGatsby, withOneEntity); | ||
|
||
expect(readded).toEqual(withOneEntity); | ||
}); | ||
|
||
it('should let you add many entities to the state', () => { | ||
const withOneEntity = adapter.addOne(TheGreatGatsby, state); | ||
|
||
const withManyMore = adapter.addMany( | ||
[AClockworkOrange, AnimalFarm], | ||
withOneEntity | ||
); | ||
|
||
expect(withManyMore).toEqual({ | ||
ids: [AClockworkOrange.id, AnimalFarm.id, TheGreatGatsby.id], | ||
entities: { | ||
[TheGreatGatsby.id]: TheGreatGatsby, | ||
[AClockworkOrange.id]: AClockworkOrange, | ||
[AnimalFarm.id]: AnimalFarm, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should let you add all entities to the state', () => { | ||
const withOneEntity = adapter.addOne(TheGreatGatsby, state); | ||
|
||
const withAll = adapter.addAll( | ||
[AClockworkOrange, AnimalFarm], | ||
withOneEntity | ||
); | ||
|
||
expect(withAll).toEqual({ | ||
ids: [AClockworkOrange.id, AnimalFarm.id], | ||
entities: { | ||
[AClockworkOrange.id]: AClockworkOrange, | ||
[AnimalFarm.id]: AnimalFarm, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should let you add remove an entity from the state', () => { | ||
const withOneEntity = adapter.addOne(TheGreatGatsby, state); | ||
|
||
const withoutOne = adapter.removeOne(TheGreatGatsby.id, state); | ||
|
||
expect(withoutOne).toEqual({ | ||
ids: [], | ||
entities: {}, | ||
}); | ||
}); | ||
|
||
it('should let you remove many entities from the state', () => { | ||
const withAll = adapter.addAll( | ||
[TheGreatGatsby, AClockworkOrange, AnimalFarm], | ||
state | ||
); | ||
|
||
const withoutMany = adapter.removeMany( | ||
[TheGreatGatsby.id, AClockworkOrange.id], | ||
withAll | ||
); | ||
|
||
expect(withoutMany).toEqual({ | ||
ids: [AnimalFarm.id], | ||
entities: { | ||
[AnimalFarm.id]: AnimalFarm, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should let you remove all entities from the state', () => { | ||
const withAll = adapter.addAll( | ||
[TheGreatGatsby, AClockworkOrange, AnimalFarm], | ||
state | ||
); | ||
|
||
const withoutAll = adapter.removeAll(withAll); | ||
|
||
expect(withoutAll).toEqual({ | ||
ids: [], | ||
entities: {}, | ||
}); | ||
}); | ||
|
||
it('should let you update an entity in the state', () => { | ||
const withOne = adapter.addOne(TheGreatGatsby, state); | ||
const changes = { title: 'A New Hope' }; | ||
|
||
const withUpdates = adapter.updateOne( | ||
{ | ||
id: TheGreatGatsby.id, | ||
changes, | ||
}, | ||
withOne | ||
); | ||
|
||
expect(withUpdates).toEqual({ | ||
ids: [TheGreatGatsby.id], | ||
entities: { | ||
[TheGreatGatsby.id]: { | ||
...TheGreatGatsby, | ||
...changes, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should not change state if you attempt to update an entity that has not been added', () => { | ||
const withUpdates = adapter.updateOne( | ||
{ | ||
id: TheGreatGatsby.id, | ||
changes: { title: 'A New Title' }, | ||
}, | ||
state | ||
); | ||
|
||
expect(withUpdates).toEqual(state); | ||
}); | ||
|
||
it('should let you update the id of entity', () => { | ||
const withOne = adapter.addOne(TheGreatGatsby, state); | ||
const changes = { id: 'A New Id' }; | ||
|
||
const withUpdates = adapter.updateOne( | ||
{ | ||
id: TheGreatGatsby.id, | ||
changes, | ||
}, | ||
withOne | ||
); | ||
|
||
expect(withUpdates).toEqual({ | ||
ids: [changes.id], | ||
entities: { | ||
[changes.id]: { | ||
...TheGreatGatsby, | ||
...changes, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should resort correctly if the id and sort key update', () => { | ||
const withOne = adapter.addAll( | ||
[TheGreatGatsby, AnimalFarm, AClockworkOrange], | ||
state | ||
); | ||
const changes = { id: 'A New Id', title: AnimalFarm.title }; | ||
|
||
const withUpdates = adapter.updateOne( | ||
{ | ||
id: TheGreatGatsby.id, | ||
changes, | ||
}, | ||
withOne | ||
); | ||
|
||
expect(withUpdates).toEqual({ | ||
ids: [AClockworkOrange.id, changes.id, AnimalFarm.id], | ||
entities: { | ||
[AClockworkOrange.id]: AClockworkOrange, | ||
[changes.id]: { | ||
...TheGreatGatsby, | ||
...changes, | ||
}, | ||
[AnimalFarm.id]: AnimalFarm, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should let you update many entities in the state', () => { | ||
const firstChange = { title: 'Zack' }; | ||
const secondChange = { title: 'Aaron' }; | ||
const withMany = adapter.addAll([TheGreatGatsby, AClockworkOrange], state); | ||
|
||
const withUpdates = adapter.updateMany( | ||
[ | ||
{ id: TheGreatGatsby.id, changes: firstChange }, | ||
{ id: AClockworkOrange.id, changes: secondChange }, | ||
], | ||
withMany | ||
); | ||
|
||
expect(withUpdates).toEqual({ | ||
ids: [AClockworkOrange.id, TheGreatGatsby.id], | ||
entities: { | ||
[TheGreatGatsby.id]: { | ||
...TheGreatGatsby, | ||
...firstChange, | ||
}, | ||
[AClockworkOrange.id]: { | ||
...AClockworkOrange, | ||
...secondChange, | ||
}, | ||
}, | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.