Skip to content

Commit

Permalink
- Added trackable-entity.spec.ts.
Browse files Browse the repository at this point in the history
- Updated map and set specs.
- Updated ReadMe.
  • Loading branch information
Anthony Sneed committed Sep 15, 2017
1 parent 998493c commit e23cd9d
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 45 deletions.
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Docs: <https://trackableentities.github.io/trackable-entities-js>

Sample application: <https://github.com/TrackableEntities/trackable-entities-js-sample.git>

> Note: You must change the TypeScript compiler target to "es2015" in **ts.config.json**.
> - Apps using trackable-entities can support most modern browsers (Chrome, Firefox, Safari, Edge, etc), but they will not be able to support legacy browsers (Internet Explorer).
## Setup

Install **trackable-entities** as a runtime dependency from npm.
Expand All @@ -19,7 +22,7 @@ npm i --save trackable-entities

## Usage

To track property changes on an object, create a class that extends `TrackableEntity`. Then add a `constructor` that returns `super.proxify(this)`. For example
To track property changes on an object, create a class that extends `TrackableEntity`. Then add a `constructor` that returns `super.proxify(this)`. For example:

```js
export class Product extends TrackableEntity {
Expand All @@ -34,4 +37,62 @@ export class Product extends TrackableEntity {
}
```

Then set the `tracking` property to `true`. Modifying property values will change the `trackingState` from `Unchanged` to `Modified` and will populate the `modifiedProperties` collection with the names of properties that were changed, so that partial updates (PATCH) may be performed by an API that knows how to apply changes to a persistence store such as a database.
Then set the `tracking` property to `true`. Modifying property values will change the `trackingState` from `Unchanged` to `Modified` and will populate the `modifiedProperties` collection with the names of properties that were changed, so that partial updates (PATCH) may be performed by an API that knows how to apply changes to a persistence store such as a database.

```js
// Product extends TrackableEntity
product = new Product(1, 'Bacon', 1);

// Turn on change tracking
product.tracking = true;

// Modify a property
product.productName = 'Peas';

// Tracking state is set to Modified
expect(product.trackingState).toEqual(TrackingState.Modified);

// Name of modified properties are tracked
expect(product.modifiedProperties.has('productName')).toBeTruthy();
```

There are two collections that support change tracking: `TrackableSet` which extends `Set<T>`, and `TrackableMap` which extends `Map<K, V>`. When items are added their `trackingState` is set to `Added`, and when items are deleted their `trackingState` is set to `Deleted`.

```js
// Create a new TrackableSet of products
const products = [
new Product(1, 'Bacon', 1),
new Product(2, 'Lettuce', 2),
new Product(3, 'Tomatoes', 3),
];
const trackableProducts = new TrackableSet<Product>(...products);

// Turn on change tracking
product.tracking = true;

// Add an entity
const addedProduct = new Product(4, 'Carrots', 4);
trackableProducts.add(addedProduct);

// Tracking state is set to Added
expect(addedProduct.trackingState).toEqual(TrackingState.Added);

// Remove an entity
const removedProduct = new Product(4, 'Carrots', 4);
trackableProducts.delete(removedProduct);

// Tracking state is set to Added
expect(removedProduct.trackingState).toEqual(TrackingState.Deleted);
```

## Persistence

