Skip to content

Instantly share code, notes, and snippets.

@hussfelt
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hussfelt/064506796c7e94e85e47 to your computer and use it in GitHub Desktop.
Save hussfelt/064506796c7e94e85e47 to your computer and use it in GitHub Desktop.
WRONG IMPLEMENTATION! RestSerializer Mixin to convert old API data to new JSONApi 2.0
import DS from 'ember-data';
import rest_serializer from 'app/mixins/serializers/rest';
export default DS.RESTSerializer.extend(rest_serializer, {
/**
* normalizeResponseExtra, triggered from rest_serializer
* @param {[type]} data [description]
* @param {[type]} payload [description]
* @param {[type]} primaryModelClass [description]
* @return {[type]} [description]
*/
normalizeResponseExtra: function(data, payload, primaryModelClass) {
// If we have sideloaded_data in the original payload
if (typeof payload.sideloaded_data !== 'undefined') {
// This is where you could work with and convert any sideloaded data
// And then build on the "data" variable
}
// Return the data
return data;
},
/**
* normalizeArrayResponseExtra, triggered from rest_serializer
* @param {[type]} data [description]
* @param {[type]} payload [description]
* @param {[type]} primaryModelClass [description]
* @return {[type]} [description]
*/
normalizeArrayResponseExtra: function(data, payload/*, primaryModelClass*/) {
// Normalize sideloaded-singular-type-name
data = this.normalizeSideloadedData(data, payload, 'sideloaded-singular-type-name', 'models-related-property-name');
// Return JSONApi 2.0 document
return data;
}
});
import Ember from 'ember';
//import DS from 'ember-data';
export default Ember.Mixin.create({
isNewSerializerAPI: true,
/**
* [normalizeSingleResponse description]
* @param {[type]} store [description]
* @param {[type]} primaryModelClass [description]
* @param {[type]} payload [description]
* @param {[type]} id [description]
* @param {[type]} requestType [description]
* @return {[type]} [description]
*/
normalizeSingleResponse: function(store, primaryModelClass, payload/*, id, requestType*/) {
// Check if we want to do more normalization-before
if (typeof this.normalizeSingleResponseExtraBefore === 'function') {
// Overwrite data
payload = this.normalizeSingleResponseExtraBefore(payload, primaryModelClass);
}
var data = {},
extracted = {},
root = 'data',
type = primaryModelClass.modelName;
// set the ID if there is one
if (typeof payload[type]['id'] !== 'undefined') {
extracted.id = payload[type]['id'];
}
// Set attributes
extracted.attributes = payload[type];
delete extracted.attributes.id;
// set the type
extracted.type = type;
// Set data
data[root] = extracted;
// Normalize any relations for this object
data = this.normalizeSingleResponseRelations(data);
// Check if we want to do more normalization
if (typeof this.normalizeSingleResponseExtra === 'function') {
// Overwrite data
data = this.normalizeSingleResponseExtra(data, payload, primaryModelClass);
}
// Return the JSONApi 2.0 payload
return data;
},
/**
* normalizeArrayResponse description
* @param {[type]} store [description]
* @param {[type]} primaryModelClass [description]
* @param {[type]} payload [description]
* @param {[type]} id [description]
* @param {[type]} requestType [description]
* @return {[type]} [description]
*/
normalizeArrayResponse: function(store, primaryModelClass, payload/*, id, requestType*/) {
var data = {},
extracted = [],
root = 'data',
type = Ember.String.underscore(Ember.String.pluralize(primaryModelClass.modelName)),
typeSingular = Ember.String.underscore(primaryModelClass.modelName);
// Check if we want to do more normalization-before
if (typeof this.normalizeArrayResponseExtraBefore === 'function') {
// Overwrite payload
payload = this.normalizeArrayResponseExtraBefore(payload, primaryModelClass);
}
//Check if payload type exists
if (typeof payload[type] === 'undefined') {
throw new Error('Missing payload data: ' + type);
}
payload[type].forEach(function(e) {
e.attributes = {};
// iterate through the object and push any attributes into the attributes array
Object.keys(e).forEach(function(key) {
// but don't push id or attributes itself into the array
if (key !== 'id' && key !== 'attributes') {
e.attributes[key] = e[key];
delete e[key];
}
});
// set the type
e.type = typeSingular;
// set the ID if there is one
if (typeof e['id'] !== 'undefined') {
e.id = e['id'];
}
extracted.push(e);
});
data[root] = extracted;
// Check if we want to do more normalization
if (typeof this.normalizeArrayResponseExtra === 'function') {
// Overwrite data
data = this.normalizeArrayResponseExtra(data, payload, primaryModelClass);
}
// Last but not least, check if we have anything we want to move to relational object
// We do this after everything because sometimes we trigger sideloaded data, and that
// itself actually extracts what it needs into a relation-object allready.
// This trigger only takes all properties left in the attributes object and converts
// what it needs to the relations object
data = this.normalizeArrayResponseRelations(data);
// Return the JSONApi 2.0 payload
return data;
},
/**
* [normalizeSideloadedData description]
* @param {[type]} data [description]
* @param {[type]} payload [description]
* @param {[type]} modelNameToLookFor [description]
* @param {[type]} modelRelationProperty [description]
* @return {[type]} [description]
*/
normalizeSideloadedData: function(data, payload, modelNameToLookFor, modelRelationProperty) {
// Pluralize type
var type = Ember.String.underscore(Ember.String.pluralize(modelNameToLookFor));
// Check if it has sideloaded type data
if (typeof payload[type] !== 'undefined') {
var extracted = [],
typeSingular = Ember.String.underscore(modelNameToLookFor);
payload[type].forEach(function(e) {
e.attributes = {};
// iterate through the object and push any attributes into the attributes array
Object.keys(e).forEach(function(key) {
// but don't push id or attributes itself into the array
if (key !== 'id' && key !== 'attributes') {
e.attributes[key] = e[key];
delete e[key];
}
});
// set the type
e.type = typeSingular;
// Break application if broken sideloaded data
if (typeof e['id'] === 'undefined') {
throw new Error('Invalid sideloaded data:' + type);
}
// Set id
e.id = e['id'];
// Push the object
extracted.push(e);
});
// Check if we already have an included property
if (typeof data.included === 'undefined') {
data.included = [];
}
// Concatenate array data with the included property
data.included = data.included.concat(extracted);
// Loop through and remove all the relationship data in the original entities, convert to relationships
data.data.forEach(function(o) {
// Check if we have a relationships container
if (typeof o.relationships === 'undefined') {
o.relationships = {};
}
// Check if we have a contaner for this modelRelationProperty
if (typeof o.relationships[modelRelationProperty] === 'undefined') {
o.relationships[modelRelationProperty] = {};
o.relationships[modelRelationProperty].data = [];
}
// Convert the attributes to relationship
o.attributes[modelRelationProperty].forEach(function(relation) {
o.relationships[modelRelationProperty].data.push({
id: relation,
type: typeSingular
});
});
// Delete the attribute-data
delete o.attributes[modelRelationProperty];
});
}
// Return the modified JSONApi 2.0 data payload
return data;
},
/**
* [normalizeArrayResponseRelations description]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
normalizeArrayResponseRelations: function (data) {
// Loop through all records
for (var y=0;y<data.data.length;y++) {
// Get all properties for this model
var propertiesArray = this.mapPropertiesToModel(data.data[y].type);
// Do nothing if there are no relations for this model
if (propertiesArray.length === 0) {
return data;
}
// Check if we have a relationships container
if (typeof data.data[y].relationships === 'undefined') {
data.data[y].relationships = {};
}
// Loop through the relationship properties
for (var i=0; i<propertiesArray.length;i++) {
// Make sure we have the property in the payload
if (typeof data.data[y].attributes[propertiesArray[i].property] === 'undefined') {
continue;
}
// Check if we have a contaner for this modelRelationProperty
if (typeof data.data[y].relationships[propertiesArray[i].property] === 'undefined') {
data.data[y].relationships[propertiesArray[i].property] = {};
if (propertiesArray[i].array) {
data.data[y].relationships[propertiesArray[i].property].data = [];
} else {
data.data[y].relationships[propertiesArray[i].property].data = {};
}
}
// Convert the attributes to relationship
if (propertiesArray[i].array) {
// Loop through the relation
for(var x=0;x<data.data[y].attributes[propertiesArray[i].property].length;x++) {
// Add a relationship for each record
data.data[y].relationships[propertiesArray[i].property].data.push({
id: data.data[y].attributes[propertiesArray[i].property][x],
type: propertiesArray[i].model
});
}
} else {
data.data[y].relationships[propertiesArray[i].property].data = {
id: data.data[y].attributes[propertiesArray[i].property],
type: propertiesArray[i].model
};
}
// Delete the attribute-data
delete data.data[y].attributes[propertiesArray[i].property];
}
}
// Return the prepared object with relational data
return data;
},
/**
* [normalizeSingleResponseRelations description]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
normalizeSingleResponseRelations: function (data) {
// Get all properties for this model
var propertiesArray = this.mapPropertiesToModel(data.data.type);
// Do nothing if there are no relations for this model
if (propertiesArray.length === 0) {
return data;
}
// Check if we have a relationships container
if (typeof data.data.relationships === 'undefined') {
data.data.relationships = {};
}
// Loop through the relationship properties
for (var i=0; i<propertiesArray.length;i++) {
// Make sure we have the property in the payload
if (typeof data.data.attributes[propertiesArray[i].property] === 'undefined') {
continue;
}
// Check if we have a contaner for this modelRelationProperty
if (typeof data.data.relationships[propertiesArray[i].property] === 'undefined') {
data.data.relationships[propertiesArray[i].property] = {};
if (propertiesArray[i].array) {
data.data.relationships[propertiesArray[i].property].data = [];
} else {
data.data.relationships[propertiesArray[i].property].data = {};
}
}
// Convert the attributes to relationship
if (propertiesArray[i].array) {
// Loop through the relation
for(var x=0;x<data.data.attributes[propertiesArray[i].property].length;x++) {
// Add a relationship for each record
data.data.relationships[propertiesArray[i].property].data.push({
id: data.data.attributes[propertiesArray[i].property][x],
type: propertiesArray[i].model
});
}
} else {
data.data.relationships[propertiesArray[i].property].data = {
id: data.data.attributes[propertiesArray[i].property],
type: propertiesArray[i].model
};
}
// Delete the attribute-data
delete data.data.attributes[propertiesArray[i].property];
}
// Return the prepared object with relational data
return data;
},
/**
* mapPropertiesToModel
* Will return model type for a property
* @param {[type]} model [description]
* @return {[type]} [description]
*/
mapPropertiesToModel: function(model) {
/**
* ******************************************
* NOTE:
* This is the unfortunate part, where we need
* to re-map all the model-relations.
* We do this and we describe the property they
* are extracted from, which model they are from
* and if it's a belongsTo/hasMany (array:true/false)
*
* I am looking for a better way to fix this
* by letting ember tell me what the relation is...
* Any feedback would be greatly appriciated!
* ******************************************
*/
// Create map
var map = {
category: [
{
property: 'items', // This is the item property in my category model
model: 'item', // It belongs to the item model
array: true // And they are "many"
}
]
};
// Check that it exists, else return empty array
if (typeof map[model] === 'undefined') {
return [];
}
// Return
return map[model];
}
@hussfelt
Copy link
Author

@hussfelt
Copy link
Author

Don't use this implementation. It's wrong! No need to do all this :)

Correct implementation here:
https://gist.github.com/hussfelt/9c9002f15cc253278edb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment