diff --git a/components/tools/OmeroWeb/omeroweb/api/__init__.py b/components/tools/OmeroWeb/omeroweb/api/__init__.py
new file mode 100644
index 00000000000..e8c86051f20
--- /dev/null
+++ b/components/tools/OmeroWeb/omeroweb/api/__init__.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2016 University of Dundee & Open Microscopy Environment.
+# All rights reserved.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""api module provides JSON OMERO API."""
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/api_exceptions.py b/components/tools/OmeroWeb/omeroweb/api/api_exceptions.py
similarity index 68%
rename from components/tools/OmeroWeb/omeroweb/webgateway/api_exceptions.py
rename to components/tools/OmeroWeb/omeroweb/api/api_exceptions.py
index ad24594249f..bfcfda7380c 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/api_exceptions.py
+++ b/components/tools/OmeroWeb/omeroweb/api/api_exceptions.py
@@ -17,38 +17,51 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+"""Exceptions used by the api/views methods."""
+
class BadRequestError(Exception):
"""
- An exception that will result in a response status of 400
- due to invalid client input
+ An exception that will result in a response status of 400.
+
+ Due to invalid client input
"""
+
status = 400
def __init__(self, message, stacktrace=None):
+ """Override init to handle message and stacktrace."""
super(BadRequestError, self).__init__(message)
self.stacktrace = stacktrace
class NotFoundError(Exception):
"""
- An exception that will result in a response status of 404
- due to objects not being found
+ An exception that will result in a response status of 404.
+
+ Raised due to objects not being found.
"""
+
status = 404
def __init__(self, message, stacktrace=None):
+ """Override init to handle message and stacktrace."""
super(NotFoundError, self).__init__(message)
self.stacktrace = stacktrace
class CreatedObject(Exception):
"""
- An exception that is thrown when new object created
- that returns a status of 201
+ An exception that is thrown when new object created.
+
+ This is not really an error but indicates to the handler
+ that a JsonResponse with status 201 should be returned.
+ The dict content is passed in as 'response'.
"""
+
status = 201
def __init__(self, response):
+ """Override init to include response dict."""
super(CreatedObject, self).__init__(response)
self.response = response
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/api_marshal.py b/components/tools/OmeroWeb/omeroweb/api/api_marshal.py
similarity index 89%
rename from components/tools/OmeroWeb/omeroweb/webgateway/api_marshal.py
rename to components/tools/OmeroWeb/omeroweb/api/api_marshal.py
index bc29769c782..bfd74c57140 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/api_marshal.py
+++ b/components/tools/OmeroWeb/omeroweb/api/api_marshal.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-''' Helper functions for views that handle object trees '''
+"""Helper functions for views that handle object trees."""
from omero_marshal import get_encoder
@@ -25,7 +25,9 @@
def normalize_objects(objects):
"""
- Takes a list of dicts generated from omero_marshal and
+ Normalize the groups and owners from omero_marshal dicts.
+
+ Take a list of dicts generated from omero_marshal and
normalizes the groups and owners into separate
lists. omero:details will only retain group and owner IDs.
"""
@@ -47,12 +49,11 @@ def normalize_objects(objects):
def marshal_objects(objects, extras=None, normalize=False):
"""
- Marshals a list of OMERO.model objects using omero_marshal
+ Marshal a list of OMERO.model objects using omero_marshal.
@param extras: A dict of id:dict to add extra data to each object
@param normalize: If true, normalize groups & owners into separate lists
"""
-
marshalled = []
for o in objects:
encoder = get_encoder(o.__class__)
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/api_query.py b/components/tools/OmeroWeb/omeroweb/api/api_query.py
similarity index 98%
rename from components/tools/OmeroWeb/omeroweb/webgateway/api_query.py
rename to components/tools/OmeroWeb/omeroweb/api/api_query.py
index 4ed4a738131..02290085436 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/api_query.py
+++ b/components/tools/OmeroWeb/omeroweb/api/api_query.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-''' Helper functions for views that handle object trees '''
+"""Helper functions for views that handle object trees."""
import omero
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/decorators.py b/components/tools/OmeroWeb/omeroweb/api/decorators.py
similarity index 91%
rename from components/tools/OmeroWeb/omeroweb/webgateway/decorators.py
rename to components/tools/OmeroWeb/omeroweb/api/decorators.py
index c3e14d6fb26..59718240112 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/decorators.py
+++ b/components/tools/OmeroWeb/omeroweb/api/decorators.py
@@ -34,14 +34,10 @@
class login_required(omeroweb.decorators.login_required):
- """
- webgateway specific extension of the OMERO.web login_required() decorator.
- """
+ """webgateway specific extension of the login_required() decorator."""
def on_not_logged_in(self, request, url, error=None):
- """
- Used for json api methods
- """
+ """Used for json api methods."""
return JsonResponse({'message': 'Not logged in'},
status=403)
@@ -49,13 +45,14 @@ def on_not_logged_in(self, request, url, error=None):
class json_response(object):
"""
Class-based decorator for wrapping Django views methods.
+
Returns JsonResponse based on dict returned by views methods.
Also handles exceptions from views methods, returning
JsonResponse with appropriate status values.
"""
def __init__(self):
- """Initialises the decorator."""
+ """Initialise the decorator."""
pass
def handle_success(self, rv):
@@ -74,7 +71,6 @@ def handle_error(self, ex, trace):
By default, we format exception or message and return this
as a JsonResponse with an appropriate status code.
"""
-
# Default status is 500 'server error'
# But we try to handle all 'expected' errors appropriately
# TODO: handle omero.ConcurrencyException
@@ -99,9 +95,9 @@ def handle_error(self, ex, trace):
rsp_json = ex.response
return JsonResponse(rsp_json, status=status)
- def __call__(ctx, f):
+ def __call__(self, f):
"""
- Returns the decorator.
+ Return the decorator.
The decorator calls the wrapped function and
handles success or exception, returning a
@@ -111,8 +107,8 @@ def wrapped(request, *args, **kwargs):
logger.debug('json_response')
try:
rv = f(request, *args, **kwargs)
- return ctx.handle_success(rv)
+ return self.handle_success(rv)
except Exception, ex:
trace = traceback.format_exc()
- return ctx.handle_error(ex, trace)
+ return self.handle_error(ex, trace)
return update_wrapper(wrapped, f)
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/urls_api.py b/components/tools/OmeroWeb/omeroweb/api/urls.py
similarity index 96%
rename from components/tools/OmeroWeb/omeroweb/webgateway/urls_api.py
rename to components/tools/OmeroWeb/omeroweb/api/urls.py
index 6d8c5753f1e..86b3c634078 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/urls_api.py
+++ b/components/tools/OmeroWeb/omeroweb/api/urls.py
@@ -17,10 +17,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-# Handles all 'api' urls without including '/webgateway/' in the url.
+"""Handles all 'api' urls."""
from django.conf.urls import url, patterns
-from omeroweb.webgateway import views
+from omeroweb.api import views
from omeroweb.webgateway.views import LoginView
from django.conf import settings
import re
diff --git a/components/tools/OmeroWeb/omeroweb/api/views.py b/components/tools/OmeroWeb/omeroweb/api/views.py
new file mode 100644
index 00000000000..14a3c63fef1
--- /dev/null
+++ b/components/tools/OmeroWeb/omeroweb/api/views.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2016 University of Dundee & Open Microscopy Environment.
+# All rights reserved.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Views.py for the OMERO JSON api app."""
+
+from django.views.generic import View
+from django.middleware import csrf
+from django.utils.decorators import method_decorator
+from django.core.urlresolvers import reverse
+from django.conf import settings
+
+import traceback
+import json
+
+from api_query import query_projects
+from omero_marshal import get_encoder, get_decoder, OME_SCHEMA_URL
+from omero import ValidationException
+from omeroweb.connector import Server
+from omeroweb.api.api_exceptions import BadRequestError, NotFoundError, \
+ CreatedObject
+from omeroweb.api.decorators import login_required, json_response
+from omeroweb.webgateway.util import getIntOrDefault
+
+
+def build_url(request, name, api_version, **kwargs):
+ """
+ Helper for generating urls within /api json responses.
+
+ By default we use request.build_absolute_uri() but this
+ can be configured by setting "omero.web.api.absolute_url"
+ to a string or empty string, used to prefix relative urls.
+ Extra **kwargs are passed to reverse() function.
+
+ @param name: Name of the url
+ @param api_version Version string
+ """
+ kwargs['api_version'] = api_version
+ url = reverse(name, kwargs=kwargs)
+ if settings.API_ABSOLUTE_URL is None:
+ return request.build_absolute_uri(url)
+ else:
+ # remove trailing slash
+ prefix = settings.API_ABSOLUTE_URL.rstrip('/')
+ return "%s%s" % (prefix, url)
+
+
+@json_response()
+def api_versions(request, **kwargs):
+ """Base url of the webgateway json api."""
+ versions = []
+ for v in settings.API_VERSIONS:
+ versions.append({
+ 'version': v,
+ 'base_url': build_url(request, 'api_base', v)
+ })
+ return {'data': versions}
+
+
+@json_response()
+def api_base(request, api_version=None, **kwargs):
+ """Base url of the webgateway json api for a specified version."""
+ v = api_version
+ rv = {'projects_url': build_url(request, 'api_projects', v),
+ 'token_url': build_url(request, 'api_token', v),
+ 'servers_url': build_url(request, 'api_servers', v),
+ 'login_url': build_url(request, 'api_login', v),
+ 'save_url': build_url(request, 'api_save', v),
+ 'schema_url': OME_SCHEMA_URL}
+ return rv
+
+
+@json_response()
+def api_token(request, api_version, **kwargs):
+ """Provide CSRF token for current session."""
+ token = csrf.get_token(request)
+ return {'data': token}
+
+
+@json_response()
+def api_servers(request, api_version, **kwargs):
+ """List the available servers to connect to."""
+ servers = []
+ for i, obj in enumerate(Server):
+ s = {'id': i + 1,
+ 'host': obj.host,
+ 'port': obj.port
+ }
+ if obj.server is not None:
+ s['server'] = obj.server
+ servers.append(s)
+ return {'data': servers}
+
+
+class ProjectView(View):
+ """Handle access to an individual Project to GET or DELETE it."""
+
+ @method_decorator(login_required(useragent='OMERO.webapi'))
+ @method_decorator(json_response())
+ def dispatch(self, *args, **kwargs):
+ """Wrap other methods to add decorators."""
+ return super(ProjectView, self).dispatch(*args, **kwargs)
+
+ def get(self, request, pid, conn=None, **kwargs):
+ """Simply GET a single Project and marshal it or 404 if not found."""
+ project = conn.getObject("Project", pid)
+ if project is None:
+ raise NotFoundError('Project %s not found' % pid)
+ encoder = get_encoder(project._obj.__class__)
+ return encoder.encode(project._obj)
+
+ def delete(self, request, pid, conn=None, **kwargs):
+ """
+ Delete the Project and return marshal of deleted Project.
+
+ Return 404 if not found.
+ """
+ try:
+ project = conn.getQueryService().get('Project', long(pid))
+ except ValidationException:
+ raise NotFoundError('Project %s not found' % pid)
+ encoder = get_encoder(project.__class__)
+ json = encoder.encode(project)
+ conn.deleteObject(project)
+ return json
+
+
+class ProjectsView(View):
+ """Handles GET for /projects/ to list available Projects."""
+
+ @method_decorator(login_required(useragent='OMERO.webapi'))
+ @method_decorator(json_response())
+ def dispatch(self, *args, **kwargs):
+ """Use dispatch to add decorators to class methods."""
+ return super(ProjectsView, self).dispatch(*args, **kwargs)
+
+ def get(self, request, conn=None, **kwargs):
+ """GET a list of Projects, filtering by various request parameters."""
+ try:
+ page = getIntOrDefault(request, 'page', 1)
+ limit = getIntOrDefault(request, 'limit', settings.PAGE)
+ group = getIntOrDefault(request, 'group', -1)
+ owner = getIntOrDefault(request, 'owner', -1)
+ childCount = request.GET.get('childCount', False) == 'true'
+ normalize = request.GET.get('normalize', False) == 'true'
+ except ValueError as ex:
+ raise BadRequestError(str(ex))
+
+ # Get the projects
+ projects = query_projects(conn,
+ group=group,
+ owner=owner,
+ childCount=childCount,
+ page=page,
+ limit=limit,
+ normalize=normalize)
+
+ return projects
+
+
+class SaveView(View):
+ """
+ This view provides 'Save' functionality for all types of objects.
+
+ POST to create a new Object and PUT to replace existing one.
+ """
+
+ @method_decorator(login_required(useragent='OMERO.webapi'))
+ @method_decorator(json_response())
+ def dispatch(self, *args, **kwargs):
+ """Apply decorators for class methods below."""
+ return super(SaveView, self).dispatch(*args, **kwargs)
+
+ def put(self, request, conn=None, **kwargs):
+ """
+ PUT handles saving of existing objects.
+
+ Therefore '@id' should be set.
+ """
+ object_json = json.loads(request.body)
+ if '@id' not in object_json:
+ raise BadRequestError(
+ "No '@id' attribute. Use POST to create new objects")
+ return self._save_object(request, conn, object_json, **kwargs)
+
+ def post(self, request, conn=None, **kwargs):
+ """
+ POST handles saving of NEW objects.
+
+ Therefore '@id' should not be set.
+ """
+ object_json = json.loads(request.body)
+ if '@id' in object_json:
+ raise BadRequestError(
+ "Object has '@id' attribute. Use PUT to update objects")
+ rsp = self._save_object(request, conn, object_json, **kwargs)
+ # will return 201 ('Created')
+ raise CreatedObject(rsp)
+
+ def _save_object(self, request, conn, object_json, **kwargs):
+ """Here we handle the saving for PUT and POST."""
+ # Try to get group from request, OR from details below...
+ group = getIntOrDefault(request, 'group', None)
+ decoder = None
+ if '@type' not in object_json:
+ raise BadRequestError('Need to specify @type attribute')
+ objType = object_json['@type']
+ decoder = get_decoder(objType)
+ # If we are passed incomplete object, or decoder couldn't be found...
+ if decoder is None:
+ raise BadRequestError('No decoder found for type: %s' % objType)
+
+ # Any marshal errors most likely due to invalid input. status=400
+ try:
+ obj = decoder.decode(object_json)
+ except Exception:
+ msg = 'Error in decode of json data by omero_marshal'
+ raise BadRequestError(msg, traceback.format_exc())
+
+ if group is None:
+ try:
+ # group might be None or unloaded
+ group = obj.getDetails().group.id.val
+ except AttributeError:
+ # Instead of default stack trace, give nicer message:
+ msg = ("Specify Group in omero:details or "
+ "query parameters ?group=:id")
+ raise BadRequestError(msg)
+
+ # If owner was unloaded (E.g. from get() above) or if missing
+ # ome.model.meta.Experimenter.ldap (not supported by omero_marshal)
+ # then saveObject() will give ValidationException.
+ # Therefore we ignore any details for now:
+ obj.unloadDetails()
+
+ conn.SERVICE_OPTS.setOmeroGroup(group)
+ obj = conn.getUpdateService().saveAndReturnObject(obj,
+ conn.SERVICE_OPTS)
+ encoder = get_encoder(obj.__class__)
+ return encoder.encode(obj)
diff --git a/components/tools/OmeroWeb/omeroweb/urls.py b/components/tools/OmeroWeb/omeroweb/urls.py
index 9e0661ef500..8e10516e918 100755
--- a/components/tools/OmeroWeb/omeroweb/urls.py
+++ b/components/tools/OmeroWeb/omeroweb/urls.py
@@ -100,7 +100,7 @@ def redirect_urlpatterns():
(r'^(?i)url/', include('omeroweb.webredirect.urls')),
(r'^(?i)feedback/', include('omeroweb.feedback.urls')),
- (r'^(?i)api/', include('omeroweb.webgateway.urls_api')),
+ (r'^(?i)api/', include('omeroweb.api.urls')),
url(r'^index/$', 'omeroweb.webclient.views.custom_index',
name="webindex_custom"),
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py
index c171ebf8383..59481fd9d76 100755
--- a/components/tools/OmeroWeb/omeroweb/webclient/views.py
+++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py
@@ -94,6 +94,7 @@
ScreenPlateLinkI, AnnotationAnnotationLinkI, TagAnnotationI
from omero import ApiUsageException, ServerError, CmdError
from omero.rtypes import rlong, rlist
+from omeroweb.webgateway.views import LoginView
import tree
@@ -174,7 +175,7 @@ def custom_index(request, conn=None, **kwargs):
# views
-class WebclientLoginView(webgateway_views.LoginView):
+class WebclientLoginView(LoginView):
"""
Webclient Login - Customises the superclass LoginView
for webclient. Also can be used by other Apps to log in to OMERO. Uses
diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/views.py b/components/tools/OmeroWeb/omeroweb/webgateway/views.py
index ca78024f5a1..1a885e624c2 100644
--- a/components/tools/OmeroWeb/omeroweb/webgateway/views.py
+++ b/components/tools/OmeroWeb/omeroweb/webgateway/views.py
@@ -23,25 +23,21 @@
from django.http import HttpResponseRedirect, HttpResponseNotAllowed, Http404
from django.template import loader as template_loader
from django.views.decorators.http import require_POST
-from django.views.generic import View
from django.core.urlresolvers import reverse, NoReverseMatch
from django.conf import settings
from django.template import RequestContext as Context
from django.core.servers.basehttp import FileWrapper
-from django.middleware import csrf
-from django.utils.decorators import method_decorator
from omero.rtypes import rlong, unwrap
from omero.constants.namespaces import NSBULKANNOTATIONS
from omero.util.ROI_utils import pointsStringToXYlist, xyListToBbox
from plategrid import PlateGrid
from omero_version import build_year
from marshal import imageMarshal, shapeMarshal, rgb_int2rgba
-from api_query import query_projects
+from django.contrib.staticfiles.templatetags.staticfiles import static
+from django.views.generic import View
from omeroweb.webadmin.forms import LoginForm
from omeroweb.decorators import get_client_ip
from omeroweb.webadmin.webadmin_utils import upgradeCheck
-from omero_marshal import get_encoder, get_decoder, OME_SCHEMA_URL
-from django.contrib.staticfiles.templatetags.staticfiles import static
try:
from hashlib import md5
@@ -51,9 +47,8 @@
from cStringIO import StringIO
import tempfile
-from omero import ApiUsageException, ValidationException
+from omero import ApiUsageException
from omero.util.decorators import timeit, TimeIt
-from omeroweb.connector import Server
from omeroweb.http import HttpJavascriptResponse, \
HttpJavascriptResponseServerError
@@ -72,12 +67,8 @@
import shutil
from omeroweb.decorators import login_required, ConnCleaningHttpResponse
-from omeroweb.webgateway.decorators import login_required as api_login_required, \
- json_response
from omeroweb.connector import Connector
from omeroweb.webgateway.util import zip_archived_files, getIntOrDefault
-from omeroweb.webgateway.api_exceptions import BadRequestError, NotFoundError,\
- CreatedObject
cache = CacheBase()
logger = logging.getLogger(__name__)
@@ -2604,99 +2595,21 @@ def object_table_query(request, objtype, objid, conn=None, **kwargs):
return tableData
-def build_url(request, name, api_version, **kwargs):
- """
- Helper for generating urls within /api json responses
- By default we use request.build_absolute_uri() but this
- can be configured by setting "omero.web.api.absolute_url"
- to a string or empty string, used to prefix relative urls.
- Extra **kwargs are passed to reverse() function.
-
- @param name: Name of the url
- @param api_version Version string
- """
- kwargs['api_version'] = api_version
- url = reverse(name, kwargs=kwargs)
- if settings.API_ABSOLUTE_URL is None:
- return request.build_absolute_uri(url)
- else:
- # remove trailing slash
- prefix = settings.API_ABSOLUTE_URL.rstrip('/')
- return "%s%s" % (prefix, url)
-
-
-@json_response()
-def api_versions(request, **kwargs):
- """
- Base url of the webgateway json api.
- """
- versions = []
- for v in settings.API_VERSIONS:
- versions.append({
- 'version': v,
- 'base_url': build_url(request, 'api_base', v)
- })
- return {'data': versions}
-
-
-@json_response()
-def api_base(request, api_version=None, **kwargs):
- """
- Base url of the webgateway json api for a specified version.
- """
- v = api_version
- rv = {'projects_url': build_url(request, 'api_projects', v),
- 'token_url': build_url(request, 'api_token', v),
- 'servers_url': build_url(request, 'api_servers', v),
- 'login_url': build_url(request, 'api_login', v),
- 'save_url': build_url(request, 'api_save', v),
- 'schema_url': OME_SCHEMA_URL}
- return rv
-
-
-@json_response()
-def api_token(request, api_version, **kwargs):
- """
- Provides CSRF token for current session
- """
- token = csrf.get_token(request)
- return {'data': token}
-
-
-@json_response()
-def api_servers(request, api_version, **kwargs):
- """
- Lists the available servers to connect to
- """
- servers = []
- for i, obj in enumerate(Server):
- s = {'id': i + 1,
- 'host': obj.host,
- 'port': obj.port
- }
- if obj.server is not None:
- s['server'] = obj.server
- servers.append(s)
- return {'data': servers}
-
-
class LoginView(View):
- """
- Webgateway Login - Subclassed by WebclientLoginView
- """
+ """Webgateway Login - Subclassed by WebclientLoginView."""
form_class = LoginForm
useragent = 'OMERO.webapi'
def get(self, request, api_version=None):
- """ Simply return a message to say GET not supported """
+ """Simply return a message to say GET not supported."""
return JsonResponse({"message":
("POST only with username, password, "
"server and csrftoken")},
status=405)
def handle_logged_in(self, request, conn, connector):
- """ Returns a response for successful login """
+ """Return a response for successful login."""
c = conn.getEventContext()
ctx = {}
for a in ['sessionId', 'sessionUuid', 'userId', 'userName', 'groupId',
@@ -2708,7 +2621,8 @@ def handle_logged_in(self, request, conn, connector):
def handle_not_logged_in(self, request, error=None, form=None):
"""
- Returns a response for failed login.
+ Return a response for failed login.
+
Reason for failure may be due to server 'error' or because
of form validation errors.
@@ -2730,8 +2644,9 @@ def handle_not_logged_in(self, request, error=None, form=None):
def post(self, request, api_version=None):
"""
- Here we handle the main login logic, creating a connection to OMERO
- and storing that on the request.session OR handling login failures
+ Here we handle the main login logic, creating a connection to OMERO.
+
+ and store that on the request.session OR handling login failures
"""
error = None
form = self.form_class(request.POST.copy())
@@ -2781,157 +2696,6 @@ def post(self, request, api_version=None):
return self.handle_not_logged_in(request, error, form)
-class ProjectView(View):
- """
- Handles access to an individual Project to GET or DELETE it
- """
-
- @method_decorator(api_login_required(useragent='OMERO.webapi'))
- @method_decorator(json_response())
- def dispatch(self, *args, **kwargs):
- return super(ProjectView, self).dispatch(*args, **kwargs)
-
- def get(self, request, pid, conn=None, **kwargs):
- """ Simply GET a single Project and marshal it or 404 if not found """
- project = conn.getObject("Project", pid)
- if project is None:
- raise NotFoundError('Project %s not found' % pid)
- encoder = get_encoder(project._obj.__class__)
- return encoder.encode(project._obj)
-
- def delete(self, request, pid, conn=None, **kwargs):
- """
- Deletes the Project and returns marshal of deleted Project or
- returns 404 if not found
- """
- try:
- project = conn.getQueryService().get('Project', long(pid))
- except ValidationException:
- raise NotFoundError('Project %s not found' % pid)
- encoder = get_encoder(project.__class__)
- json = encoder.encode(project)
- conn.deleteObject(project)
- return json
-
-
-class ProjectsView(View):
- """
- Handles GET for /projects/ to list available Projects
- """
-
- @method_decorator(api_login_required(useragent='OMERO.webapi'))
- @method_decorator(json_response())
- def dispatch(self, *args, **kwargs):
- """ Use dispatch to add decorators to class methods """
- return super(ProjectsView, self).dispatch(*args, **kwargs)
-
- def get(self, request, conn=None, **kwargs):
- """
- GET a list of Projects, filtering by various request parameters
- """
- try:
- page = getIntOrDefault(request, 'page', 1)
- limit = getIntOrDefault(request, 'limit', settings.PAGE)
- group = getIntOrDefault(request, 'group', -1)
- owner = getIntOrDefault(request, 'owner', -1)
- childCount = request.GET.get('childCount', False) == 'true'
- normalize = request.GET.get('normalize', False) == 'true'
- except ValueError as ex:
- raise BadRequestError(str(ex))
-
- # Get the projects
- projects = query_projects(conn,
- group=group,
- owner=owner,
- childCount=childCount,
- page=page,
- limit=limit,
- normalize=normalize)
-
- return projects
-
-
-class SaveView(View):
- """
- This view provides 'Save' functionality for all types of objects
- POST to create a new Object and PUT to replace existing one.
- """
-
- @method_decorator(api_login_required(useragent='OMERO.webapi'))
- @method_decorator(json_response())
- def dispatch(self, *args, **kwargs):
- """ Apply decorators for class methods below """
- return super(SaveView, self).dispatch(*args, **kwargs)
-
- def put(self, request, conn=None, **kwargs):
- """
- PUT handles saving of existing objects.
- Therefore '@id' should be set.
- """
- object_json = json.loads(request.body)
- if '@id' not in object_json:
- raise BadRequestError(
- "No '@id' attribute. Use POST to create new objects")
- return self._save_object(request, conn, object_json, **kwargs)
-
- def post(self, request, conn=None, **kwargs):
- """
- POST handles saving of NEW objects.
- Therefore '@id' should not be set.
- """
- object_json = json.loads(request.body)
- if '@id' in object_json:
- raise BadRequestError(
- "Object has '@id' attribute. Use PUT to update objects")
- rsp = self._save_object(request, conn, object_json, **kwargs)
- # will return 201 ('Created')
- raise CreatedObject(rsp)
-
- def _save_object(self, request, conn, object_json, **kwargs):
- """
- Here we handle the saving for PUT and POST
- """
- # Try to get group from request, OR from details below...
- group = getIntOrDefault(request, 'group', None)
- decoder = None
- if '@type' not in object_json:
- raise BadRequestError('Need to specify @type attribute')
- objType = object_json['@type']
- decoder = get_decoder(objType)
- # If we are passed incomplete object, or decoder couldn't be found...
- if decoder is None:
- raise BadRequestError('No decoder found for type: %s' % objType)
-
- # Any marshal errors most likely due to invalid input. status=400
- try:
- obj = decoder.decode(object_json)
- except Exception:
- msg = 'Error in decode of json data by omero_marshal'
- raise BadRequestError(msg, traceback.format_exc())
-
- if group is None:
- try:
- # group might be None or unloaded
- group = obj.getDetails().group.id.val
- except AttributeError:
- # Instead of default stack trace, give nicer message:
- msg = ("Specify Group in omero:details or "
- "query parameters ?group=:id")
- raise BadRequestError(msg)
-
- # If owner was unloaded (E.g. from get() above) or if missing
- # ome.model.meta.Experimenter.ldap (not supported by omero_marshal)
- # then saveObject() will give ValidationException.
- # Therefore we ignore any details for now:
- obj.unloadDetails()
-
- conn.SERVICE_OPTS.setOmeroGroup(group)
- obj = conn.getUpdateService().saveAndReturnObject(obj,
- conn.SERVICE_OPTS)
- encoder = get_encoder(obj.__class__)
- return encoder.encode(obj)
-
-
@login_required()
@jsonp
def get_image_rdefs_json(request, img_id=None, conn=None, **kwargs):