1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-11 05:17:55 +03:00

Finished based model/detail methods

This commit is contained in:
Adolfo Gómez 2013-12-02 03:43:17 +00:00
parent 043b28cfb4
commit 888f125dc6
10 changed files with 300 additions and 110 deletions

View File

@ -37,7 +37,7 @@ from uds.models import Provider
from services import Services
from uds.core import services
from uds.REST import NotFound
from uds.REST import NotFound, RequestError
from uds.REST.model import ModelHandler
import logging
@ -77,6 +77,10 @@ class Providers(ModelHandler):
'comments': provider.comments,
}
def checkDelete(self, item):
if item.services.count() > 0:
raise RequestError('Can\'t delete providers with services already associated')
# Types related
def enum_types(self):
return services.factory().providers().values()

View File

@ -37,7 +37,8 @@ from django.utils.translation import ugettext as _
from uds.core.Environment import Environment
from uds.REST.model import DetailHandler
from uds.REST import NotFound, ResponseError
from uds.REST import NotFound, ResponseError, RequestError
from django.db import IntegrityError
import logging
@ -59,19 +60,51 @@ class Services(DetailHandler):
'deployed_services_count' : k.deployedServices.count(),
} for k in parent.services.all() ]
else:
with parent.get(pk=item) as k:
return {
'id':k.id,
'name': k.name,
'comments': k.comments,
'type': k.data_type,
'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
}
k = parent.services.get(pk=item)
return {
'id':k.id,
'name': k.name,
'comments': k.comments,
'type': k.data_type,
'typeName' : _(k.getType().name()),
'deployed_services_count' : k.deployedServices.count(),
}
except:
logger.exception('En services')
return { 'error': 'not found' }
def saveItem(self, parent, item):
# Extract item db fields
# We need this fields for all
fields = self.readFieldsFromParams(['name', 'comments', 'data_type'])
try:
if item is None: # Create new
service = parent.services.create(**fields)
else:
service = parent.services.get(pk=item)
service.__dict__.update(fields)
service.save()
except self.model.DoesNotExist:
raise NotFound('Item not found')
except IntegrityError: # Duplicate key probably
raise RequestError('Element already exists (duplicate key error)')
except Exception:
raise RequestError('incorrect invocation to PUT')
return self.getItems(parent, service.id)
def deleteItem(self, parent, item):
try:
service = parent.services.get(pk=item)
if service.deployedServices.count() != 0:
raise RequestError('Item has associated deployed services')
service.delete()
except:
raise NotFound('service not found')
return 'deleted'
def getTitle(self, parent):
try:
return _('Services of {0}').format(parent.name)

View File

