@parch-js/rest-serializer
Version:
RestSerializer for parch to normalize request/response
448 lines (385 loc) • 14.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _inflect = require("inflect");
var _inflect2 = _interopRequireDefault(_inflect);
var _jsonSerializer = require("@parch-js/json-serializer");
var _jsonSerializer2 = _interopRequireDefault(_jsonSerializer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @class RestSerializer
* @extends <a href="https://github.com/parch-js/json-serializer" target="_blank">JSONSerializer</a>
* @constructor
*/
var RestSerializer = function (_JSONSerializer) {
_inherits(RestSerializer, _JSONSerializer);
function RestSerializer() {
_classCallCheck(this, RestSerializer);
return _possibleConstructorReturn(this, (RestSerializer.__proto__ || Object.getPrototypeOf(RestSerializer)).apply(this, arguments));
}
_createClass(RestSerializer, [{
key: "getRelationships",
/**
* Returns an array of ids for a give hasMany/belongsToMany relatioship
*
* @method getRelationships
* @param {Object} instance Sequelize model instance
* @param {Object} association Sequelize model instance
* @return {Array}
*
* @example
* ```javascript
* return orm.findOne("user", 1).then(user => {
* return serializer.getRelationships(user, user.Model.associations.Project);
* }).then(relationships => {
* /**
* * [1, 2, 3]
* *
* });
* ```
*/
value: function getRelationships(instance, association) {
var accessors = association.accessors;
var isManyRelationship = Object.keys(accessors).some(function (accessor) {
var hasManyRelationshipAccessor = ["add", "addMultiple", "count", "hasSingle", "hasAll", "removeMultiple"].some(function (valid) {
return valid === accessor;
});
return hasManyRelationshipAccessor;
});
if (isManyRelationship) {
return instance[accessors.get]().then(function (relationships) {
return relationships.map(function (relationship) {
return relationship.id;
});
});
} else {
return Promise.resolve();
}
}
/**
* Returns the name string for the record
*
* @method keyForRecord
* @param {Object} instance sequelize model instance
* @param {Boolean} singular singular or plural name
* @return {String} name string for record root
*
* @example
* ```javascript
* return orm.findOne("user", 1).then(user => {
* const res = {};
* const resKey = serializer.keyForRecord(user, true);
*
* res[resKey] = user.toJSON();
*
* return res;
* });
* ```
*/
}, {
key: "keyForRecord",
value: function keyForRecord(instance, singular) {
var tableName = instance.constructor.getTableName();
var recordKey = tableName.toLowerCase();
if (singular) {
return _inflect2.default.singularize(recordKey);
}
return _inflect2.default.pluralize(recordKey);
}
/**
* Return the object key for a relationship
*
* @method keyForRelationship
* @param {String} relationship the relationship name (e.g. `Projects`)
* @return {String} name string for the relationship
*
* @example
* ```javascript
* return serializer.keyForRelationship("Projects").then(key => {
* // "projects"
* });
* ```
*/
}, {
key: "keyForRelationship",
value: function keyForRelationship(relationship) {
return _inflect2.default.pluralize(relationship.toLowerCase());
}
/**
* Takes an array of Sequelize instances and returns an object with a root key
* based on the model name and an array of pojo records
*
* @method normalizeArrayResponse
* @param {Array} instances Sequelize instances
* @return {Promise}<Object, Error>
*
* @example
* ```javascript
* return orm.findAll("user").then(users => {
* return serializer.normalizeArrayResponse(instances);
* }).then(response => {
* /**
* * {
* * users: [{
* * }]
* * }
* });
* ```
*/
}, {
key: "normalizeArrayResponse",
value: function normalizeArrayResponse(instances, fallbackName) {
var _this2 = this;
var key = void 0;
if (!instances || !instances.length) {
key = _inflect2.default.camelize(_inflect2.default.pluralize(fallbackName), false);
return Promise.resolve(_defineProperty({}, key, []));
}
return Promise.all(instances.map(function (instance) {
key = key || _this2.keyForRecord(instance, false);
return _this2.normalizeRelationships(instance, instance);
})).then(function (records) {
return _this2._defineArrayResponse(key, records);
});
}
/**
* Takes a single Sequelize instance and returns an object with a root key based
* on the model name and a pojo record
*
* @method normalizeSingularResponse
* @param {Object} instance Sequelize model instance
* @return {Promise}<Object, Error>
*
* @example
* ```javascript
* return orm.findOne("user", 1).then(user => {
* return serializer.normalizeSingularResponse(instance, "findOne");
* }).then(response => {
* /**
* * {
* * user: {
* * }
* * }
* });
* ```
*/
}, {
key: "normalizeSingularResponse",
value: function normalizeSingularResponse(instance) {
var _this3 = this;
var key = this.keyForRecord(instance, true);
return this.normalizeRelationships(instance, instance).then(function (newRecord) {
return _this3._defineSingularResponse(key, newRecord);
});
}
/**
* @method normalizeRelationships
* @param {Object} instance Sequelize model instance
* @param {Object} payload Pojo representation of Sequelize model instance
* @return {Promis}<Object, Error>
*
* @example
* ```javascript
* return store.findOne("user", 1).then(user => {
* return serializer.normalizeRelationships(user, user.toJSON());
* }).then(response => {
* /**
* * {
* * user: {
* * projects: [1, 2, 3]
* * }
* * }
* });
* ```
*/
}, {
key: "normalizeRelationships",
value: function normalizeRelationships(instance, payload) {
var _this4 = this;
var associations = instance.constructor.associations;
return Promise.all(Object.keys(associations).map(function (association) {
return _this4.getRelationships(instance, associations[association]).then(function (relationships) {
return {
key: _this4.keyForRelationship(association),
records: relationships
};
});
})).then(function (relationships) {
relationships.forEach(function (relationship) {
payload[relationship.key] = relationship.records;
});
return payload;
});
}
/**
* Reformats the record into a RESTful object with the record name as the key.
* In addition, this will add a custom toJSON method on the response object
* that will serialize the response when sent through something like
* express#res.send, retaining the relationships on the instance, but removing
* all other extraneous data (see <a href="https://github.com/sequelize/sequelize/blob/16864699e0cc4b5fbc5bbf742b7a15eea9948e77/lib/model.js#L4005" target="_bank">Sequelize instance#toJSON</a>)
*
* @method _defineArrayResponse
* @private
* @param {String} key the name of the record (e.g. users)
* @param {Array<Object>} records Array of sequelize instances
* @returns {Object}
*
* @example
* ```javascript
* serializer._defineArrayResponse("users", [{
* dataValues: {
* firstName: "Hank",
* lastName: "Hill",
* projects: [1, 2]
* },
* someExtraneousProp: "foo"
* }]);
*
* /**
* * {
* * users: [{
* * dataValues: {
* * firstName: "Hank",
* * lastName: "Hill",
* * projects: [1, 2],
* * },
* * someExtraneousProp: "foo",
* * toJSON() {
* * }
* * }]
* * }
* *
* * response.toJSON()
* *
* * {
* * "users": [{
* * firstName: "Hank",
* * lastName: "Hill",
* * projects: [1, 2],
* * }]
* * }
* ```
*/
}, {
key: "_defineArrayResponse",
value: function _defineArrayResponse(key, records) {
var response = {};
var self = this;
Object.defineProperty(response, key, {
configurable: false,
enumerable: true,
value: records
});
Object.defineProperty(response, "toJSON", {
configurable: false,
enumerable: false,
value: function value() {
var recordArray = this[key];
var newRecords = recordArray.map(function (record) {
var associations = record.constructor.associations;
var newRecord = {};
Object.keys(associations).forEach(function (association) {
var associationKey = self.keyForRelationship(association);
if (record[associationKey]) {
newRecord[associationKey] = record[associationKey];
}
});
var plainInstance = record.toJSON();
Object.keys(plainInstance).forEach(function (property) {
newRecord[property] = plainInstance[property];
});
return newRecord;
});
return _defineProperty({}, key, newRecords);
}
});
return response;
}
/**
* Similar to {{#crossLink "RestSerializer/_defineArrayResponse:method"}}_defineArrayResponse{{/crossLink}},
* the difference being that this takes a single record and returns a singular response
*
* @method _defineSingularResponse
* @private
* @param {String} key the name of the record (e.g. users)
* @param {Object} record Sequelize instance
* @returns {Object}
*
* @example
* ```javascript
* serializer._defineSingularResponse("user", {
* dataValues: {
* firstName: "Hank",
* lastName: "Hill",
* projects: [1, 2]
* },
* someExtraneousProp: "foo",
* });
*
* /**
* * {
* * user: {
* * dataValues: {
* * firstName: "Hank",
* * lastName: "Hill",
* * projects: [1, 2],
* * someExtraneousProp: "foo",
* * toJSON() {
* * }
* * }
* * }
* *
* * response.toJSON()
* *
* * {
* * "user": [{
* * "firstName": "Hank",
* * "lastName": "Hill",
* * "projects": [1, 2],
* * }]
* * }
* ```
*/
}, {
key: "_defineSingularResponse",
value: function _defineSingularResponse(key, record) {
var response = {};
var self = this;
Object.defineProperty(response, key, {
configurable: false,
enumerable: true,
value: record
});
Object.defineProperty(response, "toJSON", {
configurable: false,
enumerable: false,
value: function value() {
var instance = this[key];
var associations = instance.constructor.associations;
var newRecord = {};
Object.keys(associations).forEach(function (association) {
var associationKey = self.keyForRelationship(association);
if (instance[associationKey]) {
newRecord[associationKey] = instance[associationKey];
}
});
var plainInstance = instance.toJSON();
Object.keys(plainInstance).forEach(function (property) {
newRecord[property] = plainInstance[property];
});
return _defineProperty({}, key, newRecord);
}
});
return response;
}
}]);
return RestSerializer;
}(_jsonSerializer2.default);
exports.default = RestSerializer;
module.exports = exports["default"];