forked from shaba/openuds
Refactorized REST api (server) to be much more "coherent". URLs are much more coherent, we have fewer handlers to implement on models, fixed to register correctly methods, even when deriving from Handler, etc...
This gives better options to implement handlers based on models also
This commit is contained in:
parent
7ea7086ba9
commit
c98e964bba
@ -18,7 +18,7 @@ encoding//src/uds/REST/methods/providers.py=utf-8
|
|||||||
encoding//src/uds/REST/methods/services.py=utf-8
|
encoding//src/uds/REST/methods/services.py=utf-8
|
||||||
encoding//src/uds/REST/methods/transports.py=utf-8
|
encoding//src/uds/REST/methods/transports.py=utf-8
|
||||||
encoding//src/uds/REST/methods/users_groups.py=utf-8
|
encoding//src/uds/REST/methods/users_groups.py=utf-8
|
||||||
encoding//src/uds/REST/mixins.py=utf-8
|
encoding//src/uds/REST/model.py=utf-8
|
||||||
encoding//src/uds/REST/processors.py=utf-8
|
encoding//src/uds/REST/processors.py=utf-8
|
||||||
encoding//src/uds/__init__.py=utf-8
|
encoding//src/uds/__init__.py=utf-8
|
||||||
encoding//src/uds/admin/urls.py=utf-8
|
encoding//src/uds/admin/urls.py=utf-8
|
||||||
|
@ -153,32 +153,18 @@ class Dispatcher(View):
|
|||||||
except AccessDenied as e:
|
except AccessDenied as e:
|
||||||
return http.HttpResponseForbidden(unicode(e))
|
return http.HttpResponseForbidden(unicode(e))
|
||||||
except NotFound as e:
|
except NotFound as e:
|
||||||
return http.Http404(unicode(e))
|
return http.HttpResponseNotFound(unicode(e))
|
||||||
except HandlerError as e:
|
except HandlerError as e:
|
||||||
return http.HttpResponseBadRequest(unicode(e))
|
return http.HttpResponseBadRequest(unicode(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('Error processing request')
|
logger.exception('Error processing request')
|
||||||
return http.HttpResponseServerError(unicode(e))
|
return http.HttpResponseServerError(unicode(e))
|
||||||
|
|
||||||
# Initializes the dispatchers
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def initialize():
|
def registerSubclasses(classes):
|
||||||
'''
|
for cls in classes:
|
||||||
This imports all packages that are descendant of this package, and, after that,
|
if len(cls.__subclasses__()) == 0: # Only classes that has not been inherited will be registered as Handlers
|
||||||
it register all subclases of Handler. (In fact, it looks for packages inside "methods" package, child of this)
|
logger.debug('Found class {0}'.format(cls))
|
||||||
'''
|
|
||||||
import os.path, pkgutil
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Dinamycally import children of this package. The __init__.py files must register, if needed, inside ServiceProviderFactory
|
|
||||||
package = 'methods'
|
|
||||||
|
|
||||||
pkgpath = os.path.join(os.path.dirname(sys.modules[__name__].__file__), package)
|
|
||||||
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
|
||||||
__import__(__name__ + '.' + package + '.' + name, globals(), locals(), [], -1)
|
|
||||||
|
|
||||||
for cls in Handler.__subclasses__(): # @UndefinedVariable
|
|
||||||
# Skip ClusteredServiceProvider
|
|
||||||
if cls.name is None:
|
if cls.name is None:
|
||||||
name = cls.__name__.lower()
|
name = cls.__name__.lower()
|
||||||
else:
|
else:
|
||||||
@ -194,5 +180,28 @@ class Dispatcher(View):
|
|||||||
service_node[name] = { '' : None }
|
service_node[name] = { '' : None }
|
||||||
|
|
||||||
service_node[name][''] = cls
|
service_node[name][''] = cls
|
||||||
|
else:
|
||||||
|
Dispatcher.registerSubclasses(cls.__subclasses__())
|
||||||
|
|
||||||
|
# Initializes the dispatchers
|
||||||
|
@staticmethod
|
||||||
|
def initialize():
|
||||||
|
'''
|
||||||
|
This imports all packages that are descendant of this package, and, after that,
|
||||||
|
it register all subclases of Handler. (In fact, it looks for packages inside "methods" package, child of this)
|
||||||
|
'''
|
||||||
|
import os.path, pkgutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logger.debug('Loading Handlers')
|
||||||
|
|
||||||
|
# Dinamycally import children of this package. The __init__.py files must register, if needed, inside ServiceProviderFactory
|
||||||
|
package = 'methods'
|
||||||
|
|
||||||
|
pkgpath = os.path.join(os.path.dirname(sys.modules[__name__].__file__), package)
|
||||||
|
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
||||||
|
__import__(__name__ + '.' + package + '.' + name, globals(), locals(), [], -1)
|
||||||
|
|
||||||
|
Dispatcher.registerSubclasses(Handler.__subclasses__()) # @UndefinedVariable
|
||||||
|
|
||||||
Dispatcher.initialize()
|
Dispatcher.initialize()
|
||||||
|
@ -39,7 +39,7 @@ from uds.core import auths
|
|||||||
|
|
||||||
from users_groups import Users, Groups
|
from users_groups import Users, Groups
|
||||||
from uds.REST import Handler, NotFound
|
from uds.REST import Handler, NotFound
|
||||||
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
|
from uds.REST.model import ModelHandler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -47,21 +47,16 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Enclosed methods under /auth path
|
# Enclosed methods under /auth path
|
||||||
|
|
||||||
class Authenticators(ModelHandlerMixin, Handler):
|
class Authenticators(ModelHandler):
|
||||||
model = Authenticator
|
model = Authenticator
|
||||||
detail = { 'users': Users, 'groups':Groups }
|
detail = { 'users': Users, 'groups':Groups }
|
||||||
|
|
||||||
def item_as_dict(self, auth):
|
table_title = _('Current authenticators')
|
||||||
type_ = auth.getType()
|
table_fields = [
|
||||||
return { 'id': auth.id,
|
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
||||||
'name': auth.name,
|
{ 'comments': {'title': _('Comments')}},
|
||||||
'users_count': auth.users.count(),
|
{ 'users_count': {'title': _('Users'), 'type': 'numeric', 'width': '5em'}}
|
||||||
'type': type_.type(),
|
]
|
||||||
'comments': auth.comments,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
|
||||||
path = 'authenticators'
|
|
||||||
|
|
||||||
def enum_types(self):
|
def enum_types(self):
|
||||||
return auths.factory().providers().values()
|
return auths.factory().providers().values()
|
||||||
@ -72,14 +67,12 @@ class Types(ModelTypeHandlerMixin, Handler):
|
|||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
|
def item_as_dict(self, auth):
|
||||||
|
type_ = auth.getType()
|
||||||
|
return { 'id': auth.id,
|
||||||
|
'name': auth.name,
|
||||||
|
'users_count': auth.users.count(),
|
||||||
|
'type': type_.type(),
|
||||||
|
'comments': auth.comments,
|
||||||
|
}
|
||||||
|
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
|
||||||
path = 'authenticators'
|
|
||||||
detail = { 'users': Users, 'groups':Groups }
|
|
||||||
|
|
||||||
title = _('Current authenticators')
|
|
||||||
fields = [
|
|
||||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
|
||||||
{ 'comments': {'title': _('Comments')}},
|
|
||||||
{ 'users_count': {'title': _('Users'), 'type': 'numeric', 'width': '5em'}}
|
|
||||||
]
|
|
||||||
|
@ -35,8 +35,7 @@ from __future__ import unicode_literals
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from uds.models import Network
|
from uds.models import Network
|
||||||
|
|
||||||
from uds.REST import Handler, HandlerError
|
from uds.REST.model import ModelHandler
|
||||||
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -44,28 +43,19 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Enclosed methods under /item path
|
# Enclosed methods under /item path
|
||||||
|
|
||||||
class Networks(ModelHandlerMixin, Handler):
|
class Networks(ModelHandler):
|
||||||
model = Network
|
model = Network
|
||||||
|
|
||||||
|
table_title = _('Current Networks')
|
||||||
|
table_fields = [
|
||||||
|
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-globe text-success' } },
|
||||||
|
{ 'net_string': {'title': _('Networks')}},
|
||||||
|
{ 'networks_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
||||||
|
]
|
||||||
|
|
||||||
def item_as_dict(self, item):
|
def item_as_dict(self, item):
|
||||||
return { 'id': item.id,
|
return { 'id': item.id,
|
||||||
'name': item.name,
|
'name': item.name,
|
||||||
'net_string': item.net_string,
|
'net_string': item.net_string,
|
||||||
'networks_count': item.transports.count(),
|
'networks_count': item.transports.count(),
|
||||||
}
|
}
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
|
||||||
path = 'networks'
|
|
||||||
|
|
||||||
# Fake mathods, to yield self on enum types and get a "fake" type for Network
|
|
||||||
def enum_types(self):
|
|
||||||
return []
|
|
||||||
|
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
|
||||||
path = 'networks'
|
|
||||||
title = _('Current Networks')
|
|
||||||
fields = [
|
|
||||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'icon', 'icon': 'fa fa-globe text-success' } },
|
|
||||||
{ 'net_string': {'title': _('Networks')}},
|
|
||||||
{ 'networks_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
|
||||||
]
|
|
||||||
|
@ -36,8 +36,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from uds.models import OSManager
|
from uds.models import OSManager
|
||||||
from uds.core.osmanagers import factory
|
from uds.core.osmanagers import factory
|
||||||
|
|
||||||
from uds.REST import Handler, HandlerError
|
from uds.REST.model import ModelHandler
|
||||||
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -45,9 +44,16 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Enclosed methods under /osm path
|
# Enclosed methods under /osm path
|
||||||
|
|
||||||
class OsManagers(ModelHandlerMixin, Handler):
|
class OsManagers(ModelHandler):
|
||||||
model = OSManager
|
model = OSManager
|
||||||
|
|
||||||
|
table_title = _('Current OS Managers')
|
||||||
|
table_fields = [
|
||||||
|
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
||||||
|
{ 'comments': {'title': _('Comments')}},
|
||||||
|
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
||||||
|
]
|
||||||
|
|
||||||
def item_as_dict(self, osm):
|
def item_as_dict(self, osm):
|
||||||
type_ = osm.getType()
|
type_ = osm.getType()
|
||||||
return { 'id': osm.id,
|
return { 'id': osm.id,
|
||||||
@ -56,19 +62,3 @@ class OsManagers(ModelHandlerMixin, Handler):
|
|||||||
'type': type_.type(),
|
'type': type_.type(),
|
||||||
'comments': osm.comments,
|
'comments': osm.comments,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
|
||||||
path = 'osmanagers'
|
|
||||||
model = OsManagers
|
|
||||||
|
|
||||||
def enum_types(self):
|
|
||||||
return factory().providers().values()
|
|
||||||
|
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
|
||||||
path = 'osmanagers'
|
|
||||||
title = _('Current OS Managers')
|
|
||||||
fields = [
|
|
||||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
|
||||||
{ 'comments': {'title': _('Comments')}},
|
|
||||||
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
|
||||||
]
|
|
||||||
|
@ -37,19 +37,28 @@ from uds.models import Provider
|
|||||||
from services import Services
|
from services import Services
|
||||||
from uds.core import services
|
from uds.core import services
|
||||||
|
|
||||||
from uds.REST import Handler, NotFound
|
from uds.REST import NotFound
|
||||||
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
|
from uds.REST.model import ModelHandler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Providers(ModelHandlerMixin, Handler):
|
class Providers(ModelHandler):
|
||||||
model = Provider
|
model = Provider
|
||||||
detail = { 'services': Services }
|
detail = { 'services': Services }
|
||||||
save_fields = ['name', 'comments']
|
save_fields = ['name', 'comments']
|
||||||
|
|
||||||
|
table_title = _('Service providers')
|
||||||
|
|
||||||
|
# Table info fields
|
||||||
|
table_fields = [
|
||||||
|
{ 'name': {'title': _('Name'), 'type': 'iconType' } },
|
||||||
|
{ 'comments': {'title': _('Comments')}},
|
||||||
|
{ 'services_count': {'title': _('Services'), 'type': 'numeric', 'width': '5em'}},
|
||||||
|
]
|
||||||
|
|
||||||
def item_as_dict(self, provider):
|
def item_as_dict(self, provider):
|
||||||
type_ = provider.getType()
|
type_ = provider.getType()
|
||||||
|
|
||||||
@ -68,26 +77,15 @@ class Providers(ModelHandlerMixin, Handler):
|
|||||||
'comments': provider.comments,
|
'comments': provider.comments,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
# Types related
|
||||||
path = 'providers'
|
|
||||||
|
|
||||||
def enum_types(self):
|
def enum_types(self):
|
||||||
return services.factory().providers().values()
|
return services.factory().providers().values()
|
||||||
|
|
||||||
|
# Gui related
|
||||||
def getGui(self, type_):
|
def getGui(self, type_):
|
||||||
try:
|
try:
|
||||||
return self.addDefaultFields(services.factory().lookup(type_).guiDescription(), ['name', 'comments'])
|
return self.addDefaultFields(services.factory().lookup(type_).guiDescription(), ['name', 'comments'])
|
||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
|
||||||
path = 'providers'
|
|
||||||
detail = { 'services': Services }
|
|
||||||
title = _('Current service providers')
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
{ 'name': {'title': _('Name'), 'type': 'iconType' } },
|
|
||||||
{ 'comments': {'title': _('Comments')}},
|
|
||||||
{ 'services_count': {'title': _('Services'), 'type': 'numeric', 'width': '5em'}},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
@ -33,9 +33,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from uds.models import Provider
|
|
||||||
|
|
||||||
from uds.REST.mixins import DetailHandler
|
from uds.core.Environment import Environment
|
||||||
|
|
||||||
|
from uds.REST.model import DetailHandler
|
||||||
|
from uds.REST import NotFound
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -44,21 +46,19 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Services(DetailHandler):
|
class Services(DetailHandler):
|
||||||
|
|
||||||
def get(self):
|
def getItems(self, parent, item):
|
||||||
# Extract providerenticator
|
# Extract providerenticator
|
||||||
provider = self._kwargs['parent']
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if len(self._args) == 0:
|
if item is None:
|
||||||
return [{
|
return [{
|
||||||
'id':k.id,
|
'id':k.id,
|
||||||
'name': k.name,
|
'name': k.name,
|
||||||
'comments': k.comments,
|
'comments': k.comments,
|
||||||
'type': k.data_type,
|
'type': k.data_type,
|
||||||
'typeName' : _(k.getType().name())
|
'typeName' : _(k.getType().name())
|
||||||
} for k in provider.services.all() ]
|
} for k in parent.services.all() ]
|
||||||
else:
|
else:
|
||||||
with provider.get(pk=self._args[0]) as k:
|
with parent.get(pk=item) as k:
|
||||||
return {
|
return {
|
||||||
'id':k.id,
|
'id':k.id,
|
||||||
'name': k.name,
|
'name': k.name,
|
||||||
@ -70,15 +70,40 @@ class Services(DetailHandler):
|
|||||||
logger.exception('En services')
|
logger.exception('En services')
|
||||||
return { 'error': 'not found' }
|
return { 'error': 'not found' }
|
||||||
|
|
||||||
def getTitle(self):
|
def getTitle(self, parent):
|
||||||
try:
|
try:
|
||||||
return _('Services of {0}').format(Provider.objects.get(pk=self._kwargs['parent_id']).name)
|
return _('Services of {0}').format(parent.name)
|
||||||
except:
|
except:
|
||||||
return _('Current services')
|
return _('Current services')
|
||||||
|
|
||||||
def getFields(self):
|
def getFields(self, parent):
|
||||||
return [
|
return [
|
||||||
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
|
{ 'name': {'title': _('Service name'), 'visible': True, 'type': 'iconType' } },
|
||||||
{ 'comments': { 'title': _('Comments') } },
|
{ 'comments': { 'title': _('Comments') } },
|
||||||
{ 'type': {'title': _('Type') } }
|
{ 'type': {'title': _('Type') } }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def getTypes(self, parent, forType):
|
||||||
|
logger.debug('getTypes parameters: {0}, {1}'.format(parent, forType))
|
||||||
|
if forType is None:
|
||||||
|
offers = [{
|
||||||
|
'name' : _(t.name()),
|
||||||
|
'type' : t.type(),
|
||||||
|
'description' : _(t.description()),
|
||||||
|
'icon' : t.icon().replace('\n', '') } for t in parent.getType().getServicesTypes()]
|
||||||
|
else:
|
||||||
|
offers = None # Do we really need to get one specific type?
|
||||||
|
for t in parent.getType().getServicesTypes():
|
||||||
|
if forType == t.type():
|
||||||
|
offers = t
|
||||||
|
break
|
||||||
|
if offers is None:
|
||||||
|
raise NotFound('type not found')
|
||||||
|
|
||||||
|
return offers # Default is that details do not have types
|
||||||
|
|
||||||
|
def getGui(self, parent, forType):
|
||||||
|
logger.debug('getGui parameters: {0}, {1}'.format(parent, forType))
|
||||||
|
serviceType = parent.getServiceByType(type)
|
||||||
|
service = serviceType( Environment.getTempEnv(), parent) # Instantiate it so it has the opportunity to alter gui description based on parent
|
||||||
|
return self.addDefaultFields( service.guiDescription(service), ['name', 'comments'])
|
||||||
|
@ -37,7 +37,7 @@ from uds.models import Transport
|
|||||||
from uds.core.transports import factory
|
from uds.core.transports import factory
|
||||||
|
|
||||||
from uds.REST import Handler, NotFound
|
from uds.REST import Handler, NotFound
|
||||||
from uds.REST.mixins import ModelHandlerMixin, ModelTypeHandlerMixin, ModelTableHandlerMixin
|
from uds.REST.model import ModelHandler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -45,25 +45,17 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Enclosed methods under /item path
|
# Enclosed methods under /item path
|
||||||
|
|
||||||
class Transports(ModelHandlerMixin, Handler):
|
class Transports(ModelHandler):
|
||||||
model = Transport
|
model = Transport
|
||||||
save_fields = ['name', 'comments', 'priority', 'nets_positive']
|
save_fields = ['name', 'comments', 'priority', 'nets_positive']
|
||||||
|
|
||||||
def item_as_dict(self, item):
|
table_title = _('Current Transports')
|
||||||
type_ = item.getType()
|
table_fields = [
|
||||||
return { 'id': item.id,
|
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
||||||
'name': item.name,
|
{ 'comments': {'title': _('Comments')}},
|
||||||
'comments': item.comments,
|
{ 'priority': {'title': _('Priority'), 'type': 'numeric', 'width': '6em' }},
|
||||||
'priority': item.priority,
|
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
||||||
'nets_positive': item.nets_positive,
|
]
|
||||||
'networks': [ n.id for n in item.networks.all() ],
|
|
||||||
'deployed_count': item.deployedServices.count(),
|
|
||||||
'type': type_.type(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Types(ModelTypeHandlerMixin, Handler):
|
|
||||||
path = 'transports'
|
|
||||||
|
|
||||||
def enum_types(self):
|
def enum_types(self):
|
||||||
return factory().providers().values()
|
return factory().providers().values()
|
||||||
@ -82,13 +74,14 @@ class Types(ModelTypeHandlerMixin, Handler):
|
|||||||
except:
|
except:
|
||||||
raise NotFound('type not found')
|
raise NotFound('type not found')
|
||||||
|
|
||||||
|
def item_as_dict(self, item):
|
||||||
class TableInfo(ModelTableHandlerMixin, Handler):
|
type_ = item.getType()
|
||||||
path = 'transports'
|
return { 'id': item.id,
|
||||||
title = _('Current Transports')
|
'name': item.name,
|
||||||
fields = [
|
'comments': item.comments,
|
||||||
{ 'name': {'title': _('Name'), 'visible': True, 'type': 'iconType' } },
|
'priority': item.priority,
|
||||||
{ 'comments': {'title': _('Comments')}},
|
'nets_positive': item.nets_positive,
|
||||||
{ 'priority': {'title': _('Priority'), 'type': 'numeric', 'width': '6em' }},
|
'networks': [ n.id for n in item.networks.all() ],
|
||||||
{ 'deployed_count': {'title': _('Used by'), 'type': 'numeric', 'width': '8em'}}
|
'deployed_count': item.deployedServices.count(),
|
||||||
]
|
'type': type_.type(),
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ from uds.core.util.State import State
|
|||||||
from uds.models import Authenticator
|
from uds.models import Authenticator
|
||||||
|
|
||||||
from uds.REST.handlers import HandlerError
|
from uds.REST.handlers import HandlerError
|
||||||
from uds.REST.mixins import DetailHandler
|
from uds.REST.model import DetailHandler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -35,168 +35,20 @@ from __future__ import unicode_literals
|
|||||||
from handlers import NotFound, RequestError
|
from handlers import NotFound, RequestError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
|
from uds.REST.handlers import Handler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Details do not have types at all
|
# a few constants
|
||||||
# so, right now, we only process details petitions for Handling & tables info
|
OVERVIEW = 'overview'
|
||||||
class DetailHandler(object):
|
TYPES = 'types'
|
||||||
def __init__(self, parentHandler, path, *args, **kwargs):
|
TABLEINFO = 'tableinfo'
|
||||||
self._parent = parentHandler
|
GUI = 'gui'
|
||||||
self._path = path
|
|
||||||
self._args = args
|
|
||||||
self._kwargs = kwargs
|
|
||||||
|
|
||||||
# A detail handler must also return title & fields for tables
|
# Base for Gui Related mixins
|
||||||
def getTitle(self):
|
class BaseModelHandler(Handler):
|
||||||
return ''
|
|
||||||
|
|
||||||
def getFields(self):
|
|
||||||
return []
|
|
||||||
|
|
||||||
class ModelHandlerMixin(object):
|
|
||||||
'''
|
|
||||||
Basic Handler for a model
|
|
||||||
Basically we will need same operations for all models, so we can
|
|
||||||
take advantage of this fact to not repeat same code again and again...
|
|
||||||
'''
|
|
||||||
authenticated = True
|
|
||||||
needs_staff = True
|
|
||||||
detail = None # Dictionary containing detail routing
|
|
||||||
model = None
|
|
||||||
save_fields = []
|
|
||||||
|
|
||||||
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 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))
|
|
||||||
|
|
||||||
def processDetail(self):
|
|
||||||
logger.debug('Processing detail')
|
|
||||||
try:
|
|
||||||
item = self.model.objects.filter(pk=self._args[0])[0]
|
|
||||||
detailCls = self.detail[self._args[1]]
|
|
||||||
args = list(self._args[2:])
|
|
||||||
path = self._path + '/'.join(args[:2])
|
|
||||||
detail = detailCls(self, path, parent = item)
|
|
||||||
return getattr(detail, self._operation)()
|
|
||||||
except:
|
|
||||||
raise NotFound('method not found')
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
logger.debug('method GET for {0}, {1}'.format(self.__class__.__name__, self._args))
|
|
||||||
if len(self._args) == 0:
|
|
||||||
result = []
|
|
||||||
for val in self.model.objects.all():
|
|
||||||
res = self.item_as_dict(val)
|
|
||||||
self.__fillIntanceFields(val, res)
|
|
||||||
result.append(res)
|
|
||||||
return result
|
|
||||||
|
|
||||||
if self._args[0] == 'overview':
|
|
||||||
return list(self.getItems())
|
|
||||||
|
|
||||||
# If has detail and is requesting detail
|
|
||||||
if self.detail is not None and len(self._args) > 1:
|
|
||||||
return self.processDetail()
|
|
||||||
|
|
||||||
try:
|
|
||||||
val = self.model.objects.get(pk=self._args[0])
|
|
||||||
res = self.item_as_dict(val)
|
|
||||||
self.__fillIntanceFields(val, res)
|
|
||||||
return res
|
|
||||||
except:
|
|
||||||
raise NotFound('item not found')
|
|
||||||
|
|
||||||
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)))
|
|
||||||
|
|
||||||
try:
|
|
||||||
if len(self._args) == 0: # create new
|
|
||||||
item = self.model.objects.create(**args);
|
|
||||||
elif len(self._args) == 1:
|
|
||||||
# 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:
|
|
||||||
raise Exception() # Incorrect invocation
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
raise NotFound('Element do not exists')
|
|
||||||
except IntegrityError: # Duplicate key probably
|
|
||||||
raise RequestError('Element already exists (duplicate key error)')
|
|
||||||
except Exception as e:
|
|
||||||
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()
|
|
||||||
|
|
||||||
res = self.item_as_dict(item)
|
|
||||||
|
|
||||||
self.__fillIntanceFields(item, res)
|
|
||||||
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
logger.debug('method DELETE for {0}, {1}'.format(self.__class__.__name__, self._args))
|
|
||||||
if len(self._args) != 1:
|
|
||||||
raise RequestError('Delete need one and only one argument')
|
|
||||||
try:
|
|
||||||
item = self.model.objects.get(pk=self._args[0]);
|
|
||||||
item.delete()
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
raise NotFound('Element do not exists')
|
|
||||||
except Exception as e:
|
|
||||||
logger.exception('delete')
|
|
||||||
raise RequestError('incorrect invocation to DELETE')
|
|
||||||
|
|
||||||
return 'deleted'
|
|
||||||
|
|
||||||
class ModelTypeHandlerMixin(object):
|
|
||||||
'''
|
|
||||||
As With models, a lot of UDS model contains info about its class.
|
|
||||||
We take advantage of this for not repeating same code (as with ModelHandlerMixin)
|
|
||||||
'''
|
|
||||||
authenticated = True
|
|
||||||
needs_staff = True
|
|
||||||
|
|
||||||
def enum_types(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def type_as_dict(self, type_):
|
|
||||||
return { 'name' : _(type_.name()),
|
|
||||||
'type' : type_.type(),
|
|
||||||
'description' : _(type_.description()),
|
|
||||||
'icon' : type_.icon().replace('\n', '')
|
|
||||||
}
|
|
||||||
|
|
||||||
def getTypes(self, *args, **kwargs):
|
|
||||||
for type_ in self.enum_types():
|
|
||||||
yield self.type_as_dict(type_)
|
|
||||||
|
|
||||||
def addField(self, gui, field):
|
def addField(self, gui, field):
|
||||||
gui.append({
|
gui.append({
|
||||||
@ -238,69 +90,14 @@ class ModelTypeHandlerMixin(object):
|
|||||||
})
|
})
|
||||||
return gui
|
return gui
|
||||||
|
|
||||||
def get(self):
|
def type_as_dict(self, type_):
|
||||||
logger.debug(self._args)
|
return { 'name' : _(type_.name()),
|
||||||
nArgs = len(self._args)
|
'type' : type_.type(),
|
||||||
if nArgs == 0:
|
'description' : _(type_.description()),
|
||||||
return list(self.getTypes())
|
'icon' : type_.icon().replace('\n', '')
|
||||||
|
}
|
||||||
found = None
|
|
||||||
for v in self.getTypes():
|
|
||||||
if v['type'] == self._args[0]:
|
|
||||||
found = v
|
|
||||||
break
|
|
||||||
|
|
||||||
if found is None:
|
|
||||||
raise NotFound('type not found')
|
|
||||||
|
|
||||||
logger.debug('Found type {0}'.format(v))
|
|
||||||
if nArgs == 1:
|
|
||||||
return found
|
|
||||||
|
|
||||||
if self._args[1] == 'gui':
|
|
||||||
gui = self.getGui(self._args[0])
|
|
||||||
# Add name default description, at top of form
|
|
||||||
|
|
||||||
logger.debug("GUI: {0}".format(gui))
|
|
||||||
return sorted(gui, key=lambda f: f['gui']['order']);
|
|
||||||
|
|
||||||
|
|
||||||
class ModelTableHandlerMixin(object):
|
|
||||||
authenticated = True
|
|
||||||
needs_staff = True
|
|
||||||
detail = None
|
|
||||||
|
|
||||||
# Fields should have id of the field, type and length
|
|
||||||
# All options can be ommited
|
|
||||||
# Sample fields:
|
|
||||||
# fields = [
|
|
||||||
# { 'name': {'title': _('Name')} },
|
|
||||||
# { 'comments': {'title': _('Comments')}},
|
|
||||||
# { 'services_count': {'title': _('Services'), 'type': 'numeric', 'width': '5em'}}
|
|
||||||
#]
|
|
||||||
|
|
||||||
fields = []
|
|
||||||
title = ''
|
|
||||||
|
|
||||||
def processDetail(self):
|
|
||||||
logger.debug('Processing detail for table')
|
|
||||||
try:
|
|
||||||
detailCls = self.detail[self._args[1]]
|
|
||||||
args = list(self._args[2:])
|
|
||||||
path = self._path + '/'.join(args[:2])
|
|
||||||
detail = detailCls(self, path, parent_id = self._args[0])
|
|
||||||
return (detail.getTitle(), detail.getFields())
|
|
||||||
except:
|
|
||||||
return ([], '')
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
if len(self._args) > 1:
|
|
||||||
title, fields = self.processDetail()
|
|
||||||
else:
|
|
||||||
# Convert to unicode fields (ugettext_lazy needs to be rendered before passing it to Json
|
|
||||||
title = self.title
|
|
||||||
fields = self.fields # Always add id column as invisible
|
|
||||||
|
|
||||||
|
def processTableFields(self, title, fields):
|
||||||
processedFields = [{ 'id' : {'visible': False, 'sortable': False, 'searchable': False } }]
|
processedFields = [{ 'id' : {'visible': False, 'sortable': False, 'searchable': False } }]
|
||||||
|
|
||||||
for f in fields:
|
for f in fields:
|
||||||
@ -314,3 +111,265 @@ class ModelTableHandlerMixin(object):
|
|||||||
processedFields.append({k1: dct})
|
processedFields.append({k1: dct})
|
||||||
return { 'title': unicode(title), 'fields': processedFields };
|
return { 'title': unicode(title), 'fields': processedFields };
|
||||||
|
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
[path] --> get Items (all hopefully, this call is delegated to getItems)
|
||||||
|
[path]/overview
|
||||||
|
[path]/ID
|
||||||
|
[path]/gui
|
||||||
|
[path]/TYPE/gui
|
||||||
|
[path]/types
|
||||||
|
[path]/types/TYPE
|
||||||
|
[path]/tableinfo
|
||||||
|
[path].... -->
|
||||||
|
'''
|
||||||
|
def __init__(self, parentHandler, path, *args, **kwargs):
|
||||||
|
self._parent = parentHandler
|
||||||
|
self._path = path
|
||||||
|
self._args = args
|
||||||
|
self._kwargs = kwargs
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
# Process args
|
||||||
|
logger.debug("Detail args: {0}".format(self._args))
|
||||||
|
nArgs = len(self._args)
|
||||||
|
parent = self._kwargs['parent']
|
||||||
|
if nArgs == 0:
|
||||||
|
return self.getItems(parent, None)
|
||||||
|
|
||||||
|
if nArgs == 1:
|
||||||
|
if self._args[0] == OVERVIEW:
|
||||||
|
return self.getItems(parent, None)
|
||||||
|
elif self._args[0] == 'gui':
|
||||||
|
return self.getGui(parent, None)
|
||||||
|
elif self._args[0] == 'types':
|
||||||
|
return self.getTypes(parent, None)
|
||||||
|
elif self._args[0] == 'tableinfo':
|
||||||
|
return self.processTableFields(self.getTitle(parent), self.getFields(parent))
|
||||||
|
|
||||||
|
raise RequestError('Invalid request')
|
||||||
|
|
||||||
|
#return self.getItems(parent, self._args[0])
|
||||||
|
|
||||||
|
if nArgs == 2:
|
||||||
|
if self._args[1] == 'gui':
|
||||||
|
return self.getGui(parent, self._args[0])
|
||||||
|
elif self._args[1] == 'types':
|
||||||
|
return self.getTypes(parent, self._args[1])
|
||||||
|
|
||||||
|
return self.fallbackGet()
|
||||||
|
|
||||||
|
# Invoked if default get can't process request
|
||||||
|
def fallbackGet(self):
|
||||||
|
raise RequestError('Invalid request')
|
||||||
|
|
||||||
|
# Default (as sample) getItems
|
||||||
|
def getItems(self, parent, item):
|
||||||
|
if item is None:
|
||||||
|
return []
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# A detail handler must also return title & fields for tables
|
||||||
|
def getTitle(self, parent):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def getFields(self, parent):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def getGui(self, parent, forType):
|
||||||
|
raise RequestError('Gui not provided')
|
||||||
|
|
||||||
|
def getTypes(self, parent, forType):
|
||||||
|
return [] # Default is that details do not have types
|
||||||
|
|
||||||
|
class ModelHandler(BaseModelHandler):
|
||||||
|
'''
|
||||||
|
Basic Handler for a model
|
||||||
|
Basically we will need same operations for all models, so we can
|
||||||
|
take advantage of this fact to not repeat same code again and again...
|
||||||
|
|
||||||
|
Urls treated are:
|
||||||
|
[path] --> Returns all elements for this path (including INSTANCE variables if it has it). (example: .../providers)
|
||||||
|
[path]/overview --> Returns all elements for this path, not including INSTANCE variables. (example: .../providers/overview)
|
||||||
|
[path]/ID --> Returns an exact element for this path. (example: .../providers/4)
|
||||||
|
[path/ID/DETAIL --> Delegates to Detail, if it has details. (example: .../providers/4/services/overview, .../providers/5/services/9/gui, ....
|
||||||
|
|
||||||
|
Note: Instance variables are the variables declared and serialized by modules.
|
||||||
|
The only detail that has types within is "Service", child of "Provider"
|
||||||
|
'''
|
||||||
|
# Authentication related
|
||||||
|
authenticated = True
|
||||||
|
needs_staff = True
|
||||||
|
# Which model does this manage
|
||||||
|
model = None
|
||||||
|
# If this model has details, which ones
|
||||||
|
detail = None # Dictionary containing detail routing
|
||||||
|
# Put needed fields
|
||||||
|
save_fields = []
|
||||||
|
# Table info needed fields and title
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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 []
|
||||||
|
|
||||||
|
def getTypes(self, *args, **kwargs):
|
||||||
|
for type_ in self.enum_types():
|
||||||
|
yield self.type_as_dict(type_)
|
||||||
|
|
||||||
|
def getType(self, type_):
|
||||||
|
found = None
|
||||||
|
for v in self.getTypes():
|
||||||
|
if v['type'] == type_:
|
||||||
|
found = v
|
||||||
|
break
|
||||||
|
|
||||||
|
if found is None:
|
||||||
|
raise NotFound('type not found')
|
||||||
|
|
||||||
|
logger.debug('Found type {0}'.format(v))
|
||||||
|
return found
|
||||||
|
|
||||||
|
# gui related
|
||||||
|
def getGui(self, type_):
|
||||||
|
raise RequestError('invalid request')
|
||||||
|
|
||||||
|
# Helper to process detail
|
||||||
|
def processDetail(self):
|
||||||
|
logger.debug('Processing detail {0}'.format(self._path))
|
||||||
|
try:
|
||||||
|
item = self.model.objects.filter(pk=self._args[0])[0]
|
||||||
|
detailCls = self.detail[self._args[1]]
|
||||||
|
args = list(self._args[2:])
|
||||||
|
path = self._path + '/'.join(args[:2])
|
||||||
|
detail = detailCls(self, path, *args, parent = item)
|
||||||
|
return getattr(detail, self._operation)()
|
||||||
|
except:
|
||||||
|
raise NotFound('method not found')
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
logger.debug('method GET for {0}, {1}'.format(self.__class__.__name__, self._args))
|
||||||
|
nArgs = len(self._args)
|
||||||
|
if nArgs == 0:
|
||||||
|
result = []
|
||||||
|
for val in self.model.objects.all():
|
||||||
|
res = self.item_as_dict(val)
|
||||||
|
self.__fillIntanceFields(val, res)
|
||||||
|
result.append(res)
|
||||||
|
return result
|
||||||
|
|
||||||
|
if nArgs == 1:
|
||||||
|
if self._args[0] == OVERVIEW:
|
||||||
|
return list(self.getItems())
|
||||||
|
elif self._args[0] == TYPES:
|
||||||
|
return list(self.getTypes())
|
||||||
|
elif self._args[0] == TABLEINFO:
|
||||||
|
return self.processTableFields(self.table_title, self.table_fields)
|
||||||
|
|
||||||
|
# get item ID
|
||||||
|
try:
|
||||||
|
val = self.model.objects.get(pk=self._args[0])
|
||||||
|
res = self.item_as_dict(val)
|
||||||
|
self.__fillIntanceFields(val, res)
|
||||||
|
return res
|
||||||
|
except:
|
||||||
|
raise NotFound('item not found')
|
||||||
|
|
||||||
|
# nArgs > 1
|
||||||
|
# Request type info
|
||||||
|
if self._args[0] == TYPES:
|
||||||
|
if nArgs != 2:
|
||||||
|
raise RequestError('invalid request')
|
||||||
|
return self.getType(self._args[1])
|
||||||
|
elif self._args[0] == GUI:
|
||||||
|
if nArgs != 2:
|
||||||
|
raise RequestError('invalid request')
|
||||||
|
gui = self.getGui(self._args[1])
|
||||||
|
return sorted(gui, key=lambda f: f['gui']['order'])
|
||||||
|
|
||||||
|
# If has detail and is requesting detail
|
||||||
|
if self.detail is not None:
|
||||||
|
return self.processDetail()
|
||||||
|
|
||||||
|
raise RequestError('invalid request')
|
||||||
|
|
||||||
|
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)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if len(self._args) == 0: # create new
|
||||||
|
item = self.model.objects.create(**args);
|
||||||
|
elif len(self._args) == 1:
|
||||||
|
# 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')
|
||||||
|
except IntegrityError: # Duplicate key probably
|
||||||
|
raise RequestError('Element already exists (duplicate key error)')
|
||||||
|
except Exception as e:
|
||||||
|
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()
|
||||||
|
|
||||||
|
res = self.item_as_dict(item)
|
||||||
|
|
||||||
|
self.__fillIntanceFields(item, res)
|
||||||
|
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
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:
|
||||||
|
raise RequestError('Delete need one and only one argument')
|
||||||
|
try:
|
||||||
|
item = self.model.objects.get(pk=self._args[0]);
|
||||||
|
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'
|
||||||
|
|
@ -172,6 +172,7 @@ function BasicModelRest(path, options) {
|
|||||||
this.putPath = options.putPath || path;
|
this.putPath = options.putPath || path;
|
||||||
this.delPath = options.delPath || path;
|
this.delPath = options.delPath || path;
|
||||||
this.typesPath = options.typesPath || (path + '/types');
|
this.typesPath = options.typesPath || (path + '/types');
|
||||||
|
this.guiPath = options.guiPath || (path + '/gui');
|
||||||
this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo');
|
this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo');
|
||||||
this.cache = api.cache('bmr'+path);
|
this.cache = api.cache('bmr'+path);
|
||||||
}
|
}
|
||||||
@ -303,7 +304,7 @@ BasicModelRest.prototype = {
|
|||||||
types : function(success_fnc, fail_fnc) {
|
types : function(success_fnc, fail_fnc) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return this._requestPath(this.typesPath, {
|
return this._requestPath(this.typesPath, {
|
||||||
cacheKey: 'type',
|
cacheKey: this.typesPath,
|
||||||
success: success_fnc,
|
success: success_fnc,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -313,9 +314,15 @@ BasicModelRest.prototype = {
|
|||||||
// value: value of the field (selected element in choice, text for inputs, etc....)
|
// value: value of the field (selected element in choice, text for inputs, etc....)
|
||||||
// gui: Description of the field (type, value or values, defvalue, ....
|
// gui: Description of the field (type, value or values, defvalue, ....
|
||||||
"use strict";
|
"use strict";
|
||||||
var path = [this.typesPath, typeName, 'gui'].join('/');
|
|
||||||
|
var path;
|
||||||
|
if( typeName !== undefined ) {
|
||||||
|
path = [this.guiPath, typeName].join('/');
|
||||||
|
} else {
|
||||||
|
path = this.guiPath;
|
||||||
|
}
|
||||||
return this._requestPath(path, {
|
return this._requestPath(path, {
|
||||||
cacheKey: typeName + '-gui',
|
cacheKey: path,
|
||||||
success: success_fnc,
|
success: success_fnc,
|
||||||
fail: fail_fnc,
|
fail: fail_fnc,
|
||||||
});
|
});
|
||||||
@ -345,8 +352,9 @@ function DetailModelRestApi(parentApi, parentId, model, options) {
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
this.base = new BasicModelRest(undefined, {
|
this.base = new BasicModelRest(undefined, {
|
||||||
getPath: [parentApi.path, parentId, model].join('/'),
|
getPath: [parentApi.path, parentId, model].join('/'),
|
||||||
typesPath: '.', // We do not has this on details
|
typesPath: [parentApi.path, parentId, model, 'types'].join('/'), // Proably this will return nothing
|
||||||
tableInfoPath: [parentApi.path, 'tableinfo', parentId, model].join('/'),
|
guiPath: [parentApi.path, parentId, model].join('/'), // Proably this will return nothing
|
||||||
|
tableInfoPath: [parentApi.path, parentId, model, 'tableinfo'].join('/'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,6 +388,10 @@ DetailModelRestApi.prototype = {
|
|||||||
return this.base.types(success_fnc, fail_fnc);
|
return this.base.types(success_fnc, fail_fnc);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
gui: function(typeName, success_fnc, fail_fnc) { // Typename can be "undefined" to request MAIN gui (it it exists ofc..)
|
||||||
|
"use strict";
|
||||||
|
return this.base.gui(typeName, success_fnc, fail_fnc);
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ gui.providers.link = function(event) {
|
|||||||
var id = selected[0].id;
|
var id = selected[0].id;
|
||||||
// Options for detail, to initialize types correctly
|
// Options for detail, to initialize types correctly
|
||||||
var detail_options = {
|
var detail_options = {
|
||||||
types: function(success_fnc, fail_fnc) {
|
/* types: function(success_fnc, fail_fnc) {
|
||||||
success_fnc(selected[0].offers);
|
success_fnc(selected[0].offers);
|
||||||
}
|
}*/
|
||||||
};
|
};
|
||||||
// Giving the name compossed with type, will ensure that only styles will be reattached once
|
// Giving the name compossed with type, will ensure that only styles will be reattached once
|
||||||
var services = new GuiElement(api.providers.detail(id, 'services', detail_options), 'services-'+selected[0].type);
|
var services = new GuiElement(api.providers.detail(id, 'services', detail_options), 'services-'+selected[0].type);
|
||||||
|
@ -105,7 +105,7 @@ GuiElement.prototype = {
|
|||||||
// Icon renderer, based on type (created on init methods in styles)
|
// Icon renderer, based on type (created on init methods in styles)
|
||||||
var renderTypeIcon = function(data, type, value){
|
var renderTypeIcon = function(data, type, value){
|
||||||
if( type == 'display' ) {
|
if( type == 'display' ) {
|
||||||
self.types[value.type] = self.types[value.type] || {}
|
self.types[value.type] = self.types[value.type] || {};
|
||||||
var css = self.types[value.type].css || 'fa fa-asterisk';
|
var css = self.types[value.type].css || 'fa fa-asterisk';
|
||||||
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
return '<span class="' + css + '"></span> ' + renderEmptyCell(data);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user