A object mapper for use in my personal projects. With this project you can create a new object using a source object and a model specification.
const model = {
id: { type: 'direct' },
name: { type: 'direct', from: 'firstName' },
amt: { type: 'number', from: 'amount' }
}
const sourceObject = {
id: 7,
firstName: 'John',
amount: 123.75
}
const destObject = await map(sourceObject, model);
//or
map(sourceObject, model).then(result => {
const destObject = result;
}).catch(err => {
throw err
});
destObject
will be an object like this:
{ id: 7, name: 'John', amt: 123.75 }
A model is defined with one key for each map you need to have in the final destination object.
For each key you have to define a type
key with one of the following values: "direct"
, "number"
, "date"
or "custom"
.
Use this type to extract a value directly from the a key of the sourceObject. If you specify a from
key, the value will be retrieved from the key you specify as a value for the from
key. Otherwise the value the value will be retrieved from the same name of the key you are defining.
see the keys name
and id
from the sample above.
The value will be converted to string using the
.toString
javascript method.
Use this type to extract a value and convert it to a number. You can specify a from
key if you want to use a source key different to the key being defined.
This type of map uses javascript's parseFloat
function to convert the value. It will throw a ModelMapperFieldError
in case of a failed conversion to a number.
Use this type to extract a value and convert it to a Date. You can specify a from
key if you want to use a source key different from the key being defined.
This type of map uses javascript's new Date()
constructor to convert the value. It will throw a ModelMapperFieldError
in case of a failed conversion to a Date.
Use this type to extract a value using a custom function or object provided in the required map
key. If you provide an object it will be returned as is.
In case you provide a function to the map
key, the mapper will call with it with three arguments:
sourceObject
: to pass to the function the sourceObject.key
: the key triggering the map function.mapModelSpec
: the full map specification for the key triggering the function. You can use custom keys in this definition if you are going to need these custom keys in your map function.
required
: used to ensure the value is not undefined or null or empty string. Applies to all types.min
: used to restrict the value to this minimum value. Applies tonumber
anddate
.max
: used to restrict the value to this maximum value. Applies tonumber
anddate
.minLength
: used to restrict the final value to a minimumLength. Applies todirect
andcustom
.maxLength
: used to restrict the final value to a maximumLength. Applies todirect
andcustom
.regExp
: used to check the final value towards a javascript'sRegExp.test
. Applies todirect
andcustom
.
All of the keys that apply to
type = 'custom'
will be checked after the evaluation of themap
function or object.
const model = {
id: { type: 'direct' },
name: { type: 'direct', from: 'firstName' },
//amt is restricted to a minimum value of 100
amt: { type: 'number', from: 'amount', min: 100 },
//birth date is restricted to a minimum value of January 1st, 1900
birthDate: { type: 'date', min: new Date('1900/01/01') }
}
const sourceObject = {
id: 7,
firstName: 'John',
amount: 123.75,
birthDate: '1800/03/01'
}
//Will throw because the provided BirthDate is older than January 1st, 1900
try {
const destObject = await map(sourceObject, model);
} catch (err) {
const errors = Object.keys(err.results)
.filter(key => err.results[key].error)
.map(errorKey => err.results[errorKey]);
//errors array will contain all keys with their error message.
}
This library provides Middlewares for mapping from a request and to a response.
const { mapBodyMiddleware } = request('@josesjs/model-mapper');
const model = {
id: { type: 'direct' },
name: { type: 'direct', from: 'firstName' },
//amt is restricted to a minimum value of 100
amt: { type: 'number', from: 'amount', min: 100 },
//birth date is restricted to a minimum value of January 1st, 1900
birthDate: { type: 'date', min: new Date('1900/01/01') }
}
expressApp.use('/endpoint', mapBodyMiddleware(model), (req, res) => {
res.status(200).json(req.mappedBody);
});
When you provide this middleware in the endpoint, the req.body object will be used as a sourceObject
to be mapped using the model
provided. If no errors, the mapped model will be available in the req.mappedBody
variable for the following middlewares.
if there is no subsequents middlewares provided, mapBodyMiddleware will throw an error with the message
'mapBodyMiddleware expect a next function to be defined.'
.
const { mapResponseMiddleware } = require('@josesjs/model-mapper');
//expected a model to map the mappedBody
expressApp.use('/endpoint', mapBodyMiddleware(...), mapResponseMiddleware(
model
));
When you provide this middleware to an endpoint. the req.mappedBody
variable will be mapped with the model
variable and returned to the response as a JSON with an status code = 200;
If you want to override the status code you can pass your customized number to the second parameter of this function. Also if you want to override the req.mappedBody
with another variable you can provide a key as a third parameter. The function will look for this key first in the response, if not found there, in the request, if not found there will use the req.mappedBody
.
You can use this middleware to catch the errors thrown by the map function. It has a unique optional parameter to use if you want to override the default mapping for errors.
I'm providing some functions to help setup routes in express.js
setupControllerServiceMiddleware(inModel, outModel, serviceCallback, defaultStatusCode = 200)
Use this function to generate an express Middleware that maps the request.body using the inModel
argument, following by a call to the serviceCallback
method. This method will be called with the arguments (model, req, res). The return value of the serviceCallback will be mapped using the outModel
argument and returned to the http client with an status code provided in the optional defaultStatusCode
parameter.
In case of errors, this middleware will call the next(err) express function.
setupControllerMiddleware(inModel, outModel, middleware, defaultStatusCode = 200)
Use this function to generate an express Middleware that maps the request.body using the inModel
argument. After the req.body is mapped, the middleware provided in the middleware
argument will be called.
This middleware will have access to the mapped object in the req.mappedBody variable. You can do any logic you need to made in order to process the request.
After the request process, you can end the request inside of the middleware
or set the variable res.mappedBody to the result you want to map using the outModel
model. For this you have to call next()
inside of your customized middleware.
If you call next with an
Error
instance as argument, the first express.js error handler middleware will be called skipping any following non-error handlers middlewares.