UNPKG

@parch-js/rest-serializer

Version:

RestSerializer for parch to normalize request/response

448 lines (385 loc) 14.1 kB
"use strict"; 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"];