-
-
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: introduce @ngrx/data library to the platform (#1733)
The tests all pass when using yarn test, but fail when running tests with Bazel. Will investigate further.
- Loading branch information
1 parent
14ebae1
commit 5d569c3
Showing
98 changed files
with
18,930 additions
and
1 deletion.
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,33 @@ | ||
package(default_visibility = ["//visibility:public"]) | ||
|
||
load("//tools:defaults.bzl", "ng_module", "ng_package") | ||
|
||
ng_module( | ||
name = "data", | ||
srcs = glob([ | ||
"*.ts", | ||
"src/**/*.ts", | ||
]), | ||
module_name = "@ngrx/data", | ||
deps = [ | ||
"//modules/effects", | ||
"//modules/entity", | ||
"//modules/store", | ||
"@npm//@angular/common", | ||
"@npm//@angular/core", | ||
"@npm//rxjs", | ||
], | ||
) | ||
|
||
ng_package( | ||
name = "npm_package", | ||
srcs = glob(["**/*.externs.js"]) + [ | ||
"package.json", | ||
], | ||
entry_point = "modules/data/index.js", | ||
packages = [ | ||
], | ||
deps = [ | ||
":data", | ||
], | ||
) |
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,3 @@ | ||
# Change Log | ||
|
||
See [CHANGELOG.md](https://github.com/ngrx/platform/blob/master/CHANGELOG.md) |
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,5 @@ | ||
# @ngrx/data | ||
|
||
The sources for this package are in the main [NgRx](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,32 @@ | ||
{ | ||
"name": "@ngrx/data", | ||
"version": "0.0.0-PLACEHOLDER", | ||
"description": "API management for NgRx", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/ngrx/platform.git" | ||
}, | ||
"keywords": [ | ||
"Angular", | ||
"Redux", | ||
"NgRx", | ||
"Schematics", | ||
"Angular CLI" | ||
], | ||
"author": "NgRx", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/ngrx/platform/issues" | ||
}, | ||
"homepage": "https://github.com/ngrx/platform#readme", | ||
"peerDependencies": { | ||
"@angular/common": "NG_VERSION", | ||
"@angular/core": "NG_VERSION", | ||
"@ngrx/store": "0.0.0-PLACEHOLDER", | ||
"@ngrx/effects": "0.0.0-PLACEHOLDER", | ||
"@ngrx/entity": "0.0.0-PLACEHOLDER", | ||
"rxjs": "RXJS_VERSION" | ||
}, | ||
"schematics": "MODULE_SCHEMATICS_COLLECTION", | ||
"sideEffects": false | ||
} |
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,12 @@ | ||
export default { | ||
entry: './dist/data/@ngrx/data.es5.js', | ||
dest: './dist/data/bundles/data.umd.js', | ||
format: 'umd', | ||
exports: 'named', | ||
moduleName: 'ngrx.data', | ||
globals: { | ||
'@ngrx/store': 'ngrx.store', | ||
'@ngrx/effects': 'ngrx.effects', | ||
'@ngrx/entity': 'ngrx.entity', | ||
} | ||
} |
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,30 @@ | ||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_test_library") | ||
|
||
ts_test_library( | ||
name = "test_lib", | ||
srcs = glob( | ||
[ | ||
"**/*.ts", | ||
], | ||
), | ||
deps = [ | ||
"//modules/data", | ||
"//modules/effects", | ||
"//modules/effects/testing", | ||
"//modules/entity", | ||
"//modules/store", | ||
"@npm//@angular/common", | ||
"@npm//rxjs", | ||
], | ||
) | ||
|
||
jasmine_node_test( | ||
name = "test", | ||
deps = [ | ||
":test_lib", | ||
"//modules/data", | ||
"//modules/effects", | ||
"//modules/entity", | ||
"//modules/store", | ||
], | ||
) |
212 changes: 212 additions & 0 deletions
212
modules/data/spec/actions/entity-action-factory.spec.ts
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,212 @@ | ||
import { | ||
EntityAction, | ||
EntityActionOptions, | ||
EntityActionPayload, | ||
EntityOp, | ||
EntityActionFactory, | ||
MergeStrategy, | ||
CorrelationIdGenerator, | ||
} from '../../'; | ||
|
||
class Hero { | ||
id: number; | ||
name: string; | ||
} | ||
|
||
describe('EntityActionFactory', () => { | ||
let factory: EntityActionFactory; | ||
|
||
beforeEach(() => { | ||
factory = new EntityActionFactory(); | ||
}); | ||
|
||
it('#create should create an EntityAction from entityName and entityOp', () => { | ||
const action = factory.create('Hero', EntityOp.QUERY_ALL); | ||
const { entityName, entityOp, data } = action.payload; | ||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.QUERY_ALL); | ||
expect(data).toBeUndefined('no data property'); | ||
}); | ||
|
||
it('#create should create an EntityAction with the given data', () => { | ||
const hero: Hero = { id: 42, name: 'Francis' }; | ||
const action = factory.create('Hero', EntityOp.ADD_ONE, hero); | ||
const { entityName, entityOp, data } = action.payload; | ||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.ADD_ONE); | ||
expect(data).toBe(hero); | ||
}); | ||
|
||
it('#create should create an EntityAction with options', () => { | ||
const options: EntityActionOptions = { | ||
correlationId: 'CRID42', | ||
isOptimistic: true, | ||
mergeStrategy: MergeStrategy.OverwriteChanges, | ||
tag: 'Foo', | ||
}; | ||
|
||
// Don't forget placeholder for missing optional data! | ||
const action = factory.create( | ||
'Hero', | ||
EntityOp.QUERY_ALL, | ||
undefined, | ||
options | ||
); | ||
const { | ||
entityName, | ||
entityOp, | ||
data, | ||
correlationId, | ||
isOptimistic, | ||
mergeStrategy, | ||
tag, | ||
} = action.payload; | ||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.QUERY_ALL); | ||
expect(data).toBeUndefined(); | ||
expect(correlationId).toBe(options.correlationId); | ||
expect(isOptimistic).toBe(options.isOptimistic); | ||
expect(mergeStrategy).toBe(options.mergeStrategy); | ||
expect(tag).toBe(options.tag); | ||
}); | ||
|
||
it('#create create an EntityAction from an EntityActionPayload', () => { | ||
const hero: Hero = { id: 42, name: 'Francis' }; | ||
const payload: EntityActionPayload = { | ||
entityName: 'Hero', | ||
entityOp: EntityOp.ADD_ONE, | ||
data: hero, | ||
correlationId: 'CRID42', | ||
isOptimistic: true, | ||
mergeStrategy: MergeStrategy.OverwriteChanges, | ||
tag: 'Foo', | ||
}; | ||
const action = factory.create(payload); | ||
|
||
const { | ||
entityName, | ||
entityOp, | ||
data, | ||
correlationId, | ||
isOptimistic, | ||
mergeStrategy, | ||
tag, | ||
} = action.payload; | ||
expect(entityName).toBe(payload.entityName); | ||
expect(entityOp).toBe(payload.entityOp); | ||
expect(data).toBe(payload.data); | ||
expect(correlationId).toBe(payload.correlationId); | ||
expect(isOptimistic).toBe(payload.isOptimistic); | ||
expect(mergeStrategy).toBe(payload.mergeStrategy); | ||
expect(tag).toBe(payload.tag); | ||
}); | ||
|
||
it('#createFromAction should create EntityAction from another EntityAction', () => { | ||
// pessimistic save | ||
const hero1: Hero = { id: undefined as any, name: 'Francis' }; | ||
const action1 = factory.create('Hero', EntityOp.SAVE_ADD_ONE, hero1); | ||
|
||
// after save succeeds | ||
const hero: Hero = { ...hero1, id: 42 }; | ||
const action = factory.createFromAction(action1, { | ||
entityOp: EntityOp.SAVE_ADD_ONE_SUCCESS, | ||
data: hero, | ||
}); | ||
const { entityName, entityOp, data } = action.payload; | ||
|
||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.SAVE_ADD_ONE_SUCCESS); | ||
expect(data).toBe(hero); | ||
const expectedType = factory.formatActionType( | ||
EntityOp.SAVE_ADD_ONE_SUCCESS, | ||
'Hero' | ||
); | ||
expect(action.type).toEqual(expectedType); | ||
}); | ||
|
||
it('#createFromAction should copy the options from the source action', () => { | ||
const options: EntityActionOptions = { | ||
correlationId: 'CRID42', | ||
isOptimistic: true, | ||
mergeStrategy: MergeStrategy.OverwriteChanges, | ||
tag: 'Foo', | ||
}; | ||
// Don't forget placeholder for missing optional data! | ||
const sourceAction = factory.create( | ||
'Hero', | ||
EntityOp.QUERY_ALL, | ||
undefined, | ||
options | ||
); | ||
|
||
const queryResults: Hero[] = [ | ||
{ id: 1, name: 'Francis' }, | ||
{ id: 2, name: 'Alex' }, | ||
]; | ||
const action = factory.createFromAction(sourceAction, { | ||
entityOp: EntityOp.QUERY_ALL_SUCCESS, | ||
data: queryResults, | ||
}); | ||
|
||
const { | ||
entityName, | ||
entityOp, | ||
data, | ||
correlationId, | ||
isOptimistic, | ||
mergeStrategy, | ||
tag, | ||
} = action.payload; | ||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.QUERY_ALL_SUCCESS); | ||
expect(data).toBe(queryResults); | ||
expect(correlationId).toBe(options.correlationId); | ||
expect(isOptimistic).toBe(options.isOptimistic); | ||
expect(mergeStrategy).toBe(options.mergeStrategy); | ||
expect(tag).toBe(options.tag); | ||
}); | ||
|
||
it('#createFromAction can suppress the data property', () => { | ||
const hero: Hero = { id: 42, name: 'Francis' }; | ||
const action1 = factory.create('Hero', EntityOp.ADD_ONE, hero); | ||
const action = factory.createFromAction(action1, { | ||
entityOp: EntityOp.SAVE_ADD_ONE, | ||
data: undefined, | ||
}); | ||
const { entityName, entityOp, data } = action.payload; | ||
expect(entityName).toBe('Hero'); | ||
expect(entityOp).toBe(EntityOp.SAVE_ADD_ONE); | ||
expect(data).toBeUndefined(); | ||
}); | ||
|
||
it('#formatActionType should format type with the entityName', () => { | ||
const action = factory.create('Hero', EntityOp.QUERY_ALL); | ||
const expectedFormat = factory.formatActionType(EntityOp.QUERY_ALL, 'Hero'); | ||
expect(action.type).toBe(expectedFormat); | ||
}); | ||
|
||
it('#formatActionType should format type with given tag instead of the entity name', () => { | ||
const tag = 'Hero - Tag Test'; | ||
const action = factory.create('Hero', EntityOp.QUERY_ALL, null, { tag }); | ||
expect(action.type).toContain(tag); | ||
}); | ||
|
||
it('can re-format generated action.type with a custom #formatActionType()', () => { | ||
factory.formatActionType = (op, entityName) => | ||
`${entityName}_${op}`.toUpperCase(); | ||
|
||
const expected = ('Hero_' + EntityOp.QUERY_ALL).toUpperCase(); | ||
const action = factory.create('Hero', EntityOp.QUERY_ALL); | ||
expect(action.type).toBe(expected); | ||
}); | ||
|
||
it('should throw if do not specify entityName', () => { | ||
expect(() => factory.create(null as any)).toThrow(); | ||
}); | ||
|
||
it('should throw if do not specify EntityOp', () => { | ||
expect(() => | ||
factory.create({ entityName: 'Hero', entityOp: null as any }) | ||
).toThrow(); | ||
}); | ||
}); |
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,3 @@ | ||
describe('EntityActionGuard', () => { | ||
// TODO: write some tests | ||
}); |
Oops, something went wrong.