Skip to content

Commit

Permalink
refactor(dtos)!: integrate DTOFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
unicornware committed May 28, 2021
1 parent b861719 commit ec86961
Show file tree
Hide file tree
Showing 16 changed files with 621 additions and 564 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,11 @@ export interface IAbstractMangoRepository<
readonly validator: IMangoValidator<E>

clear(): OrPromise<boolean>
create<F extends Path<E>>(dto: CreateEntityDTO<E, F>): OrPromise<E>
create(dto: CreateEntityDTO<E>): OrPromise<E>
delete(uid?: OneOrMany<UID>, should_exist?: boolean): OrPromise<UID[]>
patch<F extends Path<E>>(
uid: UID,
dto?: PatchEntityDTO<E, F>,
rfields?: string[]
): OrPromise<E>
patch(uid: UID, dto?: PatchEntityDTO<E>, rfields?: string[]): OrPromise<E>
setCache(collection?: E[]): OrPromise<MangoCacheRepo<E>>
save<F extends Path<E>>(dto?: OneOrMany<EntityDTO<E, F>>): OrPromise<E[]>
save(dto?: OneOrMany<EntityDTO<E>>): OrPromise<E[]>
}
```

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"types": "./dist/index.d.ts",
"scripts": {
"clean": "rm -rf node_modules || true",
"clear:jest": "jest --clearCache",
"postinstall": "husky-run install",
"format": "prettier --write \"./\"",
"lint": "eslint . --ext js,ts --fix --cache",
Expand Down Expand Up @@ -91,7 +90,7 @@
"eslint-plugin-tree-shaking": "latest",
"faker": "latest",
"husky": "4.3.8",
"jest": "27.0.0-next.8",
"jest": "next",
"jest-extended": "latest",
"lint-staged": "latest",
"npm": "latest",
Expand Down
13 changes: 6 additions & 7 deletions src/abstracts/__tests__/__fixtures__/cars-repo.fixture.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { CreateEntityDTO, EntityDTO, PatchEntityDTO } from '@/dtos'
import type { UID } from '@/types'
import type { OneOrMany, OrPromise, Path } from '@flex-development/tutils'
import type { OneOrMany, OrPromise } from '@flex-development/tutils'
import type {
CarDTOFilter,
CarParams,
CarQuery,
CarUID,
Expand All @@ -20,21 +21,19 @@ export default class CarsRepo extends AbstractMangoRepository<
CarParams,
CarQuery
> {
create<F extends Path<ICar>>(dto: CreateEntityDTO<ICar, F>): OrPromise<ICar> {
create(dto: CreateEntityDTO<ICar, CarDTOFilter>): OrPromise<ICar> {
throw new Error('Method not implemented')
}

patch<F extends Path<ICar>>(
patch(
uid: UID,
dto: PatchEntityDTO<ICar, F>,
dto: PatchEntityDTO<ICar, CarDTOFilter>,
rfields?: string[]
): OrPromise<ICar> {
throw new Error('Method not implemented')
}

save<F extends Path<ICar>>(
dto: OneOrMany<EntityDTO<ICar, F>>
): OrPromise<ICar[]> {
save(dto: OneOrMany<EntityDTO<ICar, CarDTOFilter>>): OrPromise<ICar[]> {
throw new Error('Method not implemented')
}
}
16 changes: 11 additions & 5 deletions src/abstracts/__tests__/mango-repo.abstract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe('unit:abstracts/AbstractMangoRepository', () => {
const euid = uid.trim()

// Act
const result = TestSubject.formatCreateEntityDTO(
const result = TestSubject.formatCreateEntityDTO<ICar, CarUID>(
dto as CreateEntityDTO<ICar>,
COLLECTION,
MOPTIONS
Expand All @@ -147,7 +147,7 @@ describe('unit:abstracts/AbstractMangoRepository', () => {

// Act
try {
TestSubject.formatCreateEntityDTO(
TestSubject.formatCreateEntityDTO<ICar, CarUID>(
dto as CreateEntityDTO<ICar>,
COLLECTION,
MOPTIONS
Expand All @@ -173,7 +173,13 @@ describe('unit:abstracts/AbstractMangoRepository', () => {
const spy_findOneOrFail = jest.spyOn(TestSubjectAbstract, 'findOneOrFail')

// Act
TestSubject.formatPatchEntityDTO(ENTITY_UID, {}, [], COLLECTION, MOPTIONS)
TestSubject.formatPatchEntityDTO<ICar, CarUID>(
ENTITY_UID,
{},
[],
COLLECTION,
MOPTIONS
)

// Expect
expect(spy_findOneOrFail).toBeCalledTimes(1)
Expand All @@ -186,7 +192,7 @@ describe('unit:abstracts/AbstractMangoRepository', () => {
const rfields = ['foo']

// Act
const result = TestSubject.formatPatchEntityDTO(
const result = TestSubject.formatPatchEntityDTO<ICar, CarUID>(
ENTITY_UID,
dto,
rfields,
Expand All @@ -204,7 +210,7 @@ describe('unit:abstracts/AbstractMangoRepository', () => {
const dto = { make: 'MAKE' }

// Act
const result = TestSubject.formatPatchEntityDTO(
const result = TestSubject.formatPatchEntityDTO<ICar, CarUID>(
ENTITY_UID,
dto,
[],
Expand Down
36 changes: 13 additions & 23 deletions src/abstracts/mango-repo.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ import type {
ObjectPlain,
ObjectUnknown,
OneOrMany,
OrPromise,
Path
OrPromise
} from '@flex-development/tutils'
import type { ClassType } from 'class-transformer-validator'
import type { Debugger } from 'debug'
Expand Down Expand Up @@ -253,25 +252,23 @@ export default abstract class AbstractMangoRepository<
*
* Throws a `409 CONFLICT` error if an entity with the same uid exists.
*
* @template AF - Object field paths of `dto`
* @template AE - Entity
* @template AU - Name of entity uid field
* @template AP - Repository search parameters (query criteria and options)
*
* @param {CreateEntityDTO<AE, AF>} dto - Data to create new entity
* @param {CreateEntityDTO<AE>} dto - Data to create new entity
* @param {DocumentArray<AE, AU>} [collection] - Document collection
* @param {MingoOptions<AU>} [mingo_options] - `mingo` options
* @param {typeof MINGO} [mingo] - MongoDB query language client
* @return {AE} Formatted `dto` casted as entity
* @throws {Exception}
*/
static formatCreateEntityDTO<
AF extends Path<AE>,
AE extends ObjectPlain = ObjectUnknown,
AU extends string = DUID,
AP extends MangoSearchParams<AE> = MangoSearchParams<AE>
>(
dto: CreateEntityDTO<AE, AF>,
dto: CreateEntityDTO<AE>,
collection: DocumentArray<AE, AU> = [],
mingo_options: MingoOptions<AU> = { idKey: 'id' as AU },
mingo: typeof MINGO = MINGO
Expand Down Expand Up @@ -326,13 +323,12 @@ export default abstract class AbstractMangoRepository<
*
* Throws if the entity isn't found.
*
* @template AF - Object field paths of `dto`
* @template AE - Entity
* @template AU - Name of entity uid field
* @template AP - Repository search parameters (query criteria and options)
*
* @param {UID} uid - Entity uid
* @param {PatchEntityDTO<AE, AF>} [dto] - Data to patch entity
* @param {PatchEntityDTO<AE>} [dto] - Data to patch entity
* @param {string[]} [rfields] - Additional readonly fields
* @param {DocumentArray<AE, AU>} [collection] - Document collection
* @param {MingoOptions<AU>} [mingo_options] - `mingo` options
Expand All @@ -341,13 +337,12 @@ export default abstract class AbstractMangoRepository<
* @throws {Exception}
*/
static formatPatchEntityDTO<
AF extends Path<AE>,
AE extends ObjectPlain = ObjectUnknown,
AU extends string = DUID,
AP extends MangoSearchParams<AE> = MangoSearchParams<AE>
>(
uid: UID,
dto: PatchEntityDTO<AE, AF> = {},
dto: PatchEntityDTO<AE> = {} as PatchEntityDTO<AE>,
rfields: string[] = [],
collection: DocumentArray<AE, AU> = [],
mingo_options: MingoOptions<AU> = { idKey: 'id' as AU },
Expand All @@ -367,7 +362,7 @@ export default abstract class AbstractMangoRepository<
rfields = uniq([mingo_options.idKey as string].concat(rfields))

// Return entity merged with dto
return merge({}, entity, { ...omit(dto, rfields) }) as AE
return (merge({}, entity, { ...omit(dto, rfields) }) as unknown) as AE
} catch (error) {
const code = ExceptionStatusCode.INTERNAL_SERVER_ERROR
const { message, stack } = error
Expand Down Expand Up @@ -436,35 +431,30 @@ export default abstract class AbstractMangoRepository<

/**
* @abstract
* @template F - Object field paths of `dto`
* @param {CreateEntityDTO<E, F>} dto - Data to create new entity
* @param {CreateEntityDTO<E>} dto - Data to create new entity
* @return {OrPromise<E>} New entity
* @throws {Exception}
*/
abstract create<F extends Path<E>>(dto: CreateEntityDTO<E, F>): OrPromise<E>
abstract create(dto: CreateEntityDTO<E>): OrPromise<E>

/**
* @abstract
* @template F - Object field paths of `dto`
* @param {UID} uid - Entity uid
* @param {PatchEntityDTO<E, F>} [dto] - Data to patch entity
* @param {PatchEntityDTO<E>} [dto] - Data to patch entity
* @param {string[]} [rfields] - Additional readonly fields
* @return {Promise<E>} Updated entity
* @throws {Exception}
*/
abstract patch<F extends Path<E>>(
abstract patch(
uid: UID,
dto?: PatchEntityDTO<E, F>,
dto?: PatchEntityDTO<E>,
rfields?: string[]
): OrPromise<E>

/**
* @abstract
* @template F - Object field paths of `dto`
* @param {OneOrMany<EntityDTO<E, F>>} [dto] - Entities to upsert
* @param {OneOrMany<EntityDTO<E>>} [dto] - Entities to upsert
* @return {Promise<E[]>} New or updated entities
*/
abstract save<F extends Path<E>>(
dto?: OneOrMany<EntityDTO<E, F>>
): OrPromise<E[]>
abstract save(dto?: OneOrMany<EntityDTO<E>>): OrPromise<E[]>
}
25 changes: 20 additions & 5 deletions src/dtos/create-entity.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { DeepPick, ObjectPlain, Path } from '@flex-development/tutils'
import type { DTOFilter } from '@/types'
import type {
DeepPartialByRequired,
ObjectPlain,
ObjectUnknown
} from '@flex-development/tutils'

/**
* @file Data Transfer Objects - CreateEntityDTO
Expand All @@ -8,10 +13,20 @@ import type { DeepPick, ObjectPlain, Path } from '@flex-development/tutils'
/**
* Data used to create a new entity.
*
* Constructs a type where properties `F['pick']` are required, and properties
* `F['omit']` will be omitted.
*
* Other properties will remain untouched.
*
* @template E - Entity
* @template P - Object field paths of dto
* @template F - DTO filter object
*
* @example
* interface IUser{ ... }
* type Filter = DTOFilter<IUser, 'first_name', { created_at: never }>
* type CreateUserDTO = CreateEntityDTO<IUser, Filter>
*/
export type CreateEntityDTO<
E extends ObjectPlain = ObjectPlain,
P extends Path<E> = Path<E>
> = DeepPick<E, P>
E extends ObjectPlain = ObjectUnknown,
F extends DTOFilter<E> = DTOFilter<E>
> = DeepPartialByRequired<E, F>
16 changes: 11 additions & 5 deletions src/dtos/entity.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ObjectPlain, Path } from '@flex-development/tutils'
import type { DTOFilter } from '@/types'
import type { ObjectPlain, ObjectUnknown } from '@flex-development/tutils'
import type { CreateEntityDTO } from './create-entity.dto'
import type { PatchEntityDTO } from './patch-entity.dto'

Expand All @@ -11,9 +12,14 @@ import type { PatchEntityDTO } from './patch-entity.dto'
* Data used to create or patch an entity.
*
* @template E - Entity
* @template P - Object field paths of `CreateEntityDTO` | `PatchEntityDTO`
* @template F - DTO filter object
*
* @example
* interface IUser{ ... }
* type Filter = DTOFilter<IUser, 'first_name', { created_at: never }>
* type SaveUserDTO = SaveEntityDTO<IUser, Filter>
*/
export type EntityDTO<
E extends ObjectPlain = ObjectPlain,
P extends Path<E> = Path<E>
> = CreateEntityDTO<E, P> | PatchEntityDTO<E, P>
E extends ObjectPlain = ObjectUnknown,
F extends DTOFilter<E> = DTOFilter<E>
> = CreateEntityDTO<E, F> | PatchEntityDTO<E, F>
21 changes: 16 additions & 5 deletions src/dtos/patch-entity.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { DeepPick, ObjectPlain, Path } from '@flex-development/tutils'
import type { DTOFilter } from '@/types'
import type {
DeepPartial,
ObjectPlain,
ObjectUnknown
} from '@flex-development/tutils'
import type { CreateEntityDTO } from './create-entity.dto'

/**
* @file Data Transfer Objects - PatchEntityDTO
Expand All @@ -9,9 +15,14 @@ import type { DeepPick, ObjectPlain, Path } from '@flex-development/tutils'
* Data used to patch an entity.
*
* @template E - Entity
* @template P - Object field paths of dto
* @template F - DTO filter object
*
* @example
* interface IUser{ ... }
* type Filter = DTOFilter<IUser, 'first_name', { created_at: never }>
* type PatchUserDTO = PatchEntityDTO<IUser, Filter>
*/
export type PatchEntityDTO<
E extends ObjectPlain = ObjectPlain,
P extends Path<E> = Path<E>
> = Partial<DeepPick<E, P>>
E extends ObjectPlain = ObjectUnknown,
F extends DTOFilter<E> = DTOFilter<E>
> = DeepPartial<CreateEntityDTO<E, F>>
13 changes: 4 additions & 9 deletions src/interfaces/abstract-mango-repo.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import type {
ObjectPlain,
ObjectUnknown,
OneOrMany,
OrPromise,
Path
OrPromise
} from '@flex-development/tutils'
import type { IAbstractMangoFinder } from './abstract-mango-finder.interface'
import type { MangoCacheRepo } from './mango-cache-repo.interface'
Expand Down Expand Up @@ -41,13 +40,9 @@ export interface IAbstractMangoRepository<
readonly validator: IMangoValidator<E>

clear(): OrPromise<boolean>
create<F extends Path<E>>(dto: CreateEntityDTO<E, F>): OrPromise<E>
create(dto: CreateEntityDTO<E>): OrPromise<E>
delete(uid?: OneOrMany<UID>, should_exist?: boolean): OrPromise<UID[]>
patch<F extends Path<E>>(
uid: UID,
dto?: PatchEntityDTO<E, F>,
rfields?: string[]
): OrPromise<E>
patch(uid: UID, dto?: PatchEntityDTO<E>, rfields?: string[]): OrPromise<E>
setCache(collection?: E[]): OrPromise<MangoCacheRepo<E>>
save<F extends Path<E>>(dto?: OneOrMany<EntityDTO<E, F>>): OrPromise<E[]>
save(dto?: OneOrMany<EntityDTO<E>>): OrPromise<E[]>
}
13 changes: 4 additions & 9 deletions src/interfaces/mango-repo-async.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import type { DUID, MangoParsedUrlQuery, MangoSearchParams, UID } from '@/types'
import type {
ObjectPlain,
ObjectUnknown,
OneOrMany,
Path
OneOrMany
} from '@flex-development/tutils'
import type { MangoCacheRepo } from './mango-cache-repo.interface'
import type { IMangoFinderAsync } from './mango-finder-async.interface'
Expand Down Expand Up @@ -33,13 +32,9 @@ export interface IMangoRepositoryAsync<
Q extends MangoParsedUrlQuery<E> = MangoParsedUrlQuery<E>
> extends IMangoFinderAsync<E, U, P, Q> {
clear(): Promise<boolean>
create<F extends Path<E>>(dto: CreateEntityDTO<E, F>): Promise<E>
create(dto: CreateEntityDTO<E>): Promise<E>
delete(uid?: OneOrMany<UID>, should_exist?: boolean): Promise<UID[]>
patch<F extends Path<E>>(
uid: UID,
dto?: PatchEntityDTO<E, F>,
rfields?: string[]
): Promise<E>
patch(uid: UID, dto?: PatchEntityDTO<E>, rfields?: string[]): Promise<E>
setCache(collection?: E[]): Promise<MangoCacheRepo<E>>
save<F extends Path<E>>(dto?: OneOrMany<EntityDTO<E, F>>): Promise<E[]>
save(dto?: OneOrMany<EntityDTO<E>>): Promise<E[]>
}
Loading

0 comments on commit ec86961

Please sign in to comment.