Skip to content
This repository was archived by the owner on Jun 22, 2021. It is now read-only.

Removes the Id interface requirement. #1

Merged
merged 13 commits into from
Jan 6, 2018
2 changes: 1 addition & 1 deletion docs/facade.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The facade contains common functions for storage and retrieval of entities from

The functions have some common options that they use.

- [Entity](./options.md#entity)
- [Id](./options.md#id)
- [Entity](./options.md#entity)
- [Patch](./options.md#patch)
- [Filter](./options.md#filter)
- [Sort](./options.md#sort)
Expand Down
14 changes: 7 additions & 7 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Creates a new entity using the `entity` option if no entity exists that matches

```ts
const { entity } = await facade.createEntity({
id: { id: 'example_id' },
id: 'example_id',
entity: { id: 'example_id', foo: 'bar' },
});
```
Expand Down Expand Up @@ -63,7 +63,7 @@ Retrieves a single entity that matches the `id` option.

```ts
const { entity } = await facade.getEntity({
id: { id: 'example_id' },
id: 'example_id',
});
```

Expand All @@ -74,7 +74,7 @@ For an entity that matches the `id` option, it changes all of an entity's proper

```ts
const { entity } = await facade.overwriteEntity({
id: { id: 'example_id' },
id: 'example_id',
entity: { id: 'example_id', foo: 'bar' },
});
```
Expand All @@ -86,7 +86,7 @@ For an entity that matches the `id` option, it changes some of an entity's prope

```ts
const { entity } = await facade.patchEntity({
id: { id: 'example_id' },
id: 'example_id',
patch: { foo: 'bar' },
});
```
Expand All @@ -109,7 +109,7 @@ Removes an entity that matches the `id` option.

```ts
await facade.removeEntity({
id: { id: 'example_id' },
id: 'example_id',
});
```

Expand All @@ -120,9 +120,9 @@ Creates an entity when no entity exists that matches the `id` option. Otherwise,

```ts
await facade.upsertEntity({
id: { id: 'example_id' },
id: 'example_id',
entity: { id: 'example_id', foo: 'bar' },
});
```

This package contains the [upsert entity tests](../src/tests/upsertsEntity) and the [upsert entity signature](../src/signatures/UpsertEntity.ts) for this function.
This package contains the [upsert entity tests](../src/tests/upsertsEntity) and the [upsert entity signature](../src/signatures/UpsertEntity.ts) for this function.
20 changes: 7 additions & 13 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,17 @@ The [facade](./facade.md) [functions](./functions.md) have some common options t
- [Pagination](#pagination)

### Id
This is an object that contains only the properties required to distinctly identify an entity. In most common cases there is a single property making the [unique key](https://en.wikipedia.org/wiki/Unique_key) so it will likely just contain the `id` property. However, for entities with multiple properties making the unique key it will contain those properties.

This interface is user-defined hence not contained in this package, the interface below demonstrates what this will look like in most cases.

```ts
interface Id {
readonly id: string;
}
```
This is a string that uniquely identifies an entity.

### Entity
This is an object that contains all of the entity's properties. The word "entity" has been borrowed from [Entity-Relationship Models/Diagrams](https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model) and has been used instead of the word "model" to avoid confusion with MVC.

This interface is user-defined hence not contained in this package, the interface below demonstrates what this might look like for a todo entity and extends the [Id interface from the Id example](#id).
This interface is user-defined hence not contained in this package, the interface below demonstrates what this might look like for a todo entity and extends the [TypeScript Entity interface](../src/types/Entity.ts) defined in this package which contains the `id` property.

```ts
interface TodoEntity extends Id {
import Entity from '@js-entity-repos/core/dist/types/Entity';

interface TodoEntity extends Entity {
readonly description: string;
readonly completed: boolean;
}
Expand All @@ -44,8 +38,8 @@ This is an object where a key represents the entity property to be sorted and th
This package contains the [TypeScript Sort type definition](../src/types/Sort.ts).

### Pagination
This is an object with three properties, `limit`, `forward`, and `cursor`. The `limit` property defines how many entities to return (maximum). The `forward` property defines whether the entities should be iterate through the entities forwards (when `true`) or backwards (when `false`) from the `cursor`. The `cursor` property defines where to start iterating through the entities.
This is an object with three properties, `limit`, `forward`, and `cursor`. The `limit` property defines how many entities to return (maximum). The `forward` property defines whether the entities should be iterated through forwards (when `true`) or backwards (when `false`) from the `cursor`. The `cursor` property defines where to start iterating through the entities. Cursors have been used instead of `skip` and `limit` to avoid the [pagination issues discussed by Rakhitha Nimesh](https://www.sitepoint.com/paginating-real-time-data-cursor-based-pagination/).

Concrete implementations of the facade can use the [`createCursorFromEntity`](../src/utils/createCursorFromEntity) and [`createPaginationFilter`](../src/utils/createPaginationFilter) util functions to generate cursors.
Concrete implementations of the facade can use the [`createCursorFromEntity`](../src/utils/createCursorFromEntity) and [`createPaginationFilter`](../src/utils/createPaginationFilter) utility functions to generate cursors.

This package also contains the [TypeScript Pagination interface](../src/types/Pagination.ts) and the [TypeScript Cursor type definition](../src/types/Cursor.ts).
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@
### Usage
- Install it with `npm i @js-entity-repos/core`.
- Understand it by reading the [documenation](./docs/facade.md).

### FeathersJS
The project has some similarities with parts of the [FeathersJS framework](feathersjs.com), but unfortunately I've found the following issues with FeathersJS.

- Their pagination uses skip and limit instead of cursors which causes [issues as discussed by Rakhitha Nimesh](https://www.sitepoint.com/paginating-real-time-data-cursor-based-pagination/).
- Their service interface is missing some functions provided in the [Facade](./docs/facade.md) here.
- Their errors take messages instead of parameters making it harder to support localisation.

### Thanks
Thanks to [James](https://github.com/ht2), [Mariusz](https://github.com/mariocoski), and [Pete](https://github.com/ee0pdt) at [HT2 Labs](https://www.ht2labs.com) for their feedback.
21 changes: 11 additions & 10 deletions src/Facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import PatchEntity from './signatures/PatchEntity';
import RemoveEntities from './signatures/RemoveEntities';
import RemoveEntity from './signatures/RemoveEntity';
import UpsertEntity from './signatures/UpsertEntity';
import Entity from './types/Entity';

export default interface Facade<Id, Entity extends Id> {
readonly getEntity: GetEntity<Id, Entity>;
readonly createEntity: CreateEntity<Id, Entity>;
readonly overwriteEntity: OverwriteEntity<Id, Entity>;
readonly patchEntity: PatchEntity<Id, Entity>;
readonly removeEntity: RemoveEntity<Id>;
readonly getEntities: GetEntities<Entity>;
readonly countEntities: CountEntities<Entity>;
readonly removeEntities: RemoveEntities<Entity>;
readonly upsertEntity: UpsertEntity<Id, Entity>;
export default interface Facade<E extends Entity> {
readonly getEntity: GetEntity<E>;
readonly createEntity: CreateEntity<E>;
readonly overwriteEntity: OverwriteEntity<E>;
readonly patchEntity: PatchEntity<E>;
readonly removeEntity: RemoveEntity;
readonly getEntities: GetEntities<E>;
readonly countEntities: CountEntities<E>;
readonly removeEntities: RemoveEntities<E>;
readonly upsertEntity: UpsertEntity<E>;
}
4 changes: 2 additions & 2 deletions src/errors/ConflictingEntityError.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// tslint:disable:no-class
import { BaseError } from 'make-error';

export default class ConflictingEntityError<Id> extends BaseError {
constructor(public entityName: string, public entityId: Id) {
export default class ConflictingEntityError extends BaseError {
constructor(public entityName: string, public entityId: string) {
super();
}
}
4 changes: 2 additions & 2 deletions src/errors/MissingEntityError.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// tslint:disable:no-class
import { BaseError } from 'make-error';

export default class MissingEntityError<Id> extends BaseError {
constructor(public entityName: string, public entityId: Id) {
export default class MissingEntityError extends BaseError {
constructor(public entityName: string, public entityId: string) {
super();
}
}
7 changes: 4 additions & 3 deletions src/signatures/CountEntities.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Entity from '../types/Entity';
import Filter from '../types/Filter';

export interface Opts<Entity> {
readonly filter: Filter<Entity>;
export interface Opts<E extends Entity> {
readonly filter: Filter<E>;
}

export interface Result {
readonly count: number;
}

export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result>;

export default Signature;
15 changes: 8 additions & 7 deletions src/signatures/CreateEntity.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
export interface Opts<Id, Entity extends Id> {
readonly id: Id;
readonly entity: Entity;
import Entity from '../types/Entity';

export interface Opts<E extends Entity> {
readonly id: string;
readonly entity: E;
}

export interface Result<Entity> {
readonly entity: Entity;
export interface Result<E extends Entity> {
readonly entity: E;
}

export type Signature<Id, Entity extends Id> =
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;

export default Signature;
13 changes: 7 additions & 6 deletions src/signatures/GetEntities.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import Cursor from '../types/Cursor';
import Entity from '../types/Entity';
import Filter from '../types/Filter';
import Pagination from '../types/Pagination';
import Sort from '../types/Sort';

export interface Opts<Entity> {
readonly filter: Filter<Entity>;
readonly sort: Sort<Entity>;
export interface Opts<E extends Entity> {
readonly filter: Filter<E>;
readonly sort: Sort<E>;
readonly pagination: Pagination;
}

export interface Result<Entity> {
readonly entities: Entity[];
export interface Result<E extends Entity> {
readonly entities: E[];
readonly nextCursor: Cursor;
readonly previousCursor: Cursor;
}

export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;

export default Signature;
12 changes: 7 additions & 5 deletions src/signatures/GetEntity.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export interface Opts<Id> {
readonly id: Id;
import Entity from '../types/Entity';

export interface Opts {
readonly id: string;
}

export interface Result<Entity> {
readonly entity: Entity;
export interface Result<E extends Entity> {
readonly entity: E;
}

export type Signature<Id, Entity extends Id> = (opts: Opts<Id>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts) => Promise<Result<E>>;

export default Signature;
15 changes: 8 additions & 7 deletions src/signatures/OverwriteEntity.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
export interface Opts<Id, Entity> {
readonly id: Id;
readonly entity: Entity;
import Entity from '../types/Entity';

export interface Opts<E extends Entity> {
readonly id: string;
readonly entity: E;
}

export interface Result<Entity> {
readonly entity: Entity;
export interface Result<E extends Entity> {
readonly entity: E;
}

export type Signature<Id, Entity extends Id> =
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;

export default Signature;
15 changes: 8 additions & 7 deletions src/signatures/PatchEntity.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
export interface Opts<Id, Entity> {
readonly id: Id;
readonly patch: Partial<Entity>;
import Entity from '../types/Entity';

export interface Opts<E extends Entity> {
readonly id: string;
readonly patch: Partial<E>;
}

export interface Result<Entity> {
readonly entity: Entity;
export interface Result<E extends Entity> {
readonly entity: E;
}

export type Signature<Id, Entity extends Id> =
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;

export default Signature;
7 changes: 4 additions & 3 deletions src/signatures/RemoveEntities.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Entity from '../types/Entity';
import Filter from '../types/Filter';

export interface Opts<Entity> {
readonly filter: Filter<Entity>;
export interface Opts<E extends Entity> {
readonly filter: Filter<E>;
}

export type Result = void;

export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result>;

export default Signature;
6 changes: 3 additions & 3 deletions src/signatures/RemoveEntity.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export interface Opts<Id> {
readonly id: Id;
export interface Opts {
readonly id: string;
}

export type Result = void;

export type Signature<Id> = (opts: Opts<Id>) => Promise<Result>;
export type Signature = (opts: Opts) => Promise<Result>;

export default Signature;
15 changes: 8 additions & 7 deletions src/signatures/UpsertEntity.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
export interface Opts<Id, Entity> {
readonly id: Id;
readonly entity: Entity;
import Entity from '../types/Entity';

export interface Opts<E extends Entity> {
readonly id: string;
readonly entity: E;
}

export interface Result<Entity> {
readonly entity: Entity;
export interface Result<E extends Entity> {
readonly entity: E;
}

export type Signature<Id, Entity extends Id> =
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;

export default Signature;
4 changes: 2 additions & 2 deletions src/tests/countEntities/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import 'mocha'; // tslint:disable-line:no-import-side-effect
import * as assert from 'power-assert';
import Facade from '../../Facade';
import filterTest from '../utils/filterTest';
import { TestEntity, TestId } from '../utils/testEntity';
import { TestEntity } from '../utils/testEntity';

export default (facade: Facade<TestId, TestEntity>) => {
export default (facade: Facade<TestEntity>) => {
describe('countEntities', () => {
filterTest({
assertAllEntitiesFilter: async (filter) => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/createEntity/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import * as assertRejects from 'assert-rejects';
import 'mocha'; // tslint:disable-line:no-import-side-effect
import ConflictingEntityError from '../../errors/ConflictingEntityError';
import Facade from '../../Facade';
import { TestEntity, testEntity, testId, TestId } from '../utils/testEntity';
import { TestEntity, testEntity, testId } from '../utils/testEntity';

export default (facade: Facade<TestId, TestEntity>) => {
export default (facade: Facade<TestEntity>) => {
describe('createEntity', () => {
it('should not error when identifier does not exist', async () => {
await facade.createEntity({ id: testId, entity: testEntity });
Expand Down
4 changes: 2 additions & 2 deletions src/tests/getEntities/filterTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import Facade from '../../Facade';
import Pagination from '../../types/Pagination';
import Sort from '../../types/Sort';
import filterTest, { firstEntity, secondEntity } from '../utils/filterTest';
import { TestEntity, TestId } from '../utils/testEntity';
import { TestEntity } from '../utils/testEntity';

export default (facade: Facade<TestId, TestEntity>) => {
export default (facade: Facade<TestEntity>) => {
const sort: Sort<TestEntity> = {};
const pagination: Pagination = { cursor: undefined, forward: true, limit: 2 };

Expand Down
Loading