Skip to content

Commit

Permalink
feat: introduce @ngrx/data library to the platform (#1733)
Browse files Browse the repository at this point in the history
The tests all pass when using yarn test, but fail when running tests with Bazel. Will investigate further.
  • Loading branch information
brandonroberts authored Apr 15, 2019
1 parent 14ebae1 commit 5d569c3
Show file tree
Hide file tree
Showing 98 changed files with 18,930 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .circleci/bazel.rc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ build --experimental_strict_action_env
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "medium" class which is the default:
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --jobs 3 --local_resources=2506,2.0,1.0
build --jobs 3 --local_resources=3072,2.0,1.0

# Also limit Bazel's own JVM heap to stay within our 4G container limit
startup --host_jvm_args=-Xmx1g
Expand Down
33 changes: 33 additions & 0 deletions modules/data/BUILD
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",
],
)
3 changes: 3 additions & 0 deletions modules/data/CHANGELOG.md
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)
5 changes: 5 additions & 0 deletions modules/data/README.md
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
7 changes: 7 additions & 0 deletions modules/data/index.ts
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';
32 changes: 32 additions & 0 deletions modules/data/package.json
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
}
1 change: 1 addition & 0 deletions modules/data/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src/index';
12 changes: 12 additions & 0 deletions modules/data/rollup.config.js
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',
}
}
30 changes: 30 additions & 0 deletions modules/data/spec/BUILD
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 modules/data/spec/actions/entity-action-factory.spec.ts
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();
});
});
3 changes: 3 additions & 0 deletions modules/data/spec/actions/entity-action-guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
describe('EntityActionGuard', () => {
// TODO: write some tests
});
Loading

0 comments on commit 5d569c3

Please sign in to comment.