Skip to content
This repository was archived by the owner on Jun 22, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,149 changes: 962 additions & 1,187 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"check-coverage": true
},
"dependencies": {
"@js-entity-repos/core": "^4.1.2",
"@js-entity-repos/core": "^6.0.2",
"knex": "^0.14.2",
"lodash": "^4.17.4"
},
Expand Down
41 changes: 24 additions & 17 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,45 @@
### Usage
1. Install it with `npm i @js-entity-repos/knex`.
1. For each entity you will need to do the following.
1. [Create Id and Entity interfaces](#id-and-entity-interface).
1. [Create a facade config](#facade-config).
1. [Construct the facade with the config and interfaces](#calling-the-facade).
1. [Create an Entity interfaces](#entity-interface).
1. [Create a factory config](#factory-config).
1. [Construct the facade](#construct-the-facade).
1. [Use the facade](https://github.com/js-entity-repos/core/blob/master/docs/facade.md).

### Id and Entity Interface
### Entity Interface

```ts
export interface TodoId {
readonly id: string;
}
import Entity from '@js-entity-repos/core/dist/types/Entity';

export interface TodoEntity extends TodoId {
export interface TodoEntity extends Entity {
readonly description: string;
readonly completed: boolean;
}
```

### Facade Config
### Factory Config

```ts
import FacadeConfig from '@js-entity-repos/knex/dist/Config';
import FactoryConfig from '@js-entity-repos/knex/dist/FactoryConfig';
import connectToDb from '@js-entity-repos/knex/dist/utils/connectToDb';

const todoFacadeConfig: FacadeConfig = {
constructDocument: (id, patch) => {
// Converts an entity to a document for the database.
return { ...patch, ...id }
const todoFactoryConfig: FactoryConfig<TodoEntity> = {
constructDocument: (patch) => {
// Optional property that converts an entity to a document for the database.
return patch;
},
constructEntity: (document) => {
// Converts a document from the database to an entity.
// Optional property that converts a document from the database to an entity.
return document;
},
constructFilter: (filter) => {
// Optional property that converts an entity filter to a filter for the DB.
return filter;
},
constructSort: (sort) => {
// Optional property that converts an entity sort to a sort for the DB.
return sort;
}.
db: connectToDb({
client: 'mysql',
connection: {
Expand All @@ -46,6 +52,7 @@ const todoFacadeConfig: FacadeConfig = {
user: 'todouser',
},
}),
defaultPaginationLimit: 100, // Optional property.
entityName: 'todo',
tableName: 'todos',
};
Expand All @@ -54,7 +61,7 @@ const todoFacadeConfig: FacadeConfig = {
### Construct the Facade

```ts
import facade from '@js-entity-repos/knex/dist/facade';
import factory from '@js-entity-repos/knex/dist/factory';

const todosFacade = facade<TodoId, TodoEntity>(todoFacadeConfig);
const todosFacade = factory(todoFactoryConfig);
```
11 changes: 0 additions & 11 deletions src/Config.ts

This file was deleted.

17 changes: 17 additions & 0 deletions src/FacadeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Entity from '@js-entity-repos/core/dist/types/Entity';
import Filter from '@js-entity-repos/core/dist/types/Filter';
import Sort from '@js-entity-repos/core/dist/types/Sort';
import * as knex from 'knex';

export type Document = any;

export default interface FacadeConfig<E extends Entity> {
readonly constructDocument: (patch: Partial<E>) => Document;
readonly constructEntity: (document: Document) => E;
readonly constructFilter: (filter: Filter<E>) => any;
readonly constructSort: (sort: Sort<E>) => any;
readonly db: () => Promise<knex>;
readonly defaultPaginationLimit: number;
readonly entityName: string;
readonly tableName: string;
}
17 changes: 17 additions & 0 deletions src/FactoryConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Entity from '@js-entity-repos/core/dist/types/Entity';
import Filter from '@js-entity-repos/core/dist/types/Filter';
import Sort from '@js-entity-repos/core/dist/types/Sort';
import * as knex from 'knex';

export type Document = any;

export default interface FacadeConfig<E extends Entity> {
readonly constructDocument?: (patch: Partial<E>) => Document;
readonly constructEntity?: (document: Document) => E;
readonly constructFilter?: (filter: Filter<E>) => any;
readonly constructSort?: (sort: Sort<E>) => any;
readonly db: () => Promise<knex>;
readonly defaultPaginationLimit?: number;
readonly entityName: string;
readonly tableName: string;
}
25 changes: 0 additions & 25 deletions src/facade.ts

This file was deleted.

13 changes: 6 additions & 7 deletions src/facade.test.ts → src/factory.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import facadeTest from '@js-entity-repos/core/dist/tests';
import { TestEntity, TestId } from '@js-entity-repos/core/dist/tests/utils/testEntity';
import { TestEntity } from '@js-entity-repos/core/dist/tests/utils/testEntity';
import { config } from 'dotenv';
import 'mocha'; // tslint:disable-line:no-import-side-effect
import facade from './facade';
import factory from './factory';
import connectToDb from './utils/connectToDb';
config();

Expand All @@ -21,18 +21,17 @@ const db = connectToDb({
const tableName = 'testentities';

before(async () => {
await Promise.resolve(db.schema.dropTableIfExists(tableName));
await Promise.resolve(db.schema.createTableIfNotExists(tableName, async (table) => {
const schema = (await db()).schema;
await Promise.resolve(schema.dropTableIfExists(tableName));
await Promise.resolve(schema.createTableIfNotExists(tableName, async (table) => {
table.string('id').unique();
table.string('stringProp');
table.float('numberProp');
table.boolean('booleanProp');
}));
});

facadeTest(facade<TestId, TestEntity>({
constructDocument: (id, patch) => ({ ...patch, ...id }),
constructEntity: (document) => document,
facadeTest(factory<TestEntity>({
db,
entityName: 'Test Entity',
tableName,
Expand Down
33 changes: 33 additions & 0 deletions src/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Facade from '@js-entity-repos/core/dist/Facade';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from './FacadeConfig';
import FactoryConfig from './FactoryConfig';
import countEntities from './functions/countEntities';
import createEntity from './functions/createEntity';
import getEntities from './functions/getEntities';
import getEntity from './functions/getEntity';
import patchEntity from './functions/patchEntity';
import removeEntities from './functions/removeEntities';
import removeEntity from './functions/removeEntity';
import replaceEntity from './functions/replaceEntity';

export default <E extends Entity>(factoryConfig: FactoryConfig<E>): Facade<E> => {
const facadeConfig: FacadeConfig<E> = {
constructDocument: (patch) => patch,
constructEntity: (document) => document,
constructFilter: (filter) => filter,
constructSort: (sort) => sort,
defaultPaginationLimit: 100,
...factoryConfig,
};
return {
countEntities: countEntities<E>(facadeConfig),
createEntity: createEntity<E>(facadeConfig),
getEntities: getEntities<E>(facadeConfig),
getEntity: getEntity<E>(facadeConfig),
patchEntity: patchEntity<E>(facadeConfig),
removeEntities: removeEntities<E>(facadeConfig),
removeEntity: removeEntity<E>(facadeConfig),
replaceEntity: replaceEntity<E>(facadeConfig),
};
};
9 changes: 5 additions & 4 deletions src/functions/countEntities.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import CountEntities from '@js-entity-repos/core/dist/signatures/CountEntities';
import Config from '../Config';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from '../FacadeConfig';
import filterEntities from '../utils/filterEntities';

export default <Id, Entity extends Id>(config: Config<Id, Entity>): CountEntities<Entity> => {
return async ({ filter }) => {
const table = config.db.table(config.tableName);
export default <E extends Entity>(config: FacadeConfig<E>): CountEntities<E> => {
return async ({ filter = {} }) => {
const table = (await config.db()).table(config.tableName);
const [result] = await Promise.resolve(filterEntities(table, filter).count());
return { count: result['count(*)'] };
};
Expand Down
9 changes: 5 additions & 4 deletions src/functions/createEntity.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import ConflictingEntityError from '@js-entity-repos/core/dist/errors/ConflictingEntityError';
import CreateEntity from '@js-entity-repos/core/dist/signatures/CreateEntity';
import Config from '../Config';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from '../FacadeConfig';

const conflictErrorCode = 1062;

export default <Id, Entity extends Id>(config: Config<Id, Entity>): CreateEntity<Id, Entity> => {
export default <E extends Entity>(config: FacadeConfig<E>): CreateEntity<E> => {
return async ({ id, entity }) => {
const table = config.db.table(config.tableName);
const document = config.constructDocument(id, entity);
const table = (await config.db()).table(config.tableName);
const document = config.constructDocument({ ...entity as any, id });
try {
await Promise.resolve(table.insert(document));
} catch (err) {
Expand Down
22 changes: 16 additions & 6 deletions src/functions/getEntities.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import GetEntities from '@js-entity-repos/core/dist/signatures/GetEntities';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import Sort from '@js-entity-repos/core/dist/types/Sort';
import createCursorFromEntity from '@js-entity-repos/core/dist/utils/createCursorFromEntity';
import createPaginationFilter from '@js-entity-repos/core/dist/utils/createPaginationFilter';
import { first, last, mapValues } from 'lodash';
import Config from '../Config';
import FacadeConfig from '../FacadeConfig';
import filterEntities from '../utils/filterEntities';

const xor = (conditionA: boolean, conditionB: boolean) => {
return (conditionA && !conditionB) || (!conditionA && conditionB);
};

export default <Id, Entity extends Id>(config: Config<Id, Entity>): GetEntities<Entity> => {
return async ({ filter, sort, pagination }) => {
const table = config.db.table(config.tableName);
export default <E extends Entity>(config: FacadeConfig<E>): GetEntities<E> => {
const defaultPagination = {
cursor: undefined,
forward: true,
limit: config.defaultPaginationLimit,
};
const defaultSort = { id: true } as Sort<E>;
return async ({ filter = {}, sort = defaultSort, pagination = defaultPagination }) => {
const table = (await config.db()).table(config.tableName);

const paginationFilter = createPaginationFilter(pagination, sort);
const fullFilter = { $and: [filter, paginationFilter] };
const knexSort = mapValues(sort, (sortValue: boolean) => {
const constructedFilter = config.constructFilter(fullFilter);
const constructedSort = config.constructSort(sort);
const knexSort = mapValues(constructedSort, (sortValue): string => {
return !xor(pagination.forward, sortValue) ? 'asc' : 'desc';
});

const filterQuery = filterEntities(table, fullFilter);
const filterQuery = filterEntities(table, constructedFilter);
const sortQuery = Object.keys(knexSort).reduce((result, sortKey) => {
return result.orderBy(sortKey, (knexSort as any)[sortKey]);
}, filterQuery);
Expand Down
13 changes: 8 additions & 5 deletions src/functions/getEntity.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import MissingEntityError from '@js-entity-repos/core/dist/errors/MissingEntityError';
import GetEntity from '@js-entity-repos/core/dist/signatures/GetEntity';
import Config from '../Config';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from '../FacadeConfig';
import constructIdFilter from '../utils/constructIdFilter';
import filterEntities from '../utils/filterEntities';

export default <Id, Entity extends Id>(config: Config<Id, Entity>): GetEntity<Id, Entity> => {
return async ({ id }) => {
const table = config.db.table(config.tableName);
const document = await Promise.resolve(filterEntities(table, id).first());
export default <E extends Entity>(config: FacadeConfig<E>): GetEntity<E> => {
return async ({ id, filter = {} }) => {
const table = (await config.db()).table(config.tableName);
const constructedFilter = constructIdFilter({ id, filter, config });
const document = await Promise.resolve(filterEntities(table, constructedFilter).first());

if (document === undefined || document === null) {
throw new MissingEntityError(config.entityName, id);
Expand Down
12 changes: 0 additions & 12 deletions src/functions/overwriteEntity.ts

This file was deleted.

16 changes: 10 additions & 6 deletions src/functions/patchEntity.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import PatchEntity from '@js-entity-repos/core/dist/signatures/PatchEntity';
import Config from '../Config';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from '../FacadeConfig';
import constructIdFilter from '../utils/constructIdFilter';
import filterEntities from '../utils/filterEntities';
import getEntity from './getEntity';

export default <Id, Entity extends Id>(config: Config<Id, Entity>): PatchEntity<Id, Entity> => {
return async ({ id, patch }) => {
const table = config.db.table(config.tableName);
await Promise.resolve(filterEntities(table, id).update(patch));
return getEntity<Id, Entity>(config)({ id });
export default <E extends Entity>(config: FacadeConfig<E>): PatchEntity<E> => {
return async ({ id, patch, filter = {} }) => {
const table = (await config.db()).table(config.tableName);
const document = config.constructDocument({ ...patch as any, id });
const constructedFilter = constructIdFilter({ id, filter, config });
await Promise.resolve(filterEntities(table, constructedFilter).update(document));
return getEntity<E>(config)({ id, filter });
};
};
12 changes: 7 additions & 5 deletions src/functions/removeEntities.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import RemoveEntities from '@js-entity-repos/core/dist/signatures/RemoveEntities';
import Config from '../Config';
import Entity from '@js-entity-repos/core/dist/types/Entity';
import FacadeConfig from '../FacadeConfig';
import filterEntities from '../utils/filterEntities';

export default <Id, Entity extends Id>(config: Config<Id, Entity>): RemoveEntities<Entity> => {
return async ({ filter }) => {
const table = config.db.table(config.tableName);
await Promise.resolve(filterEntities(table, filter).delete());
export default <E extends Entity>(config: FacadeConfig<E>): RemoveEntities<E> => {
return async ({ filter = {} }) => {
const table = (await config.db()).table(config.tableName);
const constructedFilter = config.constructFilter(filter);
await Promise.resolve(filterEntities(table, constructedFilter).delete());
};
};
Loading