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

Commit b217e33

Browse files
authored
feat: Removes Id interface requirement. (#1)
BREAKING CHANGE: Removes Id interface from all signatures.
1 parent aadafa0 commit b217e33

38 files changed

+184
-162
lines changed

docs/facade.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ The facade contains common functions for storage and retrieval of entities from
1414

1515
The functions have some common options that they use.
1616

17-
- [Entity](./options.md#entity)
1817
- [Id](./options.md#id)
18+
- [Entity](./options.md#entity)
1919
- [Patch](./options.md#patch)
2020
- [Filter](./options.md#filter)
2121
- [Sort](./options.md#sort)

docs/functions.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Creates a new entity using the `entity` option if no entity exists that matches
2828

2929
```ts
3030
const { entity } = await facade.createEntity({
31-
id: { id: 'example_id' },
31+
id: 'example_id',
3232
entity: { id: 'example_id', foo: 'bar' },
3333
});
3434
```
@@ -63,7 +63,7 @@ Retrieves a single entity that matches the `id` option.
6363

6464
```ts
6565
const { entity } = await facade.getEntity({
66-
id: { id: 'example_id' },
66+
id: 'example_id',
6767
});
6868
```
6969

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

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

8787
```ts
8888
const { entity } = await facade.patchEntity({
89-
id: { id: 'example_id' },
89+
id: 'example_id',
9090
patch: { foo: 'bar' },
9191
});
9292
```
@@ -109,7 +109,7 @@ Removes an entity that matches the `id` option.
109109

110110
```ts
111111
await facade.removeEntity({
112-
id: { id: 'example_id' },
112+
id: 'example_id',
113113
});
114114
```
115115

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

121121
```ts
122122
await facade.upsertEntity({
123-
id: { id: 'example_id' },
123+
id: 'example_id',
124124
entity: { id: 'example_id', foo: 'bar' },
125125
});
126126
```
127127

128-
This package contains the [upsert entity tests](../src/tests/upsertsEntity) and the [upsert entity signature](../src/signatures/UpsertEntity.ts) for this function.
128+
This package contains the [upsert entity tests](../src/tests/upsertsEntity) and the [upsert entity signature](../src/signatures/UpsertEntity.ts) for this function.

docs/options.md

+7-13
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,17 @@ The [facade](./facade.md) [functions](./functions.md) have some common options t
1010
- [Pagination](#pagination)
1111

1212
### Id
13-
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.
14-
15-
This interface is user-defined hence not contained in this package, the interface below demonstrates what this will look like in most cases.
16-
17-
```ts
18-
interface Id {
19-
readonly id: string;
20-
}
21-
```
13+
This is a string that uniquely identifies an entity.
2214

2315
### Entity
2416
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.
2517

26-
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).
18+
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.
2719

2820
```ts
29-
interface TodoEntity extends Id {
21+
import Entity from '@js-entity-repos/core/dist/types/Entity';
22+
23+
interface TodoEntity extends Entity {
3024
readonly description: string;
3125
readonly completed: boolean;
3226
}
@@ -44,8 +38,8 @@ This is an object where a key represents the entity property to be sorted and th
4438
This package contains the [TypeScript Sort type definition](../src/types/Sort.ts).
4539

4640
### Pagination
47-
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.
41+
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/).
4842

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

5145
This package also contains the [TypeScript Pagination interface](../src/types/Pagination.ts) and the [TypeScript Cursor type definition](../src/types/Cursor.ts).

readme.md

+10
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,13 @@
44
### Usage
55
- Install it with `npm i @js-entity-repos/core`.
66
- Understand it by reading the [documenation](./docs/facade.md).
7+
8+
### FeathersJS
9+
The project has some similarities with parts of the [FeathersJS framework](feathersjs.com), but unfortunately I've found the following issues with FeathersJS.
10+
11+
- 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/).
12+
- Their service interface is missing some functions provided in the [Facade](./docs/facade.md) here.
13+
- Their errors take messages instead of parameters making it harder to support localisation.
14+
15+
### Thanks
16+
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.

src/Facade.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ import PatchEntity from './signatures/PatchEntity';
77
import RemoveEntities from './signatures/RemoveEntities';
88
import RemoveEntity from './signatures/RemoveEntity';
99
import UpsertEntity from './signatures/UpsertEntity';
10+
import Entity from './types/Entity';
1011

11-
export default interface Facade<Id, Entity extends Id> {
12-
readonly getEntity: GetEntity<Id, Entity>;
13-
readonly createEntity: CreateEntity<Id, Entity>;
14-
readonly overwriteEntity: OverwriteEntity<Id, Entity>;
15-
readonly patchEntity: PatchEntity<Id, Entity>;
16-
readonly removeEntity: RemoveEntity<Id>;
17-
readonly getEntities: GetEntities<Entity>;
18-
readonly countEntities: CountEntities<Entity>;
19-
readonly removeEntities: RemoveEntities<Entity>;
20-
readonly upsertEntity: UpsertEntity<Id, Entity>;
12+
export default interface Facade<E extends Entity> {
13+
readonly getEntity: GetEntity<E>;
14+
readonly createEntity: CreateEntity<E>;
15+
readonly overwriteEntity: OverwriteEntity<E>;
16+
readonly patchEntity: PatchEntity<E>;
17+
readonly removeEntity: RemoveEntity;
18+
readonly getEntities: GetEntities<E>;
19+
readonly countEntities: CountEntities<E>;
20+
readonly removeEntities: RemoveEntities<E>;
21+
readonly upsertEntity: UpsertEntity<E>;
2122
}

src/errors/ConflictingEntityError.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// tslint:disable:no-class
22
import { BaseError } from 'make-error';
33

4-
export default class ConflictingEntityError<Id> extends BaseError {
5-
constructor(public entityName: string, public entityId: Id) {
4+
export default class ConflictingEntityError extends BaseError {
5+
constructor(public entityName: string, public entityId: string) {
66
super();
77
}
88
}

src/errors/MissingEntityError.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// tslint:disable:no-class
22
import { BaseError } from 'make-error';
33

4-
export default class MissingEntityError<Id> extends BaseError {
5-
constructor(public entityName: string, public entityId: Id) {
4+
export default class MissingEntityError extends BaseError {
5+
constructor(public entityName: string, public entityId: string) {
66
super();
77
}
88
}

src/signatures/CountEntities.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
import Entity from '../types/Entity';
12
import Filter from '../types/Filter';
23

3-
export interface Opts<Entity> {
4-
readonly filter: Filter<Entity>;
4+
export interface Opts<E extends Entity> {
5+
readonly filter: Filter<E>;
56
}
67

78
export interface Result {
89
readonly count: number;
910
}
1011

11-
export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result>;
12+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result>;
1213

1314
export default Signature;

src/signatures/CreateEntity.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
export interface Opts<Id, Entity extends Id> {
2-
readonly id: Id;
3-
readonly entity: Entity;
1+
import Entity from '../types/Entity';
2+
3+
export interface Opts<E extends Entity> {
4+
readonly id: string;
5+
readonly entity: E;
46
}
57

6-
export interface Result<Entity> {
7-
readonly entity: Entity;
8+
export interface Result<E extends Entity> {
9+
readonly entity: E;
810
}
911

10-
export type Signature<Id, Entity extends Id> =
11-
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
12+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;
1213

1314
export default Signature;

src/signatures/GetEntities.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import Cursor from '../types/Cursor';
2+
import Entity from '../types/Entity';
23
import Filter from '../types/Filter';
34
import Pagination from '../types/Pagination';
45
import Sort from '../types/Sort';
56

6-
export interface Opts<Entity> {
7-
readonly filter: Filter<Entity>;
8-
readonly sort: Sort<Entity>;
7+
export interface Opts<E extends Entity> {
8+
readonly filter: Filter<E>;
9+
readonly sort: Sort<E>;
910
readonly pagination: Pagination;
1011
}
1112

12-
export interface Result<Entity> {
13-
readonly entities: Entity[];
13+
export interface Result<E extends Entity> {
14+
readonly entities: E[];
1415
readonly nextCursor: Cursor;
1516
readonly previousCursor: Cursor;
1617
}
1718

18-
export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result<Entity>>;
19+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;
1920

2021
export default Signature;

src/signatures/GetEntity.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
export interface Opts<Id> {
2-
readonly id: Id;
1+
import Entity from '../types/Entity';
2+
3+
export interface Opts {
4+
readonly id: string;
35
}
46

5-
export interface Result<Entity> {
6-
readonly entity: Entity;
7+
export interface Result<E extends Entity> {
8+
readonly entity: E;
79
}
810

9-
export type Signature<Id, Entity extends Id> = (opts: Opts<Id>) => Promise<Result<Entity>>;
11+
export type Signature<E extends Entity> = (opts: Opts) => Promise<Result<E>>;
1012

1113
export default Signature;

src/signatures/OverwriteEntity.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
export interface Opts<Id, Entity> {
2-
readonly id: Id;
3-
readonly entity: Entity;
1+
import Entity from '../types/Entity';
2+
3+
export interface Opts<E extends Entity> {
4+
readonly id: string;
5+
readonly entity: E;
46
}
57

6-
export interface Result<Entity> {
7-
readonly entity: Entity;
8+
export interface Result<E extends Entity> {
9+
readonly entity: E;
810
}
911

10-
export type Signature<Id, Entity extends Id> =
11-
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
12+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;
1213

1314
export default Signature;

src/signatures/PatchEntity.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
export interface Opts<Id, Entity> {
2-
readonly id: Id;
3-
readonly patch: Partial<Entity>;
1+
import Entity from '../types/Entity';
2+
3+
export interface Opts<E extends Entity> {
4+
readonly id: string;
5+
readonly patch: Partial<E>;
46
}
57

6-
export interface Result<Entity> {
7-
readonly entity: Entity;
8+
export interface Result<E extends Entity> {
9+
readonly entity: E;
810
}
911

10-
export type Signature<Id, Entity extends Id> =
11-
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
12+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;
1213

1314
export default Signature;

src/signatures/RemoveEntities.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import Entity from '../types/Entity';
12
import Filter from '../types/Filter';
23

3-
export interface Opts<Entity> {
4-
readonly filter: Filter<Entity>;
4+
export interface Opts<E extends Entity> {
5+
readonly filter: Filter<E>;
56
}
67

78
export type Result = void;
89

9-
export type Signature<Entity> = (opts: Opts<Entity>) => Promise<Result>;
10+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result>;
1011

1112
export default Signature;

src/signatures/RemoveEntity.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
export interface Opts<Id> {
2-
readonly id: Id;
1+
export interface Opts {
2+
readonly id: string;
33
}
44

55
export type Result = void;
66

7-
export type Signature<Id> = (opts: Opts<Id>) => Promise<Result>;
7+
export type Signature = (opts: Opts) => Promise<Result>;
88

99
export default Signature;

src/signatures/UpsertEntity.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
export interface Opts<Id, Entity> {
2-
readonly id: Id;
3-
readonly entity: Entity;
1+
import Entity from '../types/Entity';
2+
3+
export interface Opts<E extends Entity> {
4+
readonly id: string;
5+
readonly entity: E;
46
}
57

6-
export interface Result<Entity> {
7-
readonly entity: Entity;
8+
export interface Result<E extends Entity> {
9+
readonly entity: E;
810
}
911

10-
export type Signature<Id, Entity extends Id> =
11-
(opts: Opts<Id, Entity>) => Promise<Result<Entity>>;
12+
export type Signature<E extends Entity> = (opts: Opts<E>) => Promise<Result<E>>;
1213

1314
export default Signature;

src/tests/countEntities/test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import 'mocha'; // tslint:disable-line:no-import-side-effect
22
import * as assert from 'power-assert';
33
import Facade from '../../Facade';
44
import filterTest from '../utils/filterTest';
5-
import { TestEntity, TestId } from '../utils/testEntity';
5+
import { TestEntity } from '../utils/testEntity';
66

7-
export default (facade: Facade<TestId, TestEntity>) => {
7+
export default (facade: Facade<TestEntity>) => {
88
describe('countEntities', () => {
99
filterTest({
1010
assertAllEntitiesFilter: async (filter) => {

src/tests/createEntity/test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import * as assertRejects from 'assert-rejects';
22
import 'mocha'; // tslint:disable-line:no-import-side-effect
33
import ConflictingEntityError from '../../errors/ConflictingEntityError';
44
import Facade from '../../Facade';
5-
import { TestEntity, testEntity, testId, TestId } from '../utils/testEntity';
5+
import { TestEntity, testEntity, testId } from '../utils/testEntity';
66

7-
export default (facade: Facade<TestId, TestEntity>) => {
7+
export default (facade: Facade<TestEntity>) => {
88
describe('createEntity', () => {
99
it('should not error when identifier does not exist', async () => {
1010
await facade.createEntity({ id: testId, entity: testEntity });

src/tests/getEntities/filterTest.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import Facade from '../../Facade';
44
import Pagination from '../../types/Pagination';
55
import Sort from '../../types/Sort';
66
import filterTest, { firstEntity, secondEntity } from '../utils/filterTest';
7-
import { TestEntity, TestId } from '../utils/testEntity';
7+
import { TestEntity } from '../utils/testEntity';
88

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

0 commit comments

Comments
 (0)