Validating Typescript Business Entities with Firefly Semantics Validator | Task

Ole Ersoy
Nov - 12  -  2 min

Scenario

We have PurchaseOrderDTO instances coming in over the wire as described here.

We wish to construct PurchaseOrder instances using the PurchaseOrderDTO instances and then validate the following:

  • The quantity is an integer Both the purchaseDate and the receiptDate are Date instances. The purchaseDate comes before the receiptDate The receiptDate will only be validated if the purchaseDate is valid. This functionality is provided by the @IfValid decorator and prevents validation noise.

Approach

After the PurchaseOrderDTO has been validated we drop it in to a PurchaseOrder constructor. The PurchaseOrder business entity class is defined below:

import {
  IfValid,
  IsAfterInstant,
  IsNumber,
  IsDate,
  IsDefined,
  IsString,
  IsInt,
} from '@fireflysemantics/validator';
import { PurchaseOrderDTO } from './PurchaseOrderDTO';

export class PurchaseOrder {
  @IsString()
  @IsDefined()
  id: string;
  @IsString()
  @IsDefined()
  sku: string;

  @IsDate()
  @IsDefined()
  purchaseDate: Date;

  @IsAfterInstant('purchaseDate')
  @IsDate()
  @IsDefined()
  @IfValid('purchaseDate')
  receiptDate: Date;

  @IsInt()
  @IsDefined()
  quantity: number;
  constructor(dto: PurchaseOrderDTO) {
    //===================================
    // Initialize the sku and id properties
    // with Object.assign
    //===================================
    Object.assign(this, dto);
    this.purchaseDate = new Date(dto.purchaseDate);
    this.receiptDate = new Date(dto.receiptDate);
    this.quantity = parseInt(dto.quantity);
  }
}

We use Object.assign to set the sku and id properties as these are string typed and can be mapped directly.

We create typed instances of the variables:

  • quantity
  • receiptDate
  • purchaseDates

After PurchaseOrder instance construction we can validate the instance like this:

const poDTO1: PurchaseOrderDTO = new PurchaseOrderDTO({});
const poInvalid: PurchaseOrder = new PurchaseOrder(poDTO1);
const poInValid: ObjectErrors = validate(poInvalid);
const errors: ValidationError[] = poInValid.errors;
assert.equal(poInValid.errors.length, 5, 'There should be 5 errors');

In this case we can see that 5 errors are generated. One for each property.

Demo