felicity
Version:
Javascript object instantiation from Joi schema
326 lines (247 loc) • 13.2 kB
Markdown
# 5.0.0 API Reference
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Felicity](#felicity)
- [`entityFor(schema, [options])`](#entityforschema-options)
- [Constructor methods](#constructor-methods)
- [Instance methods](#instance-methods)
- [`example(schema, [options])`](#exampleschema-options)
- [Options](#options)
- [`entityFor` Options](#entityfor-options)
- [`example` Options](#example-options)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Felicity
### `entityFor(schema, [options])`
Creates a Constructor function based on the provided Joi schema. Accepts an optional [`[options]`](#entityfor-options) parameter.
*Please note that JavaScript constructor functions only return objects. Therefore, the Joi schema provided must describe an object.*
Instances created by `new Constructor()` are "empty skeletons" of the provided Joi schema, and have sugary prototypal [methods](#instance-methods).
The returned Constructor function has the signature `([input], [options])`. Any input provided will be used to hydrate the `new` object.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().min(3).required(),
id : Joi.string().guid().required(),
keys: Joi.array().items(Joi.object().keys({id: Joi.string().guid()})).min(3).required()
});
const Conversation = Felicity.entityFor(schema);
const convoInstance = new Conversation();
const partialInstance = new Conversation({name: 'Felicity', fakeKey: 'invalid'});
/*
convoInstance
{
name: null,
id : null,
keys: []
}
partialInstance
{
name : 'Felicity',
id : null,
keys : [],
fakeKey: 'invalid'
}
*/
```
```Javascript
const nonObjectSchema = Joi.number();
const NeverGonnaHappen = Felicity.entityFor(nonObjectSchema); // throws Error 'Joi schema must describe an object for constructor functions'
```
The returned Constructor is also registered within the runtime with the exact name of *Constructor*. This can be corrected using the ES6 class `extend` expression.
```Javascript
const schema = Joi.object().keys({
id : Joi.number().required(),
name: Joi.string().required()
});
const User = Felicity.entityFor(schema);
new User() // constructor will be of name Constructor
const User = class User extends Felicity.entityFor(schema);
new User() // constructor will be of name User
```
#### Constructor methods
The Constructor function returned by `entityFor` has the following properties/methods available for use without instantiation of `new` objects.
- `prototype.schema` - The Joi validation schema provided to `entityFor`.
- `example([options])` - Returns a valid pseudo-randomly generated example Javascript Object based on the Constructor's `prototype.schema`. Accepts an optional [`[options]`](#example-options) parameter.
```Javascript
// using schema and Conversation constructor from "entityFor" code example:
const exampleConversation = Conversation.example();
/*
exampleConversation
{
name: 'taut9',
id : 'b227cd4c-4e7a-4ba4-a613-30747f7267b8',
keys: [
{ id: '401a7324-8753-4ae2-abcc-6ae96216500e' },
{ id: 'c83c4a88-db1e-4402-8345-8b92be551b4e' },
{ id: 'e4194c87-541c-41f7-9473-783bf0e790fe' }
]
}
*/
```
- `validate(input, [callback])` - Joi-validates the provided input against the Constructor's `prototype.schema`.
Returns the below validationObject unless `callback` is provided, in which case `callback(errors, validationObject)` is called.
- `validationObject` - the result of Joi validation has properties:
- `success` - boolean. `true` if Joi validation is successful, `false` if input fails Joi validation.
- `errors` - null if successful validation, array of all Joi validation error details if unsuccessful validation
- `value` - Validated input value after any native Joi type conversion is applied (if applicable. see [Joi](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback) docs for more details)
```Javascript
// Examples
const successValidationObject = {
success: true,
errors : null,
value : {/*...*/}
}
const failureValidationObject = {
success: false,
errors : [
{
message: '"name" is required',
path: 'name',
type: 'any.required',
context: { key: 'name' }
}
],
value : {/*...*/}
}
```
#### Instance methods
The `new` instances of the Constructor function returned by `entityFor` have the following properties/methods available.
- `schema` - The Joi schema provided to `entityFor`. Non-enumerable, inherited from the Constructor.
- `example([options])` - Returns a new valid pseudo-randomly generated example Javascript Object based on the instance's `schema` property.
Accepts an optional [`[options]`](#example-options) parameter.
Does not modify the instance.
- `validate([callback])` - Joi-validates the instance against the instance's `schema` property.
Returns the same validationObject as the [`Constructor.validate`](#constructor-methods) method unless `callback` is provided, in which case `callback(errors, validationObject)` is called.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().required(),
id : Joi.string().guid().required()
);
const Constructor = Felicity.entityFor(schema);
const instance = new Constructor(); // instance === { name: null, id: null }
const instanceValidation = instance.validate(); // instanceValidation === { success: false, errors: [ { message: '"name" must be a string', path: 'name', type: 'string.base', context: [Object] },{ message: '"id" must be a string', path: 'id', type: 'string.base', context: [Object] }], value: {name: null, id: null} }
instance.name = 'Felicity';
instance.id = 'e7db5468-2551-4e42-98ea-47cc57606258';
const retryValidation = instance.validate(); // retryValidation === { success: true, errors: null, value: {name: 'Felicity', id: 'e7db5468-2551-4e42-98ea-47cc57606258'}}
```
### `example(schema, [options])`
Returns a valid pseudo-randomly generated example Javascript Object based on the provided Joi schema.
Accepts an optional [`[options]`](#example-options) parameter.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().min(3).required(),
id : Joi.string().guid().required(),
tags: Joi.array().items(Joi.string().max(4)).min(2).required(),
});
const exampleDoc = Felicity.example(schema);
/*
exampleDoc
{
name: 'qgrbddv',
id : '6928f0c0-68fa-4b6f-9bc5-961db17d42b0',
tags: [ 'k2a', '31' ]
}
*/
```
### Options
All options parameters must be an object with property `config`. Properties on the `config` object are detailed by method below.
#### `entityFor` Options
- `strictInput` - default `false`. Default behavior is to not run known properties through Joi validation upon object instantiation.
If set to `true`, all input will be validated, and only properties that pass validation will be utilized on the returned object.
All others will be returned in nulled/emptied form as if there was no input for that field.
**Note**: this will **not** throw Joi `ValidationError`s. See `validateInput` for error throwing.
```Javascript
const schema = Joi.object().keys({
id: Joi.string().guid()
});
const input = {
id: '12345678' // not a valid GUID
};
const Document = Felicity.entityFor(schema);
const document = new Document(input); // { id: '12345678' }
const StrictDocument = Felicity.entityFor(schema, { config: { strictInput: true } });
const strictDocument = new StrictDocument(input); // { id: null }
```
- `strictExample` - default `false`. Default behavior is to not run examples through Joi validation before returning.
If set to `true`, example will be validated prior to returning.
Note: in most cases, there is no difference. The only known cases where this may result in ValidationErrors are with regex patterns containing lookarounds.
```Javascript
const schema = Joi.object().keys({
name : Joi.string().regex(/abcd(?=efg)/)
});
const instance = new (Felicity.entityFor(schema)); // instance === { name: null }
const mockInstance = instance.example(); // mockInstance === { name: 'abcd' }
const strictInstance = new (Felicity.entityFor(schema, { config: { strictExample: true } })); // strictInstance === { name: null }
const mockStrict = strictInstance.example(); // ValidationError
```
- `ignoreDefaults` - Default `false`. Default behavior is to stamp instances with defaults.
If set to `true`, then default values of Joi properties with `.default('value')` set will not be stamped into instances.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().required().default('felicity')
});
const Constructor = Felicity.entityFor(schema);
const instance = new Constructor(); // instance === { name: 'felicity' }
const NoDefaults = Felicity.entityFor(schema, { config: { ignoreDefaults: true } });
const noDefaultInstance = new NoDefaults(); // noDefaultInstance === { name: null }
```
- `includeOptional` - Default `false`. Default behavior is to ignore optional properties entirely.
If set to `true`, then Joi properties with `.optional()` set will be included on instances.
```Javascript
const schema = Joi.object().keys({
name : Joi.string().required(),
nickname: Joi.string().optional()
});
const Constructor = Felicity.entityFor(schema);
const instance = new Constructor(); // instance === { name: null }
const WithOptional = Felicity.entityFor(schema, { config: { includeOptional: true } });
const withOptionalInstance = new WithOptional(); // withOptionalInstance === { name: null, nickname: null }
```
- `validateInput` - Default `false`. Default behavior is to not throw errors if input is not valid.
If set to `true`, then invalid input passed to the constructor function will result in a thrown `ValidationError`.
```Javascript
const schema = Joi.object().keys({
name : Joi.string()
});
const Constructor = Felicity.entityFor(schema);
const instance = new Constructor({ name: 12345 }); // instance === { name: 12345 }
const WithValidateInput = Felicity.entityFor(schema, { config: { validateInput: true } });
const withValidateInputInstance = new WithValidateInput({ name: 12345 }); // throws ValidationError: child "name" fails because ["name" must be a string]
```
#### `example` Options
- `strictExample` - default `false`. Default behavior is to not run examples through Joi validation before returning.
If set to `true`, example will be validated prior to returning.
Note: in most cases, there is no difference. The only known cases where this may result in no example coming back are with regex patterns containing lookarounds.
```Javascript
const schema = Joi.object().keys({
name : Joi.string().regex(/abcd(?=efg)/)
});
const instance = Felicity.example(schema); // instance === { name: 'abcd' }
const strictInstance = Felicity.example(schema, { config: { strictExample: true } }); // throws ValidationError
```
- `ignoreDefaults` - Default `false`. Default behavior is to stamp instances with default values.
If set to `true`, then default values of Joi properties with `.default('value')` set will not be stamped into instances but will be generated according to the Joi property rules.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().required().default('felicity')
});
const example = Felicity.example(schema); // example === { name: 'felicity' }
const noDefaultsExample = Felicity.example(schema, { config: { ignoreDefaults: true } }); // noDefaultsExample === { name: 'nq5yhu4ttq33di' }
```
- `ignoreValids` - Default `false`. Default behavior is to pick values from `.allow()`ed and `.valid()` sets.
If set to `true`, then the allowed/valid values will not be used but will be generated according to the Joi property rules.
```Javascript
const schema = Joi.object().keys({
name: Joi.string().allow(null).required()
});
const example = Felicity.example(schema); // example === { name: null }
const noValidsExample = Felicity.example(schema, { config: { ignoreValids: true } }); // noValidsExample === { name: 'nq5yhu4ttq33di' }
```
- `includeOptional` - Default `false`. Default behavior is to ignore optional properties entirely.
If set to `true`, then Joi properties with `.optional()` set will be included on examples.
```Javascript
const schema = Joi.object().keys({
name : Joi.string().required(),
nickname: Joi.string().optional()
});
const instance = Felicity.example(schema); // instance === { name: 'ml9mmn0r8m7snhfr' }
const withOptional = Felicity.example(schema, { config: { includeOptional: true } }); // withOptional === { name: '3cpffhgccgsw0zfr', nickname: '7pfjuxfa4gxk1emi' }
```