diff --git a/.travis.yml b/.travis.yml index a821c44..d9fece1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ sudo: false language: node_js node_js: - '6' -- '5' -- '4' +- '8' +- node os: - osx - linux @@ -12,6 +12,6 @@ cache: directories: - $HOME/.npm before_install: -- npm config set spin false -script: npm run test:cover +- yarn config set spin false +script: yarn cover after_script: cat coverage/lcov.info | node_modules/coveralls/bin/coveralls.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a67eb43 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ + +# 0.1.0 (2017-10-24) + + +### Bug Fixes + +* **query:** add fallback name if no records exist ([1253d50](https://github.com/parch-js/rest-serializer/commit/1253d50)) + + +### Features + +* **serializer:** extend JSONSerializer ([83a8aa0](https://github.com/parch-js/rest-serializer/commit/83a8aa0)) +* **serializer:** remove associations argument from normalizeRelationships ([032e27f](https://github.com/parch-js/rest-serializer/commit/032e27f)) + + + diff --git a/docs/classes/RestSerializer.html b/docs/classes/RestSerializer.html index e8451e2..228d770 100644 --- a/docs/classes/RestSerializer.html +++ b/docs/classes/RestSerializer.html @@ -21,7 +21,7 @@
- API Docs for Version: 0.0.1 + API Docs for Version: 0.1.0
Defined in
- src/serializer.js:3
+ src/serializer.js:7
+ Defined in
+ src/serializer.js:212
+
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 Sequelize instance#toJSON)
+key
+ String
+
+
+ the name of the record (e.g. users)
+records
+ Array
+
+
+ Array of sequelize instances
+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],
+ * }]
+ * }
+
+
+
+ Defined in
+ src/serializer.js:303
+
Similar to _defineArrayResponse, +the difference being that this takes a single record and returns a singular response
+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],
+ * }]
+ * }
+
+
+ getRelationships
@@ -167,7 +401,7 @@
Defined in
- src/serializer.js:8
+ src/serializer.js:13
Defined in
- src/serializer.js:47
+ src/serializer.js:56
Defined in
- src/serializer.js:76
+ src/serializer.js:85
Defined in
- src/serializer.js:94
+ src/serializer.js:103
Defined in
- src/serializer.js:201
+ src/serializer.js:171
- Defined in
- src/serializer.js:137
-
Takes a single Sequelize instance and returns an object with a root key based -on the model name and a pojo record
-<Object, Error>
- -return orm.findOne("user", 1).then(user => {
- return serializer.normalizeResponse(instance, "findOne");
-}).then(response => {
- /**
- * {
- * user: {
- * }
- * }
-})
-
-
- normalizeSingularResponse
@@ -666,7 +813,7 @@
Defined in
- src/serializer.js:170
+ src/serializer.js:142
- API Docs for Version: 0.0.1 + API Docs for Version: 0.1.0
+"use strict"; + import inflect from "inflect"; +import JSONSerializer from "@parch-js/json-serializer"; + /** * @class RestSerializer + * @extends <a href="https://github.com/parch-js/json-serializer" target="_blank">JSONSerializer</a> * @constructor */ -export default class RestSerializer { +export default class RestSerializer extends JSONSerializer { /** * Returns an array of ids for a give hasMany/belongsToMany relatioship * @@ -102,10 +107,10 @@src/serializer.js File
* }); * ``` */ - async getRelationships(instance, association) { + getRelationships(instance, association) { const accessors = association.accessors; const isManyRelationship = Object.keys(accessors).some(accessor => { - return [ + const hasManyRelationshipAccessor = [ "add", "addMultiple", "count", @@ -113,12 +118,16 @@src/serializer.js File
"hasAll", "removeMultiple" ].some(valid => valid === accessor); + + return hasManyRelationshipAccessor; }); if (isManyRelationship) { - const relationships = await instance[association.accessors.get](); - - return relationships.map(relationship => relationship.id); + return instance[accessors.get]().then(relationships => + relationships.map(relationship => relationship.id) + ); + } else { + return Promise.resolve(); } } @@ -190,59 +199,22 @@src/serializer.js File
* }); * ``` */ - async normalizeArrayResponse(instances) { - const records = []; - const response = {}; + normalizeArrayResponse(instances, fallbackName) { let key; - for (const instance of instances) { - const json = instance.toJSON(); - - await this.normalizeRelationships(instance, json); - - records.push(json); + if (!instances || !instances.length) { + key = inflect.camelize(inflect.pluralize(fallbackName), false); - if (!key) { - key = this.keyForRecord(instance, false); - } + return Promise.resolve({ + [key]: [] + }); } - response[key] = records; - - return response; - } + return Promise.all(instances.map(instance => { + key = key || this.keyForRecord(instance, false); - /** - * Takes a single Sequelize instance and returns an object with a root key based - * on the model name and a pojo record - * - * @method normalizeResponse - * @param {Object} instance - * @param {String} method - * @return {Promise}<Object, Error> - * - * @example - * ```javascript - * return orm.findOne("user", 1).then(user => { - * return serializer.normalizeResponse(instance, "findOne"); - * }).then(response => { - * /** - * * { - * * user: { - * * } - * * } - * }) - * ``` - */ - async normalizeResponse(instance, method) { - switch (method) { - case "createRecord": - case "findOne": - case "updateRecord": - return this.normalizeSingularResponse(instance); - case "findAll": - return this.normalizeArrayResponse(instance); - } + return this.normalizeRelationships(instance, instance); + })).then(records => this._defineArrayResponse(key, records)); } /** @@ -266,14 +238,12 @@src/serializer.js File
* }); * ``` */ - async normalizeSingularResponse(instance) { - const json = instance.toJSON(); + normalizeSingularResponse(instance) { const key = this.keyForRecord(instance, true); - const response = { [key]: json } - await this.normalizeRelationships(instance, response[key]); - - return response; + return this.normalizeRelationships(instance, instance).then(newRecord => + this._defineSingularResponse(key, newRecord) + ); } /** @@ -296,22 +266,199 @@src/serializer.js File
* }); * ``` */ - async normalizeRelationships(instance, payload) { + normalizeRelationships(instance, payload) { const associations = instance.Model.associations; - for (const association in associations) { - const relationship = await this.getRelationships( - instance, - associations[association] - ); - const relationshipKey = this.keyForRelationship(association); + return Promise.all(Object.keys(associations).map(association => + this.getRelationships(instance, associations[association]).then(relationships => { + return { + key: this.keyForRelationship(association), + records: relationships + }; + }) + )).then(relationships => { + relationships.forEach(relationship => { + if (relationship.records) { + payload[relationship.key] = relationship.records; + } + }); + + return payload; + }); + } - if (relationship) { - payload[relationshipKey] = relationship; + /** + * 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], + * * }] + * * } + * ``` + */ + _defineArrayResponse(key, records) { + const response = {}; + + Object.defineProperty(response, key, { + configurable: false, + enumerable: true, + value: records + }); + + Object.defineProperty(response, "toJSON", { + configurable: false, + enumerable: false, + value() { + const recordArray = this[key]; + const newRecords = recordArray.map(record => { + const associations = record.Model.associations; + const newRecord = {}; + + Object.keys(associations).forEach(association => { + if (record[association]) { + newRecord[association] = record[association]; + } + }); + + const plainInstance = record.toJSON(); + + Object.keys(plainInstance).forEach(property => { + newRecord[property] = plainInstance[property]; + }); + + return newRecord; + }); + + return { + [key]: newRecords + }; } - } + }); - return payload; + 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], + * * }] + * * } + * ``` + */ + _defineSingularResponse(key, record) { + const response = {}; + + Object.defineProperty(response, key, { + configurable: false, + enumerable: true, + value: record + }); + + Object.defineProperty(response, "toJSON", { + configurable: false, + enumerable: false, + value() { + const instance = this[key]; + const associations = instance.Model.associations; + const newRecord = {}; + + Object.keys(associations).forEach(association => { + if (instance[association]) { + newRecord[association] = instance[association]; + } + }); + + const plainInstance = instance.toJSON(); + + Object.keys(plainInstance).forEach(property => { + newRecord[property] = plainInstance[property]; + }); + + return { + [key]: newRecord + }; + } + }); + + return response; } } diff --git a/docs/index.html b/docs/index.html index 6a6faf3..3fa1ef9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -21,7 +21,7 @@
- API Docs for Version: 0.0.1 + API Docs for Version: 0.1.0