Skip to content

Commit

Permalink
making update method internal
Browse files Browse the repository at this point in the history
  • Loading branch information
iotshaman committed Jul 7, 2020
1 parent 9b59fae commit 1fe2197
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 60 deletions.
59 changes: 30 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,32 @@ export class Foo {
Next, we need to create an implementation of the base "RepositoryContext" class:

```ts
//file: sample-repository.ts
//file: sample-context.ts
import { RepositoryContext, Repository } from 'json-repo';
import { Foo } from './foo';

export class SampleRepository extends RepositoryContext {
export class SampleDataContext extends RepositoryContext {
constructor(dataPath: string) { super(dataPath); }
models = {
baz: new Repository<Foo>()
}
}
```

Finally, we need to initialize the repository in our application:
Finally, we need to initialize the context in our application:

```ts
import * as path from 'path';
import { SampleRepository } from './sample-repository';
import { SampleDataContext } from './sample-context';
import { Foo } from './foo';

const jsonFilePath = path.join(__dirname, 'db.json');
let repository = new SampleRepository(jsonFilePath);
repository.initialize().then(_ => {
//you can now use the repository
let context = new SampleDataContext(jsonFilePath);
context.initialize().then(_ => {
//you can now use the context
let foo = new Foo(); foo.bar = 'baz';
repository.models.baz.add('1', foo);
return repository.saveChanges();
context.models.baz.add('1', foo);
return context.saveChanges();
});
```

Expand All @@ -69,7 +69,7 @@ The "RepositoryContext" is an abstract class that allows you to communicate with
//provided from the repository-context.d.ts file
export declare abstract class RepositoryContext {
abstract models: {
[key: string]: Repository<any>;
[key: string]: IRepository<any>;
};
constructor(dataPath?: string, entityNodeService?: IEntityNodeService);
initialize: () => Promise<void>;
Expand All @@ -87,25 +87,25 @@ export declare abstract class RepositoryContext {

#### Repository

The "Repository" class is a generic class, with the type argument representing a domain object. Some built-in methods are provided to manage and query the repository; this class can also be extended, if you wish to provide additional functionality.
The "Repository" interface is defined as a generic, with the type argument representing a domain object. Some built-in methods are provided to manage and query the repository. The default implementation of IRepository is Repository, and can also be extended if you wish to provide additional functionality.

```ts
//provided from the repository.d.ts file
export declare class Repository<T> {
get state(): string;
protected _state: string;
protected data: {
[key: string]: T;
};
load: (data: EntityNode[]) => void;
extract: () => EntityNode[];
find: (key: string) => T;
where: (filter: (item: T) => boolean) => T[];
add: (key: string, item: T) => void;
upsert: (key: string, item: T) => void;
update: (key: string, item: T) => void;
delete: (key: string) => void;
markCurrent: () => void;
get state(): string;
protected _state: string;
protected data: {
[key: string]: T;
};
load: (data: EntityNode[]) => void;
extract: () => EntityNode[];
find: (key: string) => T;
filter: (filter: (item: T) => boolean) => T[];
add: (key: string, item: T) => void;
addRange: (items: [{key: string, item: T}]) => void;
update: (key: string, action: (item: T) => T) => void;
delete: (key: string) => void;
markCurrent: () => void;
}
```

Expand All @@ -115,17 +115,18 @@ An "EntityNode" is the core persistent storage block of a repository. When files
```ts
//provided from the entity-node.d.ts file
export declare class EntityNode {
key: string;
value: any;
key: string;
value: any;
}
```

#### IEntityNodeService
The implementation of "IEntityNodeService" acts as the persistent storage layer, reading to and writing from some storage mechanism. The built-in implementation of IEntityNodeService is calls "JsonFileService", and is provided by default to all "RepositoryContext" objects. If you wish to provide a custom data persistence solution, you can achieve this by creating a custom implementation of IEntityNodeService.

```ts
//provided from the entity-node-service.d.ts file
export interface IEntityNodeService {
getEntityNodes(path: string): Promise<{[model: string]: EntityNode[];}>;
persistEntityNodes<T>(path: string, data: T): Promise<void>;
getEntityNodes(path: string): Promise<{[model: string]: EntityNode[];}>;
persistEntityNodes<T>(path: string, data: T): Promise<void>;
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "json-repo",
"version": "1.1.3",
"version": "1.2.1",
"description": "Read and write data to JSON files using the repository pattern. ",
"scripts": {
"test": "nyc mocha",
Expand Down
2 changes: 1 addition & 1 deletion src/repository-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('ContextBase', () => {
jsonService.persistEntityNodes.returns(Promise.resolve(sampleData));
let context = new NoopContextBase("inmemory", jsonService);
context.initialize().then(_ => {
context.models.sample.update('1', new SampleData());
context.models.sample.update('1', (item => item));
return context.saveChanges()
})
.then(_ => {
Expand Down
4 changes: 2 additions & 2 deletions src/repository-context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { JsonFileService } from "./json-file-service";
import { IEntityNodeService } from './entity-node-service';
import { EntityNode } from "./entity-node";
import { Repository } from "./repository";
import { IRepository } from "./repository";

export abstract class RepositoryContext {

abstract models: {[model: string]: Repository<any>};
abstract models: {[model: string]: IRepository<any>};
private dataPath: string;
private entityNodeService: IEntityNodeService;

Expand Down
53 changes: 32 additions & 21 deletions src/repository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ describe('Repository', () => {
expect(repository.state).to.equal('current');
});

it('get should return entities', () => {
it('extract should return entities', () => {
repository.load([{key: '1', value: new SampleData()}]);
expect(repository.extract().length).to.equal(1);
});

it('where should return values that match filter', () => {
it('filter should return values that match filter', () => {
let sample = [
{key: '1', value: new SampleData()},
{key: '2', value: new SampleData()}
];
sample[0].value.foo = 'foo1';
repository.load(sample);
let result = repository.where(item => item.foo == 'foo1');
let result = repository.filter(item => item.foo == 'foo1');
expect(result.length).to.equal(1);
});

Expand All @@ -51,6 +51,14 @@ describe('Repository', () => {
expect(() => { repository.add('1', new SampleData()); }).to.throw();
});

it('addRange should add multiple items', () => {
repository.addRange([
{key: '1', value: new SampleData()},
{key: '2', value: new SampleData()}
]);
expect(repository.extract().length).to.equal(2);
})

it('markCurrent should set state to current', () => {
repository.add('1', new SampleData());
repository.markCurrent();
Expand All @@ -62,41 +70,44 @@ describe('Repository', () => {
});

it('find should return value if key exists', () => {
repository.upsert('1', new SampleData());
repository.add('1', new SampleData());
expect(repository.find('1')).not.to.be.undefined;
});

it('upsert should create new entity', () => {
repository.upsert('1', new SampleData());
expect(repository.find('1')).not.to.be.undefined;
it('update should throw error when key does not exist', () => {
expect(() => { repository.update('1', (item => item)); }).to.throw();
});

it('upsert should update existing entity', () => {
it('update should update existing entity', () => {
repository.load([{key: '1', value: new SampleData()}]);
let newValue = new SampleData(); newValue.foo = 'baz';
repository.upsert('1', newValue);
expect(repository.find('1').foo).to.equal('baz');
repository.update('1', (item => {
item.foo = 'baz'; return item;
}));
expect(repository.find('1')).not.to.be.undefined;
});

it('upsert should set state to dirty', () => {
repository.upsert('1', new SampleData());
it('update should set state to dirty', () => {
repository.load([{key: '1', value: new SampleData()}]);
repository.update('1', (item => {
item.foo = 'baz'; return item;
}));
expect(repository.state).to.equal('dirty');
});

it('update should throw error when key does not exist', () => {
expect(() => { repository.update('1', new SampleData()); }).to.throw();
it('upsert should create new entity', () => {
repository.upsert('1', new SampleData());
expect(repository.find('1')).not.to.be.undefined;
});

it('update should update existing entity', () => {
it('upsert should update existing entity', () => {
repository.load([{key: '1', value: new SampleData()}]);
let newValue = new SampleData(); newValue.foo = 'baz';
repository.update('1', newValue);
expect(repository.find('1')).not.to.be.undefined;
repository.upsert('1', newValue);
expect(repository.find('1').foo).to.equal('baz');
});

it('update should set state to dirty', () => {
repository.load([{key: '1', value: new SampleData()}]);
repository.update('1', new SampleData());
it('upsert should set state to dirty', () => {
repository.upsert('1', new SampleData());
expect(repository.state).to.equal('dirty');
});

Expand Down
31 changes: 25 additions & 6 deletions src/repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { EntityNode } from './entity-node';

export class Repository<T> {
export interface IRepository<T> {
state: string;
load: (data: EntityNode[]) => void;
extract: () => EntityNode[];
find: (key: string) => T;
filter: (filter: (item: T) => boolean) => T[];
add: (key: string, item: T) => void;
addRange: (items: {key: string, value: T}[]) => void;
update: (key: string, action: (item: T) => T) => void;
delete: (key: string) => void;
markCurrent: () => void;
}

export class Repository<T> implements IRepository<T> {

get state(): string { return this._state; };
protected _state: string = 'unset';
Expand All @@ -23,7 +36,7 @@ export class Repository<T> {
return this.data[key];
}

where = (filter: (item: T) => boolean): T[] => {
filter = (filter: (item: T) => boolean): T[] => {
return this.extract().map(rslt => rslt.value).filter(filter);
}

Expand All @@ -33,13 +46,19 @@ export class Repository<T> {
this._state = 'dirty';
}

upsert = (key: string, item: T) => {
this.data[key] = item;
this._state = 'dirty';
addRange = (items: {key: string, value: T}[]) => {
for (var i = 0; i < items.length; i++) {
this.add(items[i].key, items[i].value);
}
}

update = (key: string, item: T) => {
update = (key: string, action: (item: T) => T) => {
if (!this.data[key]) throw new Error(`Item with key '${key}' does not exist.`);
this.data[key] = action(this.data[key]);
this._state = 'dirty';
}

upsert = (key: string, item: T) => {
this.data[key] = item;
this._state = 'dirty';
}
Expand Down

0 comments on commit 1fe2197

Please sign in to comment.