0.6.3 • Published 16 days ago

@burnsred/entity v0.6.3

Weekly downloads
1
License
MIT
Repository
github
Last release
16 days ago

Entity

The Entity package provides the fundamental Entity class and it supporting utilities.

An Entity is used to provide cleaned, validated data records.

Defining an Entity

import { Entity, CharField } from "@burnsred/entity";

type User {
    username: string;
    email: string;
    name?: string;
}

const UserEntity = new Entity<User>({
    idField: 'username',
    fields: {
        username: new CharField(),
        email: new CharField(),
        name: new CharField({ blank: true }),
    }
});

Once your entity is defined, you can use it to construct an User from API response data:

const rec = UserEntity.fromData(data);

Declaring Fields

Each field can specify its own list of validators and cleaners.

Cleaners are used to cast input values from the User to the right type.

Validators are used to check supplied values, and emit Errors.

fields: {
    uuid: new CharField({ cleaners=[trim] }),
    size: new IntegerField({ validators=[maxValue(10)] })
}

Common field arguments.

blank (boolean) false : Is this field allowed to be omitted?

cleaners (FieldCleaner[]) : A list of cleaners to apply when cleaning an record.

validators (FieldValidator[]) : A list of validators to apply when validating an record.

many (boolean) false : If true, the value for this field is an array of values of its type.

choices (any) [] : A list of valid choices. Can help with select lists.

defaultValue : Override the field's default value.

Any extra properties passed will be stored in the extra property.

Cleaners

Cleaner functions are primarily used for cleaning and casting data coming from the User.

Since most HTML Input fields work with string values, many Fields will default to using a cleaner which casts to the correct type.

Validators

Validator functions are used to check constraints, emitting FieldErrors to identify problems.

They can be specified either on the Field or Entitly.

Field validators are passed the value for that specific field, and are called with this being the Field.

Entity validators are passed the whole record, and are called with this being the Entity.

As an example, here is an Entity validator that ensures the start date is before the end date:

function dateRange(record) {
    const start = record.get("start_date");
    const end = record.get("end_date");

    if (start.valueOf() >= end.valueOf()) {
        return {
            code: "date-range",
            message: "Start date must be before End date.",
        };
    }
}

Errors

Managing and handling validation errors requires records to contain them.

An individual validation error consists of a code, user meangful message, and possibly some additional details.

For a scalar field, this can be managed as a list of such objects.

For vector values (i.e. many=True) a list of lists will suffice. Extracting errors for a given position is as simple as indexing.

For an EntityField, a mapping is used, instead. Each value, keyed by sub-field name, reflects the structure appropriate as described.

IntegerField.validate("1") // [{ code: 'type', message: 'Value must be a number' }]

EntityField.validate({ ... }) // { field: [...], ...}

From the top down, an Entity will get errors by looking into the Error map for the field name.

The values will be either a list of Errors, a list-of-lists of Errors, or another map.

A more complex example

Here's an example showing an Entity with a nested Entity as a field.

type Post = {
  id: number;
  user: User;
  posted: Date;
  content: string;
  public: boolean;
}


const PostEntity = new Entity<Post>({
  fields={
    id: IntegerField(),
    user: EntityField<Post>({ entity: UserEntity }),
    posted: DateField(),
    content: TextField(),
    public: BooleanField({ defaultVaule: false }),
  }
})

Now any record created by this Entity will contain a nested record in the user field.

const post = PostEntity.fromData(...);

...

const errors = UserEntity.validate(post.user);