0.2.0 • Published 3 years ago

@mangroves/jamcaa-helper v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

jamcaa-helper (饮茶小助手)

codecov Node.js CI

If you are using:

  • Nestjs
  • TypeORM
  • MySQL

Then you can drink a cup of tea instead of spending time on writing CRUD code.

Installation

yarn add @mangroves/jamcaa-helper

Usage

Instantiation

import { JamcaaHelper } from '@mangroves/jamcaa-helper'
import { InjectRepository } from '@nestjs/typeorm'
import { Entity } from './entity.ts'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  constructor (
    // You may need to inject repository if you get a `RepositoryNotFoundError`
    @InjectRepository(Entity)
    private readonly repository: Repository<Entity>,
  ) {}
}

Create

Feature

What does this method do?

  1. Check if record already exists according to unique keys
  2. Reuse soft deleted record according to options
  3. Increase data_version
  4. Add creator and updater to entity
  5. Add create_time and update_time
  6. Save to database and return saved entity

Exceptions

  • Throw 400 if entity already exists

Example

import { JamcaaHelper } from '@mangroves/jamcaa-helper'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  async create (dto: DTO, operator: string) {
    const partialEntity = getEntityFromDTO(dto)
    const savedEntity = await this.crudHelper.createInsertQuery(partialEntity, operator)
    return getDTOFromEntity(savedEntity)
  }
}

List

Feature

  1. Create and return a ListQuery instance (an internal class)
  2. You don't need to care about null/undefined/empty/array filter value in DTO
  3. Dealing with soft delete according to options

Exceptions

None

Example

import { JamcaaHelper } from '@mangroves/jamcaa-helper'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  async list (dto: DTO) {
    const [entities, totalSize] = await this.crudHelper.createListQuery()
      .filter((filterQuery) => {
        filterQuery
          .equals('column1', dto.column1)
          .equals('column2', dto.column2)
      })
      .showDeletedQuery()
      .paginationQuery(dto.page_number, dto.page_size)
      .getQueryBuilder()
      .getManyAndCount()
    return {
      entities,
      total_size: totalSize
    }
  }
}

Get

Feature

  1. Dealing with soft delete according to options

Exceptions

  • Throw 404 if entity not found

Example

import { JamcaaHelper } from '@mangroves/jamcaa-helper'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  async get (id: string) {
    const uniqueKeyConditions = { id }
    const entity = await this.crudHelper.createGetQuery(uniqueKeyConditions)
    return getDTOFromEntity(entity)
  }
}

Update

Feature

  1. Update record using FieldMask
  2. Update data_version
  3. Update updater
  4. Update update_time

Exceptions

  • Throw 404 if entity not found
  • Throw 400 if update_mask contains disallowed fields
  • Throw 400 if nothing updated

Example

Note: If you want to validate data_version, you must transform it in transformToEntity.

import { JamcaaHelper } from '@mangroves/jamcaa-helper'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  async update (id: string, dto: DTO, operator: string) {
    const uniqueKeyConditions = { id }
    const dto = { first_name: 'Charlie', person_info: { age: 12 }, data_version: '1' }
    const updateMask = ['first_name', 'person_info.age']
    const allowedMask = ['first_name', 'person_info']
    const transformFromEntity = (entity) => ({
      first_name: entity.firstName,
      person_info: entity.personInfo,
    })
    const transformToEntity = (dto) => ({
      firstName: dto.first_name,
      personInfo: dto.person_info,
      // Transform data_version if you want to validate it.
      dataVersion: dto.data_version,
    })
    const updatedEntity = await this.crudHelper.createUpdateQuery(
      uniqueKeyConditions,
      dto,
      updateMask,
      allowedMask,
      operator,
      transformFromEntity,
      transformToEntity,
    )
    return getDTOFromEntity(updatedEntity)
  }
}

Delete

Feature

  1. Dealing with soft delete according to options
  2. Add updater if soft delete feature enabled

Exceptions

  • Throw 404 if entity not found

Example

import { JamcaaHelper } from '@mangroves/jamcaa-helper'

@Injectable()
export class SomeService {
  private readonly crudHelper = new JamcaaHelper(SomeEntity, 'entityUniqueKey' /** , options, connectionName */)

  async delete (id: string, operator: string) {
    const uniqueKeyConditions = { id }
    await this.crudHelper.createDeleteQuery(uniqueKeyConditions, operator)
  }
}

Constructor Options

OptionTypeDefaultDescription
maxUnspecifiedPageSizenumber100Max page size if it is not specified
softDeletebooleantrueWhether soft delete feature should be applied
softDeleteFieldstring'deleteStatus'Soft delete column field
softDeleteEnumundeleted: any, deleted: any0, 1The values to mark if record is deleted
reuseSoftDeletedDatabooleantrueWhether to reuse soft deleted data
dataVersionbooleantrueWhether to increase data version column on update
dataVersionFieldstring'dataVersion'Data version column field
dataVersionType'string' or 'number''string'Data version type
validateDataVersionbooleantrueWhether to validate data_version or not
hasOperatorbooleantrueWhether there are creator and updater columns
creatorFieldstring'creator'Creator column field
updaterFieldstring'updater'updater column field
hasTimebooleantrueWhether there are create_time and update_time columns
createTimeFieldstring'createTime'Create time column field
updateTimeFieldstring'updateTime'Update time column field
timePrecision'ms' or 's''ms'Store time column with the precision to millisecond or second
onEntityAlreadyExistsError(entityName: string) => neverThrow 400 exceptionTo throw an exception when entity already exists
onEntityNotFoundError(entityName: string) => neverThrow 404 exceptionTo throw an exception when entity not found
onDisallowedUpdateMaskError(disallowedMask: string[]) => neverThrow 400 exceptionTo throw an exception when update_mask contains disallowed fields
onDataVersionError() => neverThrow 400 exceptionTo throw an exception when data_version is not equal to the existing entity's
onNothingUpdatedError() => neverThrow 400 exceptionTo throw an exception when nothing updated

Test

unit

yarn test:unit

e2e

Real MySQL environment required.

We run e2e test with real database running on Docker.

  1. Install Docker
  2. Execute the following command to start a MySQL container
yarn db

Then execute

yarn test:e2e

After test run, use the following command to stop and remove MySQL container (Optional)

yarn db:destroy

Caveat

Make sure you are using the same TypeORM library in node_modules, or else JamcaaHelper will not get the correct ORM connection.