Once the tracking state of entities have been set, you can pass them to a Web API where they can be persisted to a data store. [Trackable Entities](http://trackableentities.github.io) has a server-side [NuGet package](https://www.nuget.org/packages/TrackableEntities.EF.6) for persisting changes to a relational data store using [Entity Framework](https://docs.microsoft.com/en-us/ef/). The current version supports the full .NET Framework with EF 6, and a future version will support [.NET Core](https://www.microsoft.com/net/core) and EF Core as a [NetStandard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) library.

## Roadmap

We are currently developing the full API for **trackable-entities**. See the project [roadmap](https://github.com/TrackableEntities/trackable-entities-js/wiki/Roadmap) for more information.

## Contributing

If you'd like to help with this project, please contact @tonysneed via email (tony@tonysneed.com) or twitter (@tonysneed).
73 changes: 73 additions & 0 deletions src/trackable-entity.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Product } from './models/product.spec';
import { TrackableSet } from './trackable-set';
import { TrackingState } from './tracking-state';

describe('TrackableEntity', () => {

let product: Product;

beforeEach(() => {
product = new Product(1, 'Bacon', 1);
});

it('should be created', () => {
expect(product).toBeTruthy();
});

it('should set entity TrackingState to Modified when tracking', (done) => {

// Arrange
product.tracking = true;

// Act
product.productName = 'Peas';

// Assert
expect(product.trackingState).toEqual(TrackingState.Modified);
done();
});

it('should not set entity TrackingState to Modified when tracking but not changed', (done) => {

// Arrange
product.tracking = true;

// Act
product.productName = product.productName;

// Assert
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should not set entity TrackingState to Modified when not tracking', (done) => {

// Arrange
product.tracking = true;

// Act
product.tracking = false;
product.productName = 'Peas';

// Assert
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should add to entity ModifiedProperties when tracking', (done) => {

// Arrange
product.tracking = true;

// Act
product.productName = 'Peas';
product.unitPrice = 5;

// Assert
expect(product.trackingState).toEqual(TrackingState.Modified);
expect(product.modifiedProperties.size).toEqual(2);
expect(product.modifiedProperties.has('productName')).toBeTruthy();
expect(product.modifiedProperties.has('unitPrice')).toBeTruthy();
done();
});
});
12 changes: 6 additions & 6 deletions src/trackable-map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,28 @@ describe('TrackableMap', () => {

// Arrange
trackableMap.tracking = true;
const food = new Product(4, 'Carrots', 4);
const product = new Product(4, 'Carrots', 4);

// Act
trackableMap.add(food.productName, food);
trackableMap.add(product.productName, product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Added);
expect(product.trackingState).toEqual(TrackingState.Added);
done();
});

it('should not set entity TrackingState to Added when not tracking', (done) => {

// Arrange
trackableMap.tracking = true;
const food = new Product(5, 'Carrots', 4);
const product = new Product(5, 'Carrots', 4);

// Act
trackableMap.tracking = false;
trackableMap.add(food.productName, food);
trackableMap.add(product.productName, product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Unchanged);
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

Expand Down
74 changes: 37 additions & 37 deletions src/trackable-set.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ describe('TrackableSet', () => {
let trackableSet: TrackableSet<Product>;

beforeEach(() => {
const foods = [
const products = [
new Product(1, 'Bacon', 1),
new Product(2, 'Lettuce', 2),
new Product(3, 'Tomatoes', 3),
];
trackableSet = new TrackableSet<Product>(...foods);
trackableSet = new TrackableSet<Product>(...products);
});

it('should be created', () => {
Expand All @@ -27,88 +27,88 @@ describe('TrackableSet', () => {

// Arrange
trackableSet.tracking = true;
const food = new Product(4, 'Carrots', 4);
const product = new Product(4, 'Carrots', 4);

// Act
trackableSet.add(food);
trackableSet.add(product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Added);
expect(product.trackingState).toEqual(TrackingState.Added);
done();
});

it('should not set entity TrackingState to Added when not tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = new Product(4, 'Carrots', 4);
const product = new Product(4, 'Carrots', 4);

// Act
trackableSet.tracking = false;
trackableSet.add(food);
trackableSet.add(product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Unchanged);
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should set entity TrackingState to Deleted when tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
trackableSet.delete(food);
trackableSet.delete(product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Deleted);
expect(product.trackingState).toEqual(TrackingState.Deleted);
done();
});

it('should not set entity TrackingState to Deleted when not tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
trackableSet.tracking = false;
trackableSet.delete(food);
trackableSet.delete(product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Unchanged);
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should cache Deleted entities when tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
trackableSet.delete(food);
trackableSet.delete(product);

// Assert
expect(food.trackingState).toEqual(TrackingState.Deleted);
expect(product.trackingState).toEqual(TrackingState.Deleted);
const deletedEntities = [...(trackableSet as any).deletedEntities];
expect(deletedEntities[0]).toBe(food);
expect(deletedEntities[0]).toBe(product);
done();
});

it('should clear Deleted entities when not tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
trackableSet.delete(food);
trackableSet.delete(product);
trackableSet.tracking = false;

// Assert
expect(food.trackingState).toEqual(TrackingState.Deleted);
expect(product.trackingState).toEqual(TrackingState.Deleted);
const deletedEntities = [...(trackableSet as any).deletedEntities];
expect(deletedEntities.length).toEqual(0);
done();
Expand All @@ -118,60 +118,60 @@ describe('TrackableSet', () => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
food.productName = 'Peas';
product.productName = 'Peas';

// Assert
expect(food.trackingState).toEqual(TrackingState.Modified);
expect(product.trackingState).toEqual(TrackingState.Modified);
done();
});

it('should not set entity TrackingState to Modified when tracking but not changed', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
food.productName = food.productName;
product.productName = product.productName;

// Assert
expect(food.trackingState).toEqual(TrackingState.Unchanged);
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should not set entity TrackingState to Modified when not tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
trackableSet.tracking = false;
food.productName = 'Peas';
product.productName = 'Peas';

// Assert
expect(food.trackingState).toEqual(TrackingState.Unchanged);
expect(product.trackingState).toEqual(TrackingState.Unchanged);
done();
});

it('should add to entity ModifiedProperties when tracking', (done) => {

// Arrange
trackableSet.tracking = true;
const food = [...trackableSet][0];
const product = [...trackableSet][0];

// Act
food.productName = 'Peas';
food.unitPrice = 5;
product.productName = 'Peas';
product.unitPrice = 5;

// Assert
expect(food.trackingState).toEqual(TrackingState.Modified);
expect(food.modifiedProperties.size).toEqual(2);
expect(food.modifiedProperties.has('productName')).toBeTruthy();
expect(food.modifiedProperties.has('unitPrice')).toBeTruthy();
expect(product.trackingState).toEqual(TrackingState.Modified);
expect(product.modifiedProperties.size).toEqual(2);
expect(product.modifiedProperties.has('productName')).toBeTruthy();
expect(product.modifiedProperties.has('unitPrice')).toBeTruthy();
done();
});
});

0 comments on commit e23cd9d

Please sign in to comment.