UNPKG

@pgengler/ember-cli-mirage

Version:

A client-side server to help you build, test and demo your Ember app

310 lines (267 loc) 9.68 kB
import { RestSerializer, _utilsInflectorCamelize as camelize, _utilsInflectorDasherize as dasherize, } from 'miragejs'; import { isFunction } from 'lodash-es'; import { get } from '@ember/object'; /** * This serializer does not use following mirage properties to control how things are serialized * * attrs - see `serialize` on the transform property * embed - see `serialize` on the transform property * serializeIds - see serialize on the transform property * * The above configuration was applied to every property on the serializer, whereas the transforms allows you * to specify a value for each property or relation. * * This serializer uses a property `transforms` that follows the Ember Data serializer format of `attrs` to specify the * serialization (`attrs` is already in use by mirageJs). * * The startMirage has been modified to also generate serializers from the Ember Data serializers supporting * the key and the serialize/deserialize properties with the value of (true/ids/records). If a serializer is * already present in the mirage directory, the transforms will be added to it. If that serializer is not * a serializer of this type, there will be no effect. Ensure that your serializers and/or the application * serializer in the mirage directory is a type of this serializer * * @class EmberDataSerializer * @constructor * @public * */ let EmberDataSerializer = RestSerializer.extend({ /** * The property name for the primary key for mirage and ember data is normally `id`. This allows you * to specify what that property name should be in the JSON. */ primaryKey: 'id', /** * Transforms follow the format of ember data serializer attrs as follows * * { * property: { // property would be the name of the property in the mirage store * key: 'externalKey', // externalKey would be the name in the JSON * serialize: 'ids', // how should this property be serialized and deserialized * deserialize: 'ids' // the default is 'ids' that is the id of the releation * // or 'records', that is, embed the full record instead of the id * // or false, do not serialize or deserialize as applied * } * } * * These transforms will be created from the attrs on the corresponding serializer in ember data much like * the models for mirage are created from the models in in ember date. If the transforms key is specified, it * will overlay the definition created from the serializer key for key. That is you could override some * of the transform definition. */ transforms: undefined, // resolved transforms _transforms: undefined, // These are the defaults // include: [] keyForId() { return this.primaryKey; }, getKeysForIncluded() { return isFunction(this.include) ? this.include(this.request, this.primaryResource) : this.include; }, getTransforms() { return this.transforms || {}; }, getResolvedTransforms() { if (!this._resolvedTransforms) { this._resolvedTransforms = { serialize: {}, normalize: {}, }; } return this._resolvedTransforms; }, getTransformForSerialize(key) { let resolvedTransforms = this.getResolvedTransforms(); let transforms = this.getTransforms(); if (!resolvedTransforms.serialize[key]) { let transform = typeof transforms[key] === 'string' ? { key: transforms[key] } : Object.assign({}, transforms[key]); resolvedTransforms.serialize[key] = Object.assign( { key: key, serialize: 'ids', deserialize: 'ids' }, transform ); } return resolvedTransforms.serialize[key]; }, getTransformForNormalize(key) { let resolvedTransforms = this.getResolvedTransforms(); if (!resolvedTransforms.normalize[key]) { let transforms = this.getTransforms(); let foundKey; let foundTransform = Object.keys(transforms).find((item) => { foundKey = item; return transforms[item].key === key; }); let transform = foundTransform ? Object.assign({}, transforms[foundKey], { key: foundKey }) : { key: key, serialize: 'ids', deserialize: 'ids' }; resolvedTransforms.normalize[key] = transform; } return resolvedTransforms.normalize[key]; }, /** * * @param model * @param removeForeignKeys * @param didSerialize * @returns {*} * @private */ _hashForModel(model, removeForeignKeys, didSerialize = {}) { let attrs = this._attrsForModel(model); let newDidSerialize = Object.assign({}, didSerialize); newDidSerialize[model.modelName] = newDidSerialize[model.modelName] || {}; newDidSerialize[model.modelName][model.id] = true; model.associationKeys.forEach((key) => { let transform = this.getTransformForSerialize(key); if (transform.serialize) { let associatedResource = model[key]; let serializeOption = transform.serialize; if ( associatedResource && get( newDidSerialize, `${associatedResource.modelName}.${associatedResource.id}` ) ) { // force it to IDS if we already have serialized it to prevent recursion // TODO: However is the end system wants records, we need to send records, so this really should be do records, dont resurse serializeOption = 'ids'; } if (serializeOption === 'records') { let [associatedResourceHash] = this.getHashForResource( associatedResource, false, newDidSerialize, true ); let formattedKey = this._keyForProperty(key) || this.isCollection(associatedResource) ? this.keyForRelationship(key) : this.keyForEmbeddedRelationship(key); attrs[formattedKey] = associatedResourceHash; } else { let formattedKey = this._keyForProperty(key) || this.keyForRelationshipIds(key); if (this.isCollection(associatedResource)) { attrs[formattedKey] = model[`${this._container.inflector.singularize(key)}Ids`]; } else { attrs[formattedKey] = model[`${this._container.inflector.singularize(key)}Id`]; } } } }); return attrs; }, _keyForProperty(attr) { let transform = this.getTransformForSerialize(attr); return transform.key; }, keyForAttribute(attr) { if (attr === 'id') { return this.keyForId(); } return ( this._keyForProperty(attr) || RestSerializer.prototype.keyForAttribute.apply(this, arguments) ); }, keyForRelationship(type) { return ( this._keyForProperty(type) || RestSerializer.prototype.keyForRelationship.apply(this, arguments) ); }, keyForEmbeddedRelationship(attributeName) { return ( this._keyForProperty(attributeName) || RestSerializer.prototype.keyForEmbeddedRelationship.apply(this, arguments) ); }, keyForRelationshipIds(type) { return ( this._keyForProperty(type) || RestSerializer.prototype.keyForRelationshipIds.apply(this, arguments) ); }, keyForForeignKey(relationshipName) { return ( this._keyForProperty(relationshipName) || RestSerializer.prototype.keyForForeignKey.apply(this, arguments) ); }, normalize(payload) { // was it not wrapped when serialized? if (this.root === false) { let p = {}; p[this.type] = payload; payload = p; } let type = Object.keys(payload)[0]; let attrs = payload[type]; let modelName = camelize(type); let modelClass = this.schema.modelClassFor(modelName); let { belongsToAssociations, hasManyAssociations } = modelClass; let belongsToKeys = Object.keys(belongsToAssociations); let hasManyKeys = Object.keys(hasManyAssociations); let jsonApiPayload = { data: { type: this._container.inflector.pluralize(type), attributes: {}, }, }; if (attrs[this.primaryKey]) { jsonApiPayload.data.id = attrs[this.primaryKey]; } let relationships = {}; Object.keys(attrs).forEach((attrKey) => { if (attrKey !== this.primaryKey) { let transform = this.getTransformForNormalize(attrKey); let key = transform.key || attrKey; if (this.normalizeIds) { if (belongsToKeys.includes(key)) { let association = belongsToAssociations[key]; let associationModel = association.modelName; relationships[dasherize(key)] = { data: { type: associationModel, id: attrs[attrKey], }, }; } else if (hasManyKeys.includes(key)) { let association = hasManyAssociations[key]; let associationModel = association.modelName; let data = attrs[attrKey].map((id) => { return { type: associationModel, id, }; }); relationships[dasherize(key)] = { data }; } else { jsonApiPayload.data.attributes[dasherize(key)] = attrs[attrKey]; } } else { jsonApiPayload.data.attributes[dasherize(key)] = attrs[attrKey]; } } }); if (Object.keys(relationships).length) { jsonApiPayload.data.relationships = relationships; } return jsonApiPayload; }, }); export default EmberDataSerializer;