Skip to content
This repository has been archived by the owner on Feb 16, 2021. It is now read-only.
gcanti edited this page Nov 8, 2014 · 2 revisions

Asserts, transformations, validations and messages

Use cases:

  • Trusted data
    • Consuming a JSON API payload on a client
    • Handle form values
  • Untrusted data
    • Receiving a JSON Api payload on a server

Consuming a JSON API

Flow:

  1. receive the payload
  2. get an instance of the payload model (asserts)
  3. transform data (optional)
  4. get a domain model instance (optional)
// JSON received from the JSON API
var json = {
  name: 'giulio',
  surname: 'canti',
  age: 123462000000, // unix time
  adresses: [
    {country: 'IT', street: 'street1'},
    {country: 'US', street: 'street2'}
  ]
};

//
// JSON API payload model
//

var AddressJSON = struct({
  country: Country,
  street: Str
});

var PersonJSON = struct({
  name: Str,
  surname: Str,
  age: Num,
  adresses: list(AddressJSON)
});

//
// domain model
//

var Address = struct({
  country: Str,
  street: Str
});

var Person = struct({
  fullName: Str,
  age: Dat,
  adresses: list(Address)
});

//
// transformation
//

function toCountry(isoCode) {
  return isoCode === 'IT' ? 'Italy' : 'United States';
}

// transforms the payload to a model
PersonJSON.toPerson = function (json) {
  return new Person({
    fullName: json.name + ' ' + json.surname,
    age: new Date(json.age),
    adresses: json.adresses.map(function (address) {
      return {
        country: toCountry(address.country),
        street: address.street
      };
    })
  });
};

Transformations

Can I generalize the toPerson transformation? That is, can I manage the general transformation:

transform: Data -> Data

In general it's too hard. Let's consider only coordinates-wise bijective transformations:

// x: A -> parse -> y: E
// y: E -> format -> x: A (optional)
// y = parse(x)
// x = format(y)

var Transformer = struct({
  from: Type,
  to: Type,
  parse: Func, // from -> to
  format: Func // to -> from
});

var Address = struct({
  country: Str,
  street: Str
});

var Person = struct({
  name: Str, // name and surname now are distinct
  surname: Str,
  age: Dat,
  adresses: list(Address)
});

// transform: (Any, list(Transformer)) -> Any
PersonJSON.toPerson = function (json) {
  var value = transform(json, [
    {from: Str, to: Dat: parse: ..., format: ...},
    {from: AddressJSON, to: Address: parse: ..., format: ...}
  ]);
  return new Person(value);
};

Validation

Flow:

  1. validate data
  2. get a domain model instance..
  3. ..or handle error messages (optional)
var result = validate(data, type);
if (reault.isValid()) {
  // use result.value
} else {
  // use result.errors
}

How to handle error messages?

// a validation error
var error = {
  actual: Any,
  expected: Type,
  path: list(Str)
};

Solution: implement a getMessage: ValidationError -> Str function.

Handle form values

Flow:

  1. transform state (format, optional)
  2. collect form data
  3. transform data (parse, optional)
  4. validate data

Untrusted data

// payload received on a server
var payload = {...};
var result = validate(payload, type);
if (result.isValid()) {
  // use result.value
} else {
  res.error(result.errors);
}