rudimentary validation in xoslib
[plstackapi.git] / planetstack / core / xoslib / static / js / xoslib / xos-backbone.js
1 if (! window.XOSLIB_LOADED ) {
2     window.XOSLIB_LOADED=true;
3
4     SLIVER_API = "/plstackapi/slivers/";
5     SLICE_API = "/plstackapi/slices/";
6     SLICEROLE_API = "/plstackapi/slice_roles/";
7     NODE_API = "/plstackapi/nodes/";
8     SITE_API = "/plstackapi/sites/";
9     USER_API = "/plstackapi/users/";
10     USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
11     DEPLOYMENT_API = "/plstackapi/deployments/";
12     IMAGE_API = "/plstackapi/images/";
13     NETWORKTEMPLATE_API = "/plstackapi/networktemplates/";
14     NETWORK_API = "/plstackapi/networks/";
15     NETWORKSLIVER_API = "/plstackapi/networkslivers/";
16     SERVICE_API = "/plstackapi/services/";
17     SLICEPRIVILEGE_API = "/plstackapi/slice_privileges/";
18     NETWORKDEPLOYMENT_API = "/plstackapi/networkdeployments/";
19
20     /* changed as a side effect of the big rename
21     SLICEDEPLOYMENT_API = "/plstackapi/slice_deployments/";
22     USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
23     */
24
25     SLICEDEPLOYMENT_API = "/plstackapi/slicedeployments/";
26     USERDEPLOYMENT_API = "/plstackapi/userdeployments/";
27
28     SLICEPLUS_API = "/xoslib/slicesplus/";
29
30     XOSModel = Backbone.Model.extend({
31         /* from backbone-tastypie.js */
32         //idAttribute: 'resource_uri',
33
34         /* from backbone-tastypie.js */
35         url: function() {
36                     var url = this.attributes.resource_uri;
37
38                     if (!url) {
39                         if (this.id) {
40                             url = this.urlRoot + this.id;
41                         } else {
42                             // this happens when creating a new model.
43                             url = this.urlRoot;
44                         }
45                     }
46
47                     if (!url) {
48                         // XXX I'm not sure this does anything useful
49                         url = ( _.isFunction( this.collection.url ) ? this.collection.url() : this.collection.url );
50                         url = url || this.urlRoot;
51                     }
52
53                     // remove any existing query parameters
54                     url && ( url.indexOf("?") > -1 ) && ( url = url.split("?")[0] );
55
56                     url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
57
58                     url && ( url += "?no_hyperlinks=1" );
59
60                     return url;
61             },
62
63             listMethods: function() {
64                 var res = [];\r
65                 for(var m in this) {\r
66                     if(typeof this[m] == "function") {\r
67                         res.push(m)\r
68                     }\r
69                 }\r
70                 return res;\r
71             },
72
73             validate: function(attrs, options) {
74                 errors = {};
75                 _.each(this.validators, function(validatorList, fieldName) {
76                     _.each(validatorList, function(validator) {
77                         if (fieldName in attrs) {
78                             validatorResult = validateField(validator, attrs[fieldName])
79                             if (validatorResult != true) {
80                                 errors[fieldName] = validatorResult;
81                             }
82                         }
83                     });
84                 });
85                 return errors;
86             }
87     });
88
89     XOSCollection = Backbone.Collection.extend({
90         objects: function() {
91                     return this.models.map(function(element) { return element.attributes; });
92                  },
93
94         initialize: function(){
95           this.isLoaded = false;
96           this.failedLoad = false;
97           this.startedLoad = false;
98           this.sortVar = 'name';\r
99           this.sortOrder = 'asc';\r
100           this.on( "sort", this.sorted );\r
101         },\r
102 \r
103         relatedCollections: [],\r
104         foreignCollections: [],\r
105 \r
106         sorted: function() {\r
107             //console.log("sorted " + this.modelName);\r
108         },\r
109 \r
110         simpleComparator: function( model ){\r
111           parts=this.sortVar.split(".");\r
112           result = model.get(parts[0]);\r
113           for (index=1; index<parts.length; ++index) {\r
114               result=result[parts[index]];\r
115           }\r
116           return result;\r
117         },\r
118 \r
119         comparator: function (left, right) {\r
120             var l = this.simpleComparator(left);\r
121             var r = this.simpleComparator(right);\r
122 \r
123             if (l === void 0) return -1;\r
124             if (r === void 0) return 1;\r
125 \r
126             if (this.sortOrder=="desc") {\r
127                 return l < r ? 1 : l > r ? -1 : 0;\r
128             } else {\r
129                 return l < r ? -1 : l > r ? 1 : 0;\r
130             }\r
131         },\r
132 \r
133         fetchSuccess: function(collection, response, options) {\r
134             //console.log("fetch succeeded " + collection.modelName);\r
135             this.failedLoad = false;\r
136             this.fetching = false;\r
137             if (!this.isLoaded) {\r
138                 this.isLoaded = true;\r
139                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
140             }\r
141             this.trigger("fetchStateChange");\r
142             if (options["orig_success"]) {\r
143                 options["orig_success"](collection, response, options);\r
144             }\r
145         },\r
146 \r
147         fetchFailure: function(collection, response, options) {\r
148             //console.log("fetch failed " + collection.modelName);\r
149             this.fetching = false;\r
150             if ((!this.isLoaded) && (!this.failedLoad)) {\r
151                 this.failedLoad=true;\r
152                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
153             }\r
154             this.trigger("fetchStateChange");\r
155             if (options["orig_failure"]) {\r
156                 options["orig_failure"](collection, response, options);\r
157             }\r
158         },\r
159 \r
160         fetch: function(options) {\r
161             var self=this;\r
162             this.fetching=true;\r
163             //console.log("fetch " + this.modelName);\r
164             if (!this.startedLoad) {\r
165                 this.startedLoad=true;\r
166                 Backbone.trigger("xoslib:collectionLoadChange", this);\r
167             }\r
168             this.trigger("fetchStateChange");\r
169             if (options == undefined) {\r
170                 options = {};\r
171             }\r
172             options["orig_success"] = options["success"];\r
173             options["orig_failure"] = options["failure"];\r
174             options["success"] = function(collection, response, options) { self.fetchSuccess.call(self, collection, response, options); };\r
175             options["failure"] = this.fetchFailure;\r
176             Backbone.Collection.prototype.fetch.call(this, options);\r
177         },\r
178 \r
179         startPolling: function() {\r
180             if (!this._polling) {\r
181                 var collection=this;
182                 setInterval(function() { collection.fetch(); }, 10000);
183                 this._polling=true;
184                 this.fetch();
185             }
186         },
187
188         refresh: function(refreshRelated) {
189             if (!this.fetching) {
190                 this.fetch();
191             }
192             if (refreshRelated) {
193                 for (related in this.relatedCollections) {
194                     related = xos[related];
195                     if (!related.fetching) {
196                         related.fetch();
197                     }
198                 }
199             }
200         },
201
202         maybeFetch: function(options){
203                 // Helper function to fetch only if this collection has not been fetched before.
204             if(this._fetched){
205                     // If this has already been fetched, call the success, if it exists
206                 options.success && options.success();
207                 console.log("alreadyFetched");
208                 return;
209             }
210
211                 // when the original success function completes mark this collection as fetched
212             var self = this,
213             successWrapper = function(success){
214                 return function(){
215                     self._fetched = true;
216                     success && success.apply(this, arguments);
217                 };
218             };
219             options.success = successWrapper(options.success);
220             console.log("call fetch");
221             this.fetch(options);
222         },
223
224         getOrFetch: function(id, options){
225                 // Helper function to use this collection as a cache for models on the server
226             var model = this.get(id);
227
228             if(model){
229                 options.success && options.success(model);
230                 return;
231             }
232
233             model = new this.model({
234                 resource_uri: id
235             });
236
237             model.fetch(options);
238         },
239
240         filterBy: function(fieldName, value) {
241              filtered = this.filter(function(obj) {
242                  return obj.get(fieldName) == value;
243                  });
244              return new this.constructor(filtered);
245         },
246
247         /* from backbone-tastypie.js */
248         url: function( models ) {
249                     var url = this.urlRoot || ( models && models.length && models[0].urlRoot );
250                     url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
251
252                     // Build a url to retrieve a set of models. This assume the last part of each model's idAttribute
253                     // (set to 'resource_uri') contains the model's id.
254                     if ( models && models.length ) {
255                             var ids = _.map( models, function( model ) {
256                                             var parts = _.compact( model.id.split('/') );
257                                             return parts[ parts.length - 1 ];
258                                     });
259                             url += 'set/' + ids.join(';') + '/';
260                     }
261
262                     url && ( url += "?no_hyperlinks=1" );
263
264                     return url;
265             },
266
267         listMethods: function() {
268                 var res = [];\r
269                 for(var m in this) {\r
270                     if(typeof this[m] == "function") {\r
271                         res.push(m)\r
272                     }\r
273                 }\r
274                 return res;\r
275             },
276     });
277
278     function define_model(lib, attrs) {
279         modelName = attrs.modelName;
280         modelClassName = modelName;
281         collectionClassName = modelName + "Collection";
282
283         if (!attrs.collectionName) {
284             attrs.collectionName = modelName + "s";
285         }
286         collectionName = attrs.collectionName;
287
288         modelAttrs = {}
289         collectionAttrs = {}
290
291         for (key in attrs) {
292             value = attrs[key];
293             if ($.inArray(key, ["urlRoot", "modelName"])>=0) {
294                 modelAttrs[key] = value;
295             }
296             if ($.inArray(key, ["urlRoot", "modelName", "relatedCollections", "foreignCollections"])>=0) {
297                 collectionAttrs[key] = value;
298             }
299         }
300
301         if (xosdefaults && xosdefaults[modelName]) {
302             modelAttrs["defaults"] = xosdefaults[modelName];
303         }
304
305         if (xosvalidators && xosvalidators[modelName]) {
306             modelAttrs["validators"] = xosvalidators[modelName];
307         }
308
309         lib[modelName] = XOSModel.extend(modelAttrs);
310
311         collectionAttrs["model"] = lib[modelName];
312
313         lib[collectionClassName] = XOSCollection.extend(collectionAttrs);
314         lib[collectionName] = new lib[collectionClassName]();
315
316         lib.allCollectionNames.push(collectionName);
317         lib.allCollections.push(lib[collectionName]);
318     };
319
320     function xoslib() {
321         this.allCollectionNames = [];
322         this.allCollections = [];
323
324         define_model(this, {urlRoot: SLIVER_API,
325                             relatedCollections: {"networkSlivers": "sliver"},
326                             foreignCollections: ["slices", "deployments", "images", "nodes", "users"],
327                             modelName: "sliver"});
328
329         define_model(this, {urlRoot: SLICE_API,
330                            relatedCollections: {"slivers": "slice", "sliceDeployments": "slice", "slicePrivileges": "slice", "networks": "owner"},
331                            foreignCollections: ["services", "sites"],
332                            modelName: "slice"});
333
334         define_model(this, {urlRoot: SLICEDEPLOYMENT_API,
335                            foreignCollections: ["slices", "deployments"],
336                            modelName: "sliceDeployment"});
337
338         define_model(this, {urlRoot: SLICEPRIVILEGE_API,
339                             foreignCollections: ["slices", "users", "sliceRoles"],
340                             modelName: "slicePrivilege"});
341
342         define_model(this, {urlRoot: SLICEROLE_API,
343                             modelName: "sliceRole"});
344
345         define_model(this, {urlRoot: NODE_API,
346                             foreignCollections: ["sites", "deployments"],
347                             modelName: "node"});
348
349         define_model(this, {urlRoot: SITE_API,
350                             relatedCollections: {"users": "site", "slices": "site", "nodes": "site"},
351                             modelName: "site"});
352
353         define_model(this, {urlRoot: USER_API,
354                             relatedCollections: {"slicePrivileges": "user", "slices": "owner", "userDeployments": "user"},
355                             foreignCollections: ["sites"],
356                             modelName: "user"});
357
358         define_model(this, {urlRoot: USERDEPLOYMENT_API,
359                             foreignCollections: ["users","deployments"],
360                             modelName: "userDeployment"});
361
362         define_model(this, { urlRoot: DEPLOYMENT_API,
363                              relatedCollections: {"nodes": "deployment", "slivers": "deploymentNetwork", "networkDeployments": "deployment", "userDeployments": "deployment"},
364                              modelName: "deployment"});
365
366         define_model(this, {urlRoot: IMAGE_API,
367                             model: this.image,
368                             modelName: "image"});
369
370         define_model(this, {urlRoot: NETWORKTEMPLATE_API,
371                             modelName: "networkTemplate"});
372
373         define_model(this, {urlRoot: NETWORK_API,
374                             relatedCollections: {"networkDeployments": "network", "networkSlivers": "network"},
375                             foreignCollections: ["slices", "networkTemplates"],
376                             modelName: "network"});
377
378         define_model(this, {urlRoot: NETWORKSLIVER_API,
379                             modelName: "networkSliver"});
380
381         define_model(this, {urlRoot: NETWORKDEPLOYMENT_API,
382                             modelName: "networkDeployment"});
383
384         define_model(this, {urlRoot: SERVICE_API,
385                             modelName: "service"});
386
387         // enhanced REST
388         define_model(this, {urlRoot: SLICEPLUS_API,
389                             relatedCollections: {'slivers': "slice"},
390                             modelName: "slicePlus",
391                             collectionName: "slicesPlus"});
392
393         this.listObjects = function() { return this.allCollectionNames; };
394
395         this.getCollectionStatus = function() {
396             stats = {isLoaded: 0, failedLoad: 0, startedLoad: 0};
397             for (index in this.allCollections) {
398                 collection = this.allCollections[index];
399                 if (collection.isLoaded) {
400                     stats["isLoaded"] = stats["isLoaded"] + 1;
401                 }
402                 if (collection.failedLoad) {
403                     stats["failedLoad"] = stats["failedLoad"] + 1;
404                 }
405                 if (collection.startedLoad) {
406                     stats["startedLoad"] = stats["startedLoad"] + 1;
407                 }
408             }
409             stats["completedLoad"] = stats["failedLoad"] + stats["isLoaded"];
410             return stats;
411         };
412     };
413
414     xos = new xoslib();
415
416     function getCookie(name) {
417         var cookieValue = null;\r
418         if (document.cookie && document.cookie != '') {\r
419             var cookies = document.cookie.split(';');\r
420             for (var i = 0; i < cookies.length; i++) {\r
421                 var cookie = jQuery.trim(cookies[i]);\r
422                 // Does this cookie string begin with the name we want?\r
423                 if (cookie.substring(0, name.length + 1) == (name + '=')) {\r
424                     cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\r
425                     break;\r
426                 }\r
427             }\r
428         }\r
429         return cookieValue;\r
430     }
431
432     (function() {
433       var _sync = Backbone.sync;\r
434       Backbone.sync = function(method, model, options){\r
435         options.beforeSend = function(xhr){\r
436           var token = getCookie("csrftoken");\r
437           xhr.setRequestHeader('X-CSRFToken', token);\r
438         };\r
439         return _sync(method, model, options);\r
440       };\r
441     })();
442 }