fieldify
v2.1.6
Published
Fieldify object manipulation & validator
Downloads
16
Maintainers
Readme
Fieldify
Fieldify is a multi-purpose schema manipulation library, data extractor, generic object and schema iterator. It allows to read, transform or verify a data schema. It is very useful for handling complex objects and schemas. Especially when designing CRUD or API input validator. But also define protocols or file format, the field of Fieldify is very wide.
Warning: On the current Git repository, you are on the "master" branch, which corresponds to the future version 2 of Fieldify. If you want to access the production code, please go to the "V1" branch.
Documentations
Installation
Using NPM :
npm install fieldify
Using Yarn :
yarn add fieldify
Including Fieldify
Using require()
const fieldify = require("fieldify")
Using import
import fieldify from "fieldify"
Portability
The package is independant from other in order to have a clean base.
Fieldify is known to be ran well on different Javascript plateforms such as :
- NodeJS
- In Browser (Chrome, Firefox, IE, Opera, ...)
- Electron
- ReactJS
- React Native
Fieldify Schema
Fieldify embeds a schema and types mechanism allowing to manipulate input and output data based on a schema.
Schema Declaration
It is super simple to create a single schema using Fieldfy, the design is inspired from MongoDB but Fieldify works differently.
First you need to know that the $ sign before keys names are used to define special operation on the schema.
Let's say we just need to validate an email and a token, and after update the token can not be readed from the database.
const fieldify = require("fieldify")
const { types } = fieldify;
// here is the schema
const schema = {
email: {
$type: "Email",
$write: true,
$read: true,
},
token: {
$type: "String",
$write: true,
$read: false,
},
}
// here we create the schema context
const hdl = new fieldify.schema("test")
// here we compile the final schema
// you can update whenever you want using hdl.compile()
// or hdl.fusion() and hdl.compile()
hdl.compile(schema)
// define a user input
const input = {
email: "[email protected]",
token: "supertoken"
}
// run the verifier against the input
hdl.verify(input, (fieldified) => {
if(fieldified.error === false) {
console.log("Error in the input")
}
})
// verify promise version call
const fieldified = await hdl.verify(input)
if(fieldified.error === false) {
console.log("Error in the input")
}
// after verification you can store in
// database after encoding
hdl.encode(input, (fieldified) => {
console.log(fieldified.result)
})
// and getting data from the database
hdl.decode(input, (fieldified) => {
console.log(fieldified.result)
})
// and finally filter what is going out from the db
hdl.filter(input, (fieldified) => {
console.log(fieldified.result)
})
// filter promise version
const fieldified = await hdl.filter(input)
console.log(fieldified.result)
Roles based Schema Declaration
Schema based on roles allows to define roles for some fields of the schema. This allows to limit the reading or writing of fields according to a role. Typically an administrator will be able to field a field when a user will not be able to do so.
Fieldify provides a role management method associated to the field directly. Depending on these roles, Fieldify will create different schemes per existing role. This makes data verification or filtering much easier.
Moreover, Fieldify allows to rewrite all 'leaf' fields starting with $. It is possible to change the values for $read or $write but also $encode or $decode. Here is how to declare a schema based on roles:
const fieldify = require("fieldify")
const { roles } = fieldify
const theSchema = {
test: {
$type: 'String',
// this is default role schema where the field can not write or read.
$write: false,
$read: false,
$roles: {
admin: {
// admin can read & write
$read: true,
$write: true
},
user: {
// user can just read, not write
$read: true
}
}
}
}
const hdl = new roles('test', theSchema)
// here we have 3 instances of schema
// verify on default role
const fieldified = await hdl.default.verify(input)
if(fieldified.error === false) {
console.log("Error in the input")
}
// verify on admin role
const fieldified = await hdl.admin.verify(input)
if(fieldified.error === false) {
console.log("Error in the input")
}
// verify on user role
const fieldified = await hdl.user.verify(input)
if(fieldified.error === false) {
console.log("Error in the input")
}
F2020.1 Official Types
| Type | Description | Class |-|-|- | String | Single string | Low | Number | Number Type | Low | Moment | Manage Date Picker | Low | Select | Item selector | Low | Name | Name type | Low | Checkbox | Single checkbox | Low | DatePickerRange | Date picking with range | Low | Color | HTML Color selector | Low | FieldName | Internal Field Name | Low | Hash | Hash | Low | KV | Single KV | Low | URL | Valid URL | Low | Email | E-mail address | Low | Radio | Radio button | Low | Country | List of Countries | Low | Hash | HASH type field | Low | Slug | Slug string | Low | TimePickerRange | Time picking with range | Low
Upcomming Official Types
| Type | Description | Class |-|-|- | Switch | Switch button | Low | Rate | Rate selector | Low | Address | IRL Address | Low | LocationGPS | GPS Position | Low
Schema Types
Each type has it own space in a schema, few methods are exposed to perform different operations on the field. Every type are derived from fieldifyType.
Actually Fieldify Types provides different access method :
- verify(input, cb): Verify / Validate / Sanatize user input
- filter(input, cb): Filtering Data Output generally from a database (prevent leak)
- encode(input, cb): Before to write into the database - this is how to write
- decode(input, cb): After data getting out from the database - this is how to read
Each type in a schema as it owns configuration. Fieldify supports 4 mains options on the field:
- $required: Is the field is required ? true = yes, default true
- $read: Is it allowed to read the field using filter(), true = yes, default false
- $write: Is it allowed to write the field using verify(), true = yes, default false
- $type: The field type declaration
Types
Internal Design
There are a few basic points in Fieldify. In particular the management of arrays and the use of $ in front of certain fields.
Fieldify is a recursive object iterator which allows :
- Read or transform a schema - assignator
- Extract and verify the input data following a schema - iterator
It is essential to understand the use of $. When a field in a schema is preceded by $ this means that the iterator will not enter processing in this field, this allows options to be given to the parent field and this recursively.
So you can give any options you want to define the properties of a field.
const schema = {
name: {
first: {
$options: "string",
$max: 30,
$onCheck: (data, next) => {}
},
last: {
$options: "string"
}
}
};
Nested Object and Array
The last important point in Fieldify is the notion of Array and Nested object. The great ability of Fieldify is to support Nested Objects and Array. In a Fieldify schema the definition of an Array makes it possible to define the type of the field. One cannot thus define several element in an array of schema however to define one of them will allow Fieldify to authorize elements in time as source/input in the iterator.
In a schema the assign _ or the _fusion* * will only take the first element of an Array to compose the output.
It's a bit complex like that but very useful every day:
// a Fieldify schema
const schema = {
name: {
// define an array field
first: [
{
fieldOne: {
$opt: true
},
fieldTwo: {
$opt: true
}
}
]
}
};
// an input
const input = {
name: {
first: [
{
fieldOne: 32,
fieldTwo: 43
},
{
fieldOne: 1,
fieldTwo: 4
}
]
}
};
Schema Assignation
The assigner allows you to extract fields (those that are not prefixed with $) from a schema in a desired format. This is particularly useful for transforming a Fieldify schema into another schema format.
Example: Transforming a Fieldify schema into a Mongo (mongoose) schema
Info: The assigner works in blocking mode. It is not recommended to let users control the schema without validation.
Below is an example of a Fieldify schema
const schema = {
entry: {
$read: false,
subEntry1: {
$read: true
},
subEntry2: {
$read: true,
subEntry22: {
$read: true
}
}
}
};
In the example below we transform an assigner into another format. Even if $read _ is _false* * we continue to follow the tree.
const extract = fieldify.assign(schema, (user, dst, object, source) => {
dst["_read"] = object.$read;
});
/ * Will return
{
"entry": {
"_read": false,
"subEntry1": {
"_read": true
},
"subEntry2": {
"_read": true,
"subEntry22": {
"_read": true
}
}
}
}
*/
Extract a schema and prohibit the iterator from going further in its floor if $read _ is false. This is _return(false)* * which indicates the iterator to return to a lower floor.
const extract = fieldify.assign(schema, (user, dst, object, source) => {
dst["_read"] = object.$read;
// do not follow the rest in any case
if (object.$read === false) return false;
});
/* Will return
{
"entry": {
"_read": false
}
}
*/
There is an example that shows how to merge 2 fields into one in examples/assignator-rw.js.
Input Iterator
Once the schema is defined and modeled it is necessary to compile it in order to optimize the traversing of the tree.
const fieldify = require("fieldify");
const crypto = require("crypto");
const schema = {
$write: false,
name: {
$read: false,
$write: true,
first: { $read: true },
last: { $read: true }
},
password: {
$write: true
}
};
const handler = fieldify.compile(schema);
The handler * corresponds to the compiled instance of the schema which will be used later for the iteration.
The iterator is a means of extracting data from an entry according to the defined Fieldify scheme. This is very useful for validating or verifying input data.
This is how things start to get interesting, in the example below we are going to check some input data and assignment.
Considering the following entry:
const input = {
name: {
first: "Michael",
last: "Vergoz"
},
password: "My super password"
};
We will create 2 assignators, one to extract the data that is readable and the other for the data that can be written. Thus the password field cannot be read.
function isReadable(current, next) {
if (current.access.$read === true) {
current.result[current.key] = current.input;
}
next();
}
function isWritable(current, next) {
if (current.access.$write === true) {
current.result[current.key] = current.input;
}
next();
}
It is important to note that the extraction functions are asynchronous and so the next() callback must be executed on each pass. It is thus possible to question a third party service for a field without blocking the iteration.
const opts = {
handler: handler,
input: input,
onAssign: isReadable,
onEnd: iterator => {
console.log(iterator.result);
}
};
fieldify.iterator(opts);
In the example above, we retrieve the input data according to the Fieldify schema compiled handler with the onAssign() assignment function which will extract only the fields inheriting from a $read flag to true. In this example, the password field will not be rendered when the iteration has finished and executed the onEnd() callback
This type of iteration is very useful when presenting data to the user (database > user)
const opts = {
handler: handler,
input: input,
onAssign: isWritable,
onEnd: iterator => {
console.log(iterator.result);
}
};
fieldify.iterator(opts);
In the example above the password field will be returned in the result. This case arises when we want to insert the data in a database (user > database)