Skip to content

Instantly share code, notes, and snippets.

@ablankenship10
Last active April 11, 2020 05:19
Show Gist options
  • Save ablankenship10/152e33038fb29a0d452849e6a08758dd to your computer and use it in GitHub Desktop.
Save ablankenship10/152e33038fb29a0d452849e6a08758dd to your computer and use it in GitHub Desktop.
Serialization class for Dynamoose. Configure properties on Model and Document as shown in TODO comments
// TODO -- Model.serializer = new Serializer();
class Serializer {
_serializers = {};
_defaultSerializer = null;
add(name, options) {
validateName(name);
validateOptions(options);
this._serializers[name] = options;
if (!this._defaultSerializer) {
this.setDefault(name);
}
}
setDefault(name) {
validateName(name);
this._defaultSerializer = name;
}
remove(name) {
validateName(name);
if (this._serializers[name]) {
delete this._serializers[name];
}
if (this._defaultSerializer === name) {
this._defaultSerializer = null;
}
}
// TODO -- Model.serializeMany = Model.serializer._serializeMany;
_serializeMany = (documentsArray = [], nameOrOptions) => {
documentsArray = cleanAndValidateDocumentsArray(documentsArray);
return documentsArray.map(doc => doc.serialize(nameOrOptions));
};
// TODO -- Document.serialize = nameOrOptions => this.Model.serializer._serialize(this._internalProperties, nameOrOptions);
_serialize = (document, nameOrOptions = this._defaultSerializer) => {
const inputType = typeof nameOrOptions;
let isArray = Array.isArray(nameOrOptions);
let options;
if (inputType === 'string') {
options = this._serializers[nameOrOptions];
} else if (isArray || inputType === 'object') {
options = nameOrOptions;
}
try {
validateOptions(options);
isArray = Array.isArray(options);
if (isArray) { return includeHandler(document, options); }
let serialized = {};
if(options.include){
serialized = includeHandler(document, options.include, serialized);
}
if(options.exclude){
if(!options.include){
serialized = {...document};
}
serialized = excludeHandler(document, options.exclude, serialized);
}
if(options.modify && typeof options.modify === 'function'){
serialized = options.modify(serialized, document);
}
return serialized;
} catch (error) {
// Failing quietly and defaulting to dumping the whole object may not be safest idea, lest we expose sensitive data.
return { ...document };
}
};
}
const includeHandler = (document, includeRules, serialized = {}) =>
includeRules.reduce((_serialized, key) => {
if (document.hasOwnProperty(key)) {
_serialized[key] = document[key];
}
return _serialized;
}, serialized);
const excludeHandler = (document, excludeRules, serialized = {}) =>
excludeRules.reduce((_serialized, key) => {
if (_serialized.hasOwnProperty(key)) {
delete _serialized[key];
}
return _serialized;
}, serialized);
const validateName = name => {
if (!name || typeof name !== 'string') {
throw new Error('Field name is required and should be of type string');
}
};
const validateOptions = options => {
if (!options || !(Array.isArray(options) || typeof options === 'object')) {
throw new Error('Field options is required and should be an object or array');
}
};
const cleanAndValidateDocumentsArray = documentsArray => {
if (!documentsArray || !Array.isArray(documentsArray)) {
throw new Error('documentsArray must be an array of document objects');
}
return documentsArray.filter(doc => typeof doc.serialize === 'function');
};
module.exports = Serializer;
/*
*
* Example Cases
*
* */
// Dummy Model class
class Model {
serializer = new Serializer();
serializeMany = this.serializer._serializeMany;
}
const MyModel = new Model();
// Dummy Document class
class Document {
Model = MyModel;
_internalProperties = {};
serialize = nameOrOptions => this.Model.serializer._serialize(this._internalProperties, nameOrOptions);
constructor(properties) {
Object.assign(this._internalProperties, {...properties});
}
}
MyModel.serializer.add('contactOnly', ['email', 'phone']);
MyModel.serializer.add('excludeSecure', { exclude: ['password'] });
MyModel.serializer.add('isActive', {
exclude: ['status', 'password'],
modify: (serialized, original) => {
serialized.isActive = original.status === 'active';
return serialized;
}
});
const docOne = new Document({
id: 1,
email: 'one@example.com',
phone: '1234567890',
password: 'superSecret',
status: 'active'
});
const docTwo = new Document({
id: 2,
email: 'two@example.com',
phone: '9876543210',
password: 'moreSecret',
status: 'notActive'
});
// Test array only (include)
console.log(docOne.serialize('contactOnly'));
// Test excluding only
console.log(docTwo.serialize('excludeSecure'));
// Test serializeMany with exclude and modify
console.log(MyModel.serializeMany([docOne, docTwo], 'isActive'));
// Test default serializer (should be contactOnly, the first created)
console.log(docOne.serialize());
// Change the default serializer
MyModel.serializer.setDefault('isActive');
// Test the new default serializer
console.log(docOne.serialize());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment