Skip to content

Instantly share code, notes, and snippets.

@nikolajbaer
Created October 7, 2012 19:38
Show Gist options
  • Save nikolajbaer/3849339 to your computer and use it in GitHub Desktop.
Save nikolajbaer/3849339 to your computer and use it in GitHub Desktop.
API adapter for tastypie API
// Integrates to Web Cube Enterprise 4.2.4 API
var API = function(api_base){
var _api_base = api_base;
var _api = this;
/* utility function for ajax json request */
var make_request = function(args,api_resource_path,ajax_callback,method,data){
var parameters = "";
if(method == undefined){ method="GET"; }
for(var k in args){
if(parameters==""){
parameters+="?";
}else{
parameters += "&";
}
parameters += k+"="+encodeURI(args[k]);
}
var settings={
url:api_resource_path + parameters,
success: ajax_callback,
dataType: "json",
contentType: "application/json" ,
type: method,
};
if(data != undefined){ settings.data = data; }
$.ajax(settings);
}
var BaseObject = {
_init: function(model) {
this._model = model;
this._original = {};
if(model._schema == undefined){
// bail if we are a lightweight reference model
// TODO ideal world we figure out what this reference model's schema is, and laod it into the api?
// .. but then we would need to do a lookup for all empty models, and back-init all the ones that we
// are getting before the schema loads. ugh.
return;
}
// TODO turn each reference field into a Query object
for(var f in model._schema.fields){
var field = model._schema.fields[f];
// save original
this._original[f] = this[f];
if(field.type=="related"){
// HACK no way to know if this is a many or a single related resource
if(field.help_text.substr(0,4) == "Many"){
for(var i=0;i<this[f].length;i++){
if(typeof this[f][i] == "string"){ // it is related resource_uri, so make it a Query
console.log(this[f][i]);
this[f][i] = new RelatedModel(this[f][i]);
}else{
//this[f][i] = $.extend(BaseObject,this[f][i]); // init it, but not with RelatedModel?
}// otherwise this was prefetched, so let it be (but maybe we should init it?)
}
//this[f] = Query(this[f]); // would need to have query that takes in an array of resource_uris
}else{
if(typeof this[f] == "string"){ // it is related resource_uri, so make it a Query
this[f] = new RelatedModel(this[f]);
}
}
}
}
},
do_action:function(action,data,callback){
// TODO
if( this._model._schema.actions == undefined ||
this._model_schema.actions[action] == undefined){ throw "No such action available"; }
make_request({},this.resource_uri+action,callback,"POST",JSON.stringify(data));
},
save:function(){
if(this._model == undefined){ throw "Object does not have a _model"; }
// TODO if this model has a _resource_uri, PUT there
// Foreach field, check if it has changed based upon the _original
// - If related field, check if that has changed based upon the _original
// Create PUT and execute
// - Then create PUT and execute for all RelatedModels
// TODO check how Django Admin does this process?
},
delete:function(){
// TODO handle
// NOTE: maybe don't add this if we are read-only? or just throw exception?
}
}
var RelatedModel = function(related_resource_uri){
this.resource_uri = related_resource_uri;
this.get_resource_uri = function(){
return this.resource_uri;
}
this.get = function(callback,options){
var q = new Query(this,callback,options);
q.fetch();
return q;
}
}
var BaseModel = {
get: function(callback,args,options){
// TODO make this chain-able?
default_args = {
limit: 20,
offset: 0
}
default_options = {
page_progress_callback: null // called as each page is loaded with current progress
}
var opt = $.extend(default_options,options);
opt.args = $.extend(default_args,args);
var q = new Query(this,callback,options);
// shoudl this be done automatically?
q.fetch();
return q;
},
get_resource_uri: function(){
return _api_base + this._resource_path;
},
_import_schema: function(){
if(this._schema == undefined){ return; }
// TODO build a BaseObject, and extend it based upon the fields?
//
}
}
var Query = function(model,final_callback,options){
this.model = model;
this.status = "pending";
this.final_callback = final_callback;
this.results = [];
this.total_count = null;
var _query = this; // for closure
// handle missing options, options.args
if(options == undefined){ options = {args:{}}; }
if(options.args == undefined){ options.args = {}; }
this.options = options;
var query_callback = function(data){
console.log("Got ",data," for query ",_query);
// process list or single result
var results = null;
if(data.meta == undefined){
// fix the query count if we just got one object
_query.total_count = 1;
var results = [data];
}else{
results = data.objects;
_query.total_count = data.meta.total_count;
}
console.log(results);
// process the results
for(var i=0;i<results.length;i++){
var obj = $.extend({},BaseObject,results[i]);
obj._init(_query.model);
console.log(obj);
_query.results.push(obj);
}
console.log(_query.results);
// if there are paged results, trigger the next page now
if(data.meta != undefined && data.meta.next != null){
make_request(_query.options.args,data.meta.next,query_callback);
if(typeof _query.options.page_progress_callback === "function"){
// probably some sort of "progress event" would be best
_query.options.page_progress_callback({last_page:data.objects,query:_query});
}
}
// fire the final callback if we are done, and set the status
if(data.meta == undefined || (data.meta != undefined && data.meta.next == null)){
_query.status = "complete";
if(typeof _query.final_callback === 'function'){
_query.final_callback(_query);
}
}
}
this.fetch = function(){
make_request(_query.options.args,_query.model.get_resource_uri(),query_callback);
}
}
/* Import
*
* Imports all the specified resource paths in the style "app/model" (e.g. "blog/blogentry"). Accepts a string or list
* If specified, the callback will be called as each is loaded with the resulting Model object as the result
*
*/
this.import = function(resource_paths,callback){
if(typeof resource_paths == "string"){
resource_paths = [resource_paths];
}
for(var r in resource_paths){
var resource_path=resource_paths[r];
make_request({},_api_base + resource_path,function(obj){
var o=_api;
var res = resource_path.split("/");
for (var p in res){
if(o[res[p]] == undefined){
o[res[p]]={};
}
o=o[res[p]];
}
o._resource_path = resource_path;
if(obj.meta!=undefined){
// if we have a meta object, then we are at an endpoint
// TODO load teh schema.. what do wew want to do then?
//make_request(obj[
// Add Object Methods
$.extend(o,BaseModel);
make_request({},_api_base + resource_path+"/schema",function(obj){
console.log("updating schema for:" + o._resource_path);
console.log(obj);
o._schema = obj;
o._import_schema();
});
}else{
// otherwise treat all items as different endpoints
for(var k in obj){
o[k] = {list_endpoint:obj[k].list_endpoint,schema:obj[k].schema};
// Do we populate this schema?
}
}
if(typeof callback === "function"){
callback(o);
}
});
}
}
}
// debug
var api = new API("/api/v1/");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment