define('ember-data/serializers/json', ['exports', 'ember-data/serializer', 'ember-data/-private'], function (exports, _serializer, _private) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var emberAssign = Ember.assign || Ember.merge; /** Ember Data 2.0 Serializer: In Ember Data a Serializer is used to serialize and deserialize records when they are transferred in and out of an external source. This process involves normalizing property names, transforming attribute values and serializing relationships. By default, Ember Data uses and recommends the `JSONAPISerializer`. `JSONSerializer` is useful for simpler or legacy backends that may not support the http://jsonapi.org/ spec. For example, given the following `User` model and JSON payload: ```app/models/user.js import DS from 'ember-data'; export default DS.Model.extend({ friends: DS.hasMany('user'), house: DS.belongsTo('location'), name: DS.attr('string') }); ``` ```js { id: 1, name: 'Sebastian', friends: [3, 4], links: { house: '/houses/lefkada' } } ``` `JSONSerializer` will normalize the JSON payload to the JSON API format that the Ember Data store expects. You can customize how JSONSerializer processes its payload by passing options in the `attrs` hash or by subclassing the `JSONSerializer` and overriding hooks: - To customize how a single record is normalized, use the `normalize` hook. - To customize how `JSONSerializer` normalizes the whole server response, use the `normalizeResponse` hook. - To customize how `JSONSerializer` normalizes a specific response from the server, use one of the many specific `normalizeResponse` hooks. - To customize how `JSONSerializer` normalizes your id, attributes or relationships, use the `extractId`, `extractAttributes` and `extractRelationships` hooks. The `JSONSerializer` normalization process follows these steps: - `normalizeResponse` - entry method to the serializer. - `normalizeCreateRecordResponse` - a `normalizeResponse` for a specific operation is called. - `normalizeSingleResponse`|`normalizeArrayResponse` - for methods like `createRecord` we expect a single record back, while for methods like `findAll` we expect multiple records back. - `normalize` - `normalizeArray` iterates and calls `normalize` for each of its records while `normalizeSingle` calls it once. This is the method you most likely want to subclass. - `extractId` | `extractAttributes` | `extractRelationships` - `normalize` delegates to these methods to turn the record payload into the JSON API format. @class JSONSerializer @namespace DS @extends DS.Serializer */ var JSONSerializer = _serializer.default.extend({ /** The `primaryKey` is used when serializing and deserializing data. Ember Data always uses the `id` property to store the id of the record. The external source may not always follow this convention. In these cases it is useful to override the `primaryKey` property to match the `primaryKey` of your external store. Example ```app/serializers/application.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ primaryKey: '_id' }); ``` @property primaryKey @type {String} @default 'id' */ primaryKey: 'id', /** The `attrs` object can be used to declare a simple mapping between property names on `DS.Model` records and payload keys in the serialized JSON object representing the record. An object with the property `key` can also be used to designate the attribute's key on the response payload. Example ```app/models/person.js import DS from 'ember-data'; export default DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), occupation: DS.attr('string'), admin: DS.attr('boolean') }); ``` ```app/serializers/person.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ attrs: { admin: 'is_admin', occupation: { key: 'career' } } }); ``` You can also remove attributes by setting the `serialize` key to `false` in your mapping object. Example ```app/serializers/person.js import DS from 'ember-data'; export default DS.JSONSerializer.extend({ attrs: { admin: { serialize: false }, occupation: { key: 'career' } } }); ``` When serialized: ```javascript { "firstName": "Harry", "lastName": "Houdini", "career": "magician" } ``` Note that the `admin` is now not included in the payload. @property attrs @type {Object} */ mergedProperties: ['attrs'], applyTransforms: function applyTransforms(typeClass, data) { var _this = this; var attributes = Ember.get(typeClass, 'attributes'); typeClass.eachTransformedAttribute(function (key, typeClass) { if (data[key] === undefined) { return; } var transform = _this.transformFor(typeClass); var transformMeta = attributes.get(key); data[key] = transform.deserialize(data[key], transformMeta.options); }); return data; }, normalizeResponse: function normalizeResponse(store, primaryModelClass, payload, id, requestType) { switch (requestType) { case 'findRecord': return this.normalizeFindRecordResponse.apply(this, arguments); case 'queryRecord': return this.normalizeQueryRecordResponse.apply(this, arguments); case 'findAll': return this.normalizeFindAllResponse.apply(this, arguments); case 'findBelongsTo': return this.normalizeFindBelongsToResponse.apply(this, arguments); case 'findHasMany': return this.normalizeFindHasManyResponse.apply(this, arguments); case 'findMany': return this.normalizeFindManyResponse.apply(this, arguments); case 'query': return this.normalizeQueryResponse.apply(this, arguments); case 'createRecord': return this.normalizeCreateRecordResponse.apply(this, arguments); case 'deleteRecord': return this.normalizeDeleteRecordResponse.apply(this, arguments); case 'updateRecord': return this.normalizeUpdateRecordResponse.apply(this, arguments); } }, normalizeFindRecordResponse: function normalizeFindRecordResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSingleResponse.apply(this, arguments); }, normalizeQueryRecordResponse: function normalizeQueryRecordResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSingleResponse.apply(this, arguments); }, normalizeFindAllResponse: function normalizeFindAllResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeArrayResponse.apply(this, arguments); }, normalizeFindBelongsToResponse: function normalizeFindBelongsToResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSingleResponse.apply(this, arguments); }, normalizeFindHasManyResponse: function normalizeFindHasManyResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeArrayResponse.apply(this, arguments); }, normalizeFindManyResponse: function normalizeFindManyResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeArrayResponse.apply(this, arguments); }, normalizeQueryResponse: function normalizeQueryResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeArrayResponse.apply(this, arguments); }, normalizeCreateRecordResponse: function normalizeCreateRecordResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSaveResponse.apply(this, arguments); }, normalizeDeleteRecordResponse: function normalizeDeleteRecordResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSaveResponse.apply(this, arguments); }, normalizeUpdateRecordResponse: function normalizeUpdateRecordResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSaveResponse.apply(this, arguments); }, normalizeSaveResponse: function normalizeSaveResponse(store, primaryModelClass, payload, id, requestType) { return this.normalizeSingleResponse.apply(this, arguments); }, normalizeSingleResponse: function normalizeSingleResponse(store, primaryModelClass, payload, id, requestType) { return this._normalizeResponse(store, primaryModelClass, payload, id, requestType, true); }, normalizeArrayResponse: function normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) { return this._normalizeResponse(store, primaryModelClass, payload, id, requestType, false); }, _normalizeResponse: function _normalizeResponse(store, primaryModelClass, payload, id, requestType, isSingle) { var documentHash = { data: null, included: [] }; var meta = this.extractMeta(store, primaryModelClass, payload); if (meta) { (true && !(Ember.typeOf(meta) === 'object') && Ember.assert('The `meta` returned from `extractMeta` has to be an object, not "' + Ember.typeOf(meta) + '".', Ember.typeOf(meta) === 'object')); documentHash.meta = meta; } if (isSingle) { var _normalize = this.normalize(primaryModelClass, payload), data = _normalize.data, included = _normalize.included; documentHash.data = data; if (included) { documentHash.included = included; } } else { var ret = new Array(payload.length); for (var i = 0, l = payload.length; i < l; i++) { var item = payload[i]; var _normalize2 = this.normalize(primaryModelClass, item), _data = _normalize2.data, _included = _normalize2.included; if (_included) { var _documentHash$include; (_documentHash$include = documentHash.included).push.apply(_documentHash$include, _included); } ret[i] = _data; } documentHash.data = ret; } return documentHash; }, normalize: function normalize(modelClass, resourceHash) { var data = null; if (resourceHash) { this.normalizeUsingDeclaredMapping(modelClass, resourceHash); if (Ember.typeOf(resourceHash.links) === 'object') { this.normalizeUsingDeclaredMapping(modelClass, resourceHash.links); } data = { id: this.extractId(modelClass, resourceHash), type: modelClass.modelName, attributes: this.extractAttributes(modelClass, resourceHash), relationships: this.extractRelationships(modelClass, resourceHash) }; this.applyTransforms(modelClass, data.attributes); } return { data: data }; }, extractId: function extractId(modelClass, resourceHash) { var primaryKey = Ember.get(this, 'primaryKey'); var id = resourceHash[primaryKey]; return (0, _private.coerceId)(id); }, extractAttributes: function extractAttributes(modelClass, resourceHash) { var _this2 = this; var attributeKey = void 0; var attributes = {}; modelClass.eachAttribute(function (key) { attributeKey = _this2.keyForAttribute(key, 'deserialize'); if (resourceHash[attributeKey] !== undefined) { attributes[key] = resourceHash[attributeKey]; } }); return attributes; }, extractRelationship: function extractRelationship(relationshipModelName, relationshipHash) { if (Ember.isNone(relationshipHash)) { return null; } /* When `relationshipHash` is an object it usually means that the relationship is polymorphic. It could however also be embedded resources that the EmbeddedRecordsMixin has be able to process. */ if (Ember.typeOf(relationshipHash) === 'object') { if (relationshipHash.id) { relationshipHash.id = (0, _private.coerceId)(relationshipHash.id); } var modelClass = this.store.modelFor(relationshipModelName); if (relationshipHash.type && !(0, _private.modelHasAttributeOrRelationshipNamedType)(modelClass)) { if ((0, _private.isEnabled)("ds-payload-type-hooks")) { var modelName = this.modelNameFromPayloadType(relationshipHash.type); var deprecatedModelNameLookup = this.modelNameFromPayloadKey(relationshipHash.type); if (modelName !== deprecatedModelNameLookup && this._hasCustomModelNameFromPayloadKey()) { (true && !(false) && Ember.deprecate("You used modelNameFromPayloadKey to customize how a type is normalized. Use modelNameFromPayloadType instead", false, { id: 'ds.json-serializer.deprecated-type-for-polymorphic-relationship', until: '3.0.0' })); modelName = deprecatedModelNameLookup; } relationshipHash.type = modelName; } else { relationshipHash.type = this.modelNameFromPayloadKey(relationshipHash.type); } } return relationshipHash; } return { id: (0, _private.coerceId)(relationshipHash), type: relationshipModelName }; }, extractPolymorphicRelationship: function extractPolymorphicRelationship(relationshipModelName, relationshipHash, relationshipOptions) { return this.extractRelationship(relationshipModelName, relationshipHash); }, extractRelationships: function extractRelationships(modelClass, resourceHash) { var _this3 = this; var relationships = {}; modelClass.eachRelationship(function (key, relationshipMeta) { var relationship = null; var relationshipKey = _this3.keyForRelationship(key, relationshipMeta.kind, 'deserialize'); if (resourceHash[relationshipKey] !== undefined) { var data = null; var relationshipHash = resourceHash[relationshipKey]; if (relationshipMeta.kind === 'belongsTo') { if (relationshipMeta.options.polymorphic) { // extracting a polymorphic belongsTo may need more information // than the type and the hash (which might only be an id) for the // relationship, hence we pass the key, resource and // relationshipMeta too data = _this3.extractPolymorphicRelationship(relationshipMeta.type, relationshipHash, { key: key, resourceHash: resourceHash, relationshipMeta: relationshipMeta }); } else { data = _this3.extractRelationship(relationshipMeta.type, relationshipHash); } } else if (relationshipMeta.kind === 'hasMany') { if (!Ember.isNone(relationshipHash)) { data = new Array(relationshipHash.length); for (var i = 0, l = relationshipHash.length; i < l; i++) { var item = relationshipHash[i]; data[i] = _this3.extractRelationship(relationshipMeta.type, item); } } } relationship = { data: data }; } var linkKey = _this3.keyForLink(key, relationshipMeta.kind); if (resourceHash.links && resourceHash.links[linkKey] !== undefined) { var related = resourceHash.links[linkKey]; relationship = relationship || {}; relationship.links = { related: related }; } if (relationship) { relationships[key] = relationship; } }); return relationships; }, modelNameFromPayloadKey: function modelNameFromPayloadKey(key) { return (0, _private.normalizeModelName)(key); }, normalizeRelationships: function normalizeRelationships(typeClass, hash) { var _this4 = this; var payloadKey = void 0; if (this.keyForRelationship) { typeClass.eachRelationship(function (key, relationship) { payloadKey = _this4.keyForRelationship(key, relationship.kind, 'deserialize'); if (key === payloadKey) { return; } if (hash[payloadKey] === undefined) { return; } hash[key] = hash[payloadKey]; delete hash[payloadKey]; }); } }, normalizeUsingDeclaredMapping: function normalizeUsingDeclaredMapping(modelClass, hash) { var attrs = Ember.get(this, 'attrs'); var normalizedKey = void 0; var payloadKey = void 0; if (attrs) { for (var key in attrs) { normalizedKey = payloadKey = this._getMappedKey(key, modelClass); if (hash[payloadKey] === undefined) { continue; } if (Ember.get(modelClass, 'attributes').has(key)) { normalizedKey = this.keyForAttribute(key); } if (Ember.get(modelClass, 'relationshipsByName').has(key)) { normalizedKey = this.keyForRelationship(key); } if (payloadKey !== normalizedKey) { hash[normalizedKey] = hash[payloadKey]; delete hash[payloadKey]; } } } }, _getMappedKey: function _getMappedKey(key, modelClass) { (true && Ember.warn('There is no attribute or relationship with the name `' + key + '` on `' + modelClass.modelName + '`. Check your serializers attrs hash.', Ember.get(modelClass, 'attributes').has(key) || Ember.get(modelClass, 'relationshipsByName').has(key), { id: 'ds.serializer.no-mapped-attrs-key' })); var attrs = Ember.get(this, 'attrs'); var mappedKey = void 0; if (attrs && attrs[key]) { mappedKey = attrs[key]; //We need to account for both the { title: 'post_title' } and //{ title: { key: 'post_title' }} forms if (mappedKey.key) { mappedKey = mappedKey.key; } if (typeof mappedKey === 'string') { key = mappedKey; } } return key; }, _canSerialize: function _canSerialize(key) { var attrs = Ember.get(this, 'attrs'); return !attrs || !attrs[key] || attrs[key].serialize !== false; }, _mustSerialize: function _mustSerialize(key) { var attrs = Ember.get(this, 'attrs'); return attrs && attrs[key] && attrs[key].serialize === true; }, shouldSerializeHasMany: function shouldSerializeHasMany(snapshot, key, relationship) { if (this._shouldSerializeHasMany !== JSONSerializer.prototype._shouldSerializeHasMany) { (true && !(false) && Ember.deprecate('The private method _shouldSerializeHasMany has been promoted to the public API. Please remove the underscore to use the public shouldSerializeHasMany method.', false, { id: 'ds.serializer.private-should-serialize-has-many', until: '3.0.0' })); } return this._shouldSerializeHasMany(snapshot, key, relationship); }, _shouldSerializeHasMany: function _shouldSerializeHasMany(snapshot, key, relationship) { var relationshipType = snapshot.type.determineRelationshipType(relationship, this.store); if (this._mustSerialize(key)) { return true; } return this._canSerialize(key) && (relationshipType === 'manyToNone' || relationshipType === 'manyToMany'); }, serialize: function serialize(snapshot, options) { var _this5 = this; var json = {}; if (options && options.includeId) { if ((0, _private.isEnabled)('ds-serialize-id')) { this.serializeId(snapshot, json, Ember.get(this, 'primaryKey')); } else { var id = snapshot.id; if (id) { json[Ember.get(this, 'primaryKey')] = id; } } } snapshot.eachAttribute(function (key, attribute) { _this5.serializeAttribute(snapshot, json, key, attribute); }); snapshot.eachRelationship(function (key, relationship) { if (relationship.kind === 'belongsTo') { _this5.serializeBelongsTo(snapshot, json, relationship); } else if (relationship.kind === 'hasMany') { _this5.serializeHasMany(snapshot, json, relationship); } }); return json; }, serializeIntoHash: function serializeIntoHash(hash, typeClass, snapshot, options) { emberAssign(hash, this.serialize(snapshot, options)); }, serializeAttribute: function serializeAttribute(snapshot, json, key, attribute) { if (this._canSerialize(key)) { var type = attribute.type; var value = snapshot.attr(key); if (type) { var transform = this.transformFor(type); value = transform.serialize(value, attribute.options); } // if provided, use the mapping provided by `attrs` in // the serializer var payloadKey = this._getMappedKey(key, snapshot.type); if (payloadKey === key && this.keyForAttribute) { payloadKey = this.keyForAttribute(key, 'serialize'); } json[payloadKey] = value; } }, serializeBelongsTo: function serializeBelongsTo(snapshot, json, relationship) { var key = relationship.key; if (this._canSerialize(key)) { var belongsToId = snapshot.belongsTo(key, { id: true }); // if provided, use the mapping provided by `attrs` in // the serializer var payloadKey = this._getMappedKey(key, snapshot.type); if (payloadKey === key && this.keyForRelationship) { payloadKey = this.keyForRelationship(key, "belongsTo", "serialize"); } //Need to check whether the id is there for new&async records if (Ember.isNone(belongsToId)) { json[payloadKey] = null; } else { json[payloadKey] = belongsToId; } if (relationship.options.polymorphic) { this.serializePolymorphicType(snapshot, json, relationship); } } }, serializeHasMany: function serializeHasMany(snapshot, json, relationship) { var key = relationship.key; var shouldSerializeHasMany = '_shouldSerializeHasMany'; if ((0, _private.isEnabled)("ds-check-should-serialize-relationships")) { shouldSerializeHasMany = 'shouldSerializeHasMany'; } if (this[shouldSerializeHasMany](snapshot, key, relationship)) { var hasMany = snapshot.hasMany(key, { ids: true }); if (hasMany !== undefined) { // if provided, use the mapping provided by `attrs` in // the serializer var payloadKey = this._getMappedKey(key, snapshot.type); if (payloadKey === key && this.keyForRelationship) { payloadKey = this.keyForRelationship(key, "hasMany", "serialize"); } json[payloadKey] = hasMany; // TODO support for polymorphic manyToNone and manyToMany relationships } } }, serializePolymorphicType: function serializePolymorphicType() {}, extractMeta: function extractMeta(store, modelClass, payload) { if (payload && payload['meta'] !== undefined) { var meta = payload.meta; delete payload.meta; return meta; } }, extractErrors: function extractErrors(store, typeClass, payload, id) { var _this6 = this; if (payload && typeof payload === 'object' && payload.errors) { payload = (0, _private.errorsArrayToHash)(payload.errors); this.normalizeUsingDeclaredMapping(typeClass, payload); typeClass.eachAttribute(function (name) { var key = _this6.keyForAttribute(name, 'deserialize'); if (key !== name && payload[key] !== undefined) { payload[name] = payload[key]; delete payload[key]; } }); typeClass.eachRelationship(function (name) { var key = _this6.keyForRelationship(name, 'deserialize'); if (key !== name && payload[key] !== undefined) { payload[name] = payload[key]; delete payload[key]; } }); } return payload; }, keyForAttribute: function keyForAttribute(key, method) { return key; }, keyForRelationship: function keyForRelationship(key, typeClass, method) { return key; }, keyForLink: function keyForLink(key, kind) { return key; }, transformFor: function transformFor(attributeType, skipAssertion) { var transform = (0, _private.getOwner)(this).lookup('transform:' + attributeType); (true && !(skipAssertion || !!transform) && Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform)); return transform; } }); if ((0, _private.isEnabled)("ds-payload-type-hooks")) { JSONSerializer.reopen({ modelNameFromPayloadType: function modelNameFromPayloadType(type) { return (0, _private.normalizeModelName)(type); }, _hasCustomModelNameFromPayloadKey: function _hasCustomModelNameFromPayloadKey() { return this.modelNameFromPayloadKey !== JSONSerializer.prototype.modelNameFromPayloadKey; } }); } if ((0, _private.isEnabled)("ds-serialize-id")) { JSONSerializer.reopen({ serializeId: function serializeId(snapshot, json, primaryKey) { var id = snapshot.id; if (id) { json[primaryKey] = id; } } }); } exports.default = JSONSerializer; });