@ -111,32 +111,51 @@ class BaseModelHandler(Handler):
processedFields.append({k1: dct})
return { 'title': unicode(title), 'fields': processedFields };
def readFieldsFromParams(self, fldList):
args = {}
try:
for key in fldList:
args[key] = self._params[key]
del self._params[key]
except KeyError as e:
raise RequestError('needed parameter not found in data {0}'.format(unicode(e)))
return args
# Exceptions
def invalidRequestException(self):
raise RequestError('Invalid Request')
# Details do not have types at all
# so, right now, we only process details petitions for Handling & tables info
class DetailHandler(BaseModelHandler):
'''
Detail handler (for relations such as provider-->services, authenticators-->users,groups, deployed services-->cache,assigned, groups, transports
Urls treated are:
Urls recognized for GET are:
[path] --> get Items (all hopefully, this call is delegated to getItems)
[path]/overview
[path]/ID
[path]/gui
[path]/TYPE/gui
[path]/gui/TYPE
[path]/types
[path]/types/TYPE
[path]/tableinfo
[path].... -->
[path]/tableinfo
For PUT:
[path] --> create NEW item
[path]/ID --> Modify existing item
For DELETE:
[path]/ID
'''
def __init__(self, parentHandler, path, *args, **kwargs):
def __init__(self, parentHandler, path, params, *args, **kwargs):
self._parent = parentHandler
self._path = path
self._params = params
self._args = args
self._kwargs = kwargs
def get(self):
# Process args
logger.debug("Detail args: {0}".format(self._args))
logger.debug("Detail args for GET: {0}".format(self._args))
nArgs = len(self._args)
parent = self._kwargs['parent']
if nArgs == 0:
@ -163,15 +182,54 @@ class DetailHandler(BaseModelHandler):
return self.fallbackGet()
def put(self):
'''
Put is delegated to specific implementation
'''
logger.debug("Detail args for PUT: {0}, {1}".format(self._args, self._params))
parent = self._kwargs['parent']
if len(self._args) == 0:
# Create new
item = None
elif len(self._args) == 1:
item = self._args[0]
else:
self.invalidRequestException()
return self.saveItem(parent, item)
def delete(self):
'''
Put is delegated to specific implementation
'''
logger.debug("Detail args for DELETE: {0}".format(self._args))
parent = self._kwargs['parent']
if len(self._args) != 1:
self.invalidRequestException()
return self.deleteItem(parent, self._args[0])
# Invoked if default get can't process request
def fallbackGet(self):
raise RequestError('Invalid request')
raise self.invalidRequestException()
# Default (as sample) getItems
def getItems(self, parent, item):
if item is None:
if item is None: # Returns ALL detail items
return []
return {}
return {} # Returns one item
# Default save
def saveItem(self, parent, item):
self.invalidRequestException()
# Default delete
def deleteItem(self, parent, item):
self.invalidRequestException()
# A detail handler must also return title & fields for tables
def getTitle(self, parent):
@ -181,7 +239,7 @@ class DetailHandler(BaseModelHandler):
return []
def getGui(self, parent, forType):
raise RequestError('Gui not provided')
raise RequestError('Gui not provided for this type of object')
def getTypes(self, parent, forType):
return [] # Default is that details do not have types
@ -214,24 +272,12 @@ class ModelHandler(BaseModelHandler):
table_fields = []
table_title = ''
def __fillIntanceFields(self, item, res):
if hasattr(item, 'getInstance'):
for key, value in item.getInstance().valuesDict().iteritems():
value = {"true":True, "false":False}.get(value, value)
logger.debug('{0} = {1}'.format(key, value))
res[key] = value
# This method must be override, depending on what is provided
# Data related
def item_as_dict(self, item):
pass
def getItems(self, *args, **kwargs):
for item in self.model.objects.filter(*args, **kwargs):
try:
yield self.item_as_dict(item)
except:
logger.exception('Exception getting item from {0}'.format(self.model))
# types related
def enum_types(self): # override this
return []
@ -255,7 +301,14 @@ class ModelHandler(BaseModelHandler):
# gui related
def getGui(self, type_):
raise RequestError('invalid request')
self.invalidRequestException()
# Delete related, checks if the item can be deleted
# If it can't be so, raises an exception
def checkDelete(self, item):
pass
# End overridable
# Helper to process detail
def processDetail(self):
@ -265,11 +318,25 @@ class ModelHandler(BaseModelHandler):
detailCls = self.detail[self._args[1]]
args = list(self._args[2:])
path = self._path + '/'.join(args[:2])
detail = detailCls(self, path, *args, parent = item)
detail = detailCls(self, path, self._params, *args, parent = item)
return getattr(detail, self._operation)()
except AttributeError:
raise NotFound('method not found')
def getItems(self, *args, **kwargs):
for item in self.model.objects.filter(*args, **kwargs):
try:
yield self.item_as_dict(item)
except:
logger.exception('Exception getting item from {0}'.format(self.model))
def fillIntanceFields(self, item, res):
if hasattr(item, 'getInstance'):
for key, value in item.getInstance().valuesDict().iteritems():
value = {"true":True, "false":False}.get(value, value)
logger.debug('{0} = {1}'.format(key, value))
res[key] = value
def get(self):
logger.debug('method GET for {0}, {1}'.format(self.__class__.__name__, self._args))
nArgs = len(self._args)
@ -277,7 +344,7 @@ class ModelHandler(BaseModelHandler):
result = []
for val in self.model.objects.all():
res = self.item_as_dict(val)
self.__fillIntanceFields(val, res)
self.fillIntanceFields(val, res)
result.append(res)
return result
@ -293,7 +360,7 @@ class ModelHandler(BaseModelHandler):
try:
val = self.model.objects.get(pk=self._args[0])
res = self.item_as_dict(val)
self.__fillIntanceFields(val, res)
self.fillIntanceFields(val, res)
return res
except:
raise NotFound('item not found')
@ -318,59 +385,56 @@ class ModelHandler(BaseModelHandler):
def put(self):
logger.debug('method PUT for {0}, {1}'.format(self.__class__.__name__, self._args))
args = {}
try:
for key in self.save_fields:
args[key] = self._params[key]
del self._params[key]
except KeyError as e:
raise RequestError('needed parameter not found in data {0}'.format(unicode(e)))
if len(self._args) > 1: # Detail?
return self.processDetail()
try:
# Extract fields
args = self.readFieldsFromParams(self.save_fields)
deleteOnError = False
if len(self._args) == 0: # create new
item = self.model.objects.create(**args);
elif len(self._args) == 1:
item = self.model.objects.create(**args)
deleteOnError = True
else: # Must have 1 arg
# We have to take care with this case, update will efectively update records on db
item = self.model.objects.get(pk=self._args[0]);
item.__dict__.update(args) # Update fields from args
else: # TODO: Maybe a detail put request
raise Exception() # Incorrect invocation
except self.model.DoesNotExist:
raise NotFound('Element do not exists')
raise NotFound('Item not found')
except IntegrityError: # Duplicate key probably
raise RequestError('Element already exists (duplicate key error)')
except Exception as e:
except Exception:
raise RequestError('incorrect invocation to PUT')
# Store associated object if needed
if self._params.has_key('data_type'): # Needs to store instance
item.data_type = self._params['data_type']
item.data = item.getInstance(self._params).serialize()
item.save()
res = self.item_as_dict(item)
self.__fillIntanceFields(item, res)
item.save()
try:
if self._params.has_key('data_type'): # Needs to store instance
item.data_type = self._params['data_type']
item.data = item.getInstance(self._params).serialize()
item.save()
res = self.item_as_dict(item)
self.fillIntanceFields(item, res)
except:
if deleteOnError:
item.delete()
raise
return res
def delete(self):
logger.debug('method DELETE for {0}, {1}'.format(self.__class__.__name__, self._args))
if len(self._args) == 2: # TODO: Detail request
raise Exception()
if len(self._args) > 1:
return self.processDetail()
if len(self._args) != 1:
raise RequestError('Delete need one and only one argument')
try:
item = self.model.objects.get(pk=self._args[0]);
self.checkDelete(item)
item.delete()
except self.model.DoesNotExist:
raise NotFound('Element do not exists')
except Exception:
logger.exception('delete')
raise RequestError('incorrect invocation to DELETE')
return 'deleted'

View File

@ -43,7 +43,7 @@
api.templates.get = function(name, success_fnc) {
var $this = this;
success_fnc = success_fnc || function(){};
api.doLog('Getting tempkate ' + name);
api.doLog('Getting template ' + name);
if (name.indexOf('?') == -1) {
if ($this.cache.get(name) ) {
success_fnc($this.cache.get(name));
@ -64,7 +64,6 @@
$this.cache.put('_' + cachedId, $this.evaluate(data));
$this.cache.put(name, cachedId);
api.doLog('Success getting template "' + name + '".');
api.doLog('Received: ' + data);
success_fnc(cachedId);
},
fail: function( jqXHR, textStatus, errorThrown ) {

View File

@ -359,6 +359,34 @@ DetailModelRestApi.prototype = {
"use strict";
return this.base.get(success_fnc, fail_fnc);
},
put: function(data, options) {
"use strict";
return this.base.put(data, options);
},
create: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
success: success_fnc,
fail: fail_fnc
});
},
save: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
id: data.id,
success: success_fnc,
fail: fail_fnc
});
},
// --------------
// Delete
// --------------
del: function(id, success_fnc, fail_fnc) {
"use strict";
return this.base.del(id, success_fnc, fail_fnc);
},
tableInfo: function(success_fnc, fail_fnc) {
"use strict";
return this.base.tableInfo(success_fnc, fail_fnc);

View File

@ -41,13 +41,13 @@ gui.providers.link = function(event) {
container : 'providers-placeholder',
rowSelect : 'single',
onCheck : function(check, items) { // Check if item can be deleted
if( check == 'delete' ) {
/*if( check == 'delete' ) {
for( var i in items ) {
if( items[i].services_count > 0)
return false;
}
return true;
}
}*/
return true;
},
onRowSelect : function(selected) {
@ -71,7 +71,9 @@ gui.providers.link = function(event) {
return true;
},
buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit : services.typedEdit(gettext('Edit service'), gettext('Error processing service')),
onEdit : gui.methods.typedEdit(services, gettext('Edit service'), gettext('Error processing service')),
onNew : gui.methods.typedNew(services, gettext('New service'), gettext('Error creating service')),
onDelete: gui.methods.del(services, gettext('Delete service'), gettext('Error deleting service')),
scrollToTable : false,
onLoad: function(k) {
api.tools.unblockUI();
@ -80,7 +82,9 @@ gui.providers.link = function(event) {
return false;
},
buttons : [ 'new', 'edit', 'delete', 'xls' ],
onEdit: gui.providers.typedEdit(gettext('Edit provider'), gettext('Error processing provider')),
onNew : gui.methods.typedNew(gui.providers, gettext('New provider'), gettext('Error creating provider')),
onEdit: gui.methods.typedEdit(gui.providers, gettext('Edit provider'), gettext('Error processing provider')),
onDelete: gui.methods.del(gui.providers, gettext('Delete provider'), gettext('Error deleting provider')),
});
});
@ -107,7 +111,7 @@ gui.authenticators.link = function(event) {
gui.authenticators.table({
container : 'auths-placeholder',
rowSelect : 'single',
buttons : [ 'edit', 'delete', 'xls' ],
buttons : [ 'new', 'edit', 'delete', 'xls' ],
onRowSelect : function(selected) {
api.tools.blockUI();
var id = selected[0].id;
@ -135,7 +139,9 @@ gui.authenticators.link = function(event) {
onRefresh : function() {
$('#users-placeholder').empty(); // Remove detail on parent refresh
},
onEdit: gui.authenticators.typedEdit(gettext('Edit authenticator'), gettext('Error processing authenticator')),
onNew : gui.methods.typedNew(gui.authenticators, gettext('New authenticator'), gettext('Error creating authenticator')),
onEdit: gui.methods.typedEdit(gui.authenticators, gettext('Edit authenticator'), gettext('Error processing authenticator')),
onDelete: gui.methods.del(gui.authenticators, gettext('Delete authenticator'), gettext('Error deleting authenticator')),
});
});
@ -241,8 +247,7 @@ gui.connectivity.link = function(event) {
// TODO: Add confirmation to deletion
gui.connectivity.transports.rest.del(value.id, function(){
refreshFnc();
}, gui.failRequestModalFnc(gettext('Error removing transport'))
);
}, gui.failRequestModalFnc(gettext('Error removing transport')) );
},
});
gui.connectivity.networks.table({

View File

@ -352,9 +352,9 @@ GuiElement.prototype = {
headings.push(api.spreadsheet.cell(heading.sTitle, 'String', styles.bold));
});
rows.push(api.spreadsheet.row(headings));
$.each(data, function(index, row) {
$.each(data, function(index1, row) {
var cells = [];
$.each(columns, function(index, col){
$.each(columns, function(index2, col){
if( col.bVisible === false ) {
return;
}
@ -363,7 +363,6 @@ GuiElement.prototype = {
});
rows.push(api.spreadsheet.row(cells));
});
var ctx = {
creation_date: (new Date()).toISOString(),
worksheet: title,
@ -371,6 +370,7 @@ GuiElement.prototype = {
rows_count: rows.length,
rows: rows.join('\n')
};
gui.doLog(ctx);
setTimeout( function() {
saveAs(new Blob([api.templates.evaluate(tmpl, ctx)],
{type: 'application/vnd.ms-excel'} ), title + '.xls');
@ -446,28 +446,4 @@ GuiElement.prototype = {
}); // End Tableinfo data
return '#' + tableId;
},
// "Generic" edit method to set onEdit table
typedEdit: function(modalTitle, modalErrorMsg, guiProcessor, fieldsProcessor) {
"use strict";
var self = this;
return function(value, event, table, refreshFnc) {
self.rest.gui(value.type, function(guiDefinition) {
var tabs = guiProcessor ? guiProcessor(guiDefinition) : guiDefinition;
self.rest.item(value.id, function(item) {
gui.forms.launchModal(modalTitle+' <b>'+value.name+'</b>', tabs, item, function(form_selector, closeFnc) {
var fields = gui.forms.read(form_selector);
fields.data_type = value.type;
fields = fieldsProcessor ? fieldsProcessor(fields) : fields; // Preprocess fields (probably generate tabs...)
self.rest.save(fields, function(data) { // Success on put
closeFnc();
refreshFnc();
}, gui.failRequestModalFnc(modalErrorMsg)); // Fail on put, show modal message
return false;
});
});
}, gui.failRequestModalFnc(modalErrorMsg));
};
},
};

View File

@ -116,8 +116,17 @@
.on('hidden.bs.modal', function () {
$(id).remove();
});
return id;
};
gui.alert = function(message, type) {
api.templates.get('alert', function(tmpl) {
$(api.templates.evaluate(tmpl, {
type: type,
message: message
})).appendTo('#alerts');
});
};
gui.failRequestMessageFnc = function(jqXHR, textStatus, errorThrown) {
api.templates.get('request_failed', function(tmpl) {
@ -209,7 +218,71 @@
gui.setLinksEvents();
gui.dashboard.link();
};
// Generic "methods" for editing, creating, etc...
gui.methods = {};
// "Generic" edit method to set onEdit table
gui.methods.typedEdit = function(parent, modalTitle, modalErrorMsg, guiProcessor, fieldsProcessor) {
var self = parent;
return function(value, event, table, refreshFnc) {
self.rest.gui(value.type, function(guiDefinition) {
var tabs = guiProcessor ? guiProcessor(guiDefinition) : guiDefinition; // Preprocess fields (probably generate tabs...)
self.rest.item(value.id, function(item) {
gui.forms.launchModal(modalTitle+' <b>'+value.name+'</b>', tabs, item, function(form_selector, closeFnc) {
var fields = gui.forms.read(form_selector);
fields.data_type = value.type;
fields = fieldsProcessor ? fieldsProcessor(fields) : fields;
self.rest.save(fields, function(data) { // Success on put
closeFnc();
refreshFnc();
gui.alert(gettext('Edition successfully done'), 'success');
}, gui.failRequestModalFnc(modalErrorMsg)); // Fail on put, show modal message
return false;
});
});
}, gui.failRequestModalFnc(modalErrorMsg));
};
};
// "Generic" new method to set onNew table
gui.methods.typedNew = function(parent, modalTitle, modalErrorMsg, guiProcessor, fieldsProcessor) {
var self = parent;
return function(type, table, refreshFnc) {
self.rest.gui(type, function(guiDefinition) {
var tabs = guiProcessor ? guiProcessor(guiDefinition) : guiDefinition; // Preprocess fields (probably generate tabs...)
gui.forms.launchModal(modalTitle, tabs, undefined, function(form_selector, closeFnc) {
var fields = gui.forms.read(form_selector);
fields.data_type = type;
fields = fieldsProcessor ? fieldsProcessor(fields) : fields; // P
self.rest.create(fields, function(data) { // Success on put
closeFnc();
refreshFnc();
gui.alert(gettext('Creation successfully done'), 'success');
}, gui.failRequestModalFnc(modalErrorMsg) // Fail on put, show modal message
);
});
});
};
};
gui.methods.del = function(parent, modalTitle, modalErrorMsg) {
var self = parent;
return function(value, event, table, refreshFnc) {
var content = gettext('Are you sure do you want to delete ') + '<b>' + value.name + '</b>';
var modalId = gui.launchModal(modalTitle, content, '<button type="button" class="btn btn-danger button-accept">' + gettext('Delete') + '</button>');
$(modalId + ' .button-accept').click(function(){
$(modalId).modal('hide');
self.rest.del(value.id, function(){
refreshFnc();
gui.alert(gettext('Deletion successfully done'), 'success');
}, gui.failRequestModalFnc(modalErrorMsg) );
});
};
};
// Public attributes
gui.debug = true;
}(window.gui = window.gui || {}, jQuery));

View File

@ -35,8 +35,9 @@
<!-- End of menu -->
<!-- Content -->
<div id="page-wrapper">
<div id="alerts">
</div>
<div id="content">
{% block body %}{% endblock %}
</div>
<div class="row">
<div id="minimized" class="col-lg-12">
@ -124,6 +125,7 @@
{% js_template 'table' %}
{% js_template 'modal' %}
{% js_template 'responsive_table' %}
{% js_template 'alert' %}
<!-- fields -->
{% js_template 'fld/checkbox' %}
{% js_template 'fld/choice' %}

View File

@ -0,0 +1,6 @@
{% verbatim %}
<div class="alert alert-dismissable alert-{{ type }}">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ message }}
</div>
{% endverbatim %}