Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b8eb153
add_data() in ObjectsView used to add urls in ProjectsView
will-moore Jan 13, 2017
c179062
Rename pid -> object_id for ObjectView
will-moore Jan 14, 2017
10b3315
ObjectsView.add_data() uses subcleass.url = {} to add urls
will-moore Jan 14, 2017
3832b75
Adding urls to 'extra' in test_screens()
will-moore Jan 15, 2017
9cff702
Work-around to add http://testserver/ to test urls
will-moore Jan 16, 2017
09a2e71
Use request.build_absolute_uri() in test_screens
will-moore Jan 16, 2017
cdb5a5e
Revert "Use request.build_absolute_uri() in test_screens"
will-moore Jan 16, 2017
7785e68
Added build_url() helper to test_api_containers
will-moore Jan 16, 2017
801ad6f
Remove '_url' values from json in test_api_projects * containers
will-moore Jan 16, 2017
37bb038
Merge remote-tracking branch 'origin/develop' into api_urls_in_json
will-moore Jan 17, 2017
e0ff25e
Add 'images_url' to /datasets/ json
will-moore Jan 17, 2017
d986da1
New ApiView base class for ObjectsView and ObjectView
will-moore Jan 17, 2017
3ce2708
Add children_urls to Project, Dataset & Screen json
will-moore Jan 17, 2017
fd287d3
Fix pid -> object_id rename in test_api_projects.py
will-moore Jan 17, 2017
e0f9313
Add new test_pdi_urls
will-moore Jan 17, 2017
871b803
Ignore urls in test_api_images assert_objects()
will-moore Jan 18, 2017
af2614e
Fixes from #5035 for api/views.py
will-moore Jan 18, 2017
64c05a7
Add test_spw_urls()
will-moore Jan 18, 2017
074876d
Use url: prefix for urls in json
will-moore Jan 19, 2017
ddc2f23
Update tests to use 'url:' prefix for urls
will-moore Jan 19, 2017
981db6e
flake8 fix
will-moore Jan 19, 2017
a9b6ca0
Update test_api_login.py to use 'url:' prefix
will-moore Jan 19, 2017
ff95b03
Fix typo webclint
will-moore Jan 19, 2017
a364e39
Fix missing '_url' -> 'url:' keys in test_api_projects.py
will-moore Jan 20, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions components/tools/OmeroWeb/omeroweb/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"""

api_project = url(
r'^v(?P<api_version>%s)/m/projects/(?P<pid>[0-9]+)/$' % versions,
r'^v(?P<api_version>%s)/m/projects/(?P<object_id>[0-9]+)/$' % versions,
views.ProjectView.as_view(),
name='api_project')
"""
Expand All @@ -99,7 +99,7 @@
"""

api_dataset = url(
r'^v(?P<api_version>%s)/m/datasets/(?P<pid>[0-9]+)/$' % versions,
r'^v(?P<api_version>%s)/m/datasets/(?P<object_id>[0-9]+)/$' % versions,
views.DatasetView.as_view(),
name='api_dataset')
"""
Expand All @@ -123,15 +123,15 @@
"""

api_image = url(
r'^v(?P<api_version>%s)/m/images/(?P<pid>[0-9]+)/$' % versions,
r'^v(?P<api_version>%s)/m/images/(?P<object_id>[0-9]+)/$' % versions,
views.ImageView.as_view(),
name='api_image')
"""
Image url to GET or DELETE a single Image
"""

api_screen = url(
r'^v(?P<api_version>%s)/m/screens/(?P<pid>[0-9]+)/$' % versions,
r'^v(?P<api_version>%s)/m/screens/(?P<object_id>[0-9]+)/$' % versions,
views.ScreenView.as_view(),
name='api_screen')
"""
Expand Down Expand Up @@ -162,7 +162,7 @@
"""

api_plate = url(
r'^v(?P<api_version>%s)/m/plates/(?P<pid>[0-9]+)/$' % versions,
r'^v(?P<api_version>%s)/m/plates/(?P<object_id>[0-9]+)/$' % versions,
views.PlateView.as_view(),
name='api_plate')
"""
Expand Down
145 changes: 116 additions & 29 deletions components/tools/OmeroWeb/omeroweb/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def api_versions(request, **kwargs):
for v in settings.API_VERSIONS:
versions.append({
'version': v,
'base_url': build_url(request, 'api_base', v)
'url:base': build_url(request, 'api_base', v)
})
return {'data': versions}

Expand All @@ -76,16 +76,16 @@ def api_versions(request, **kwargs):
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),
'datasets_url': build_url(request, 'api_datasets', v),
'images_url': build_url(request, 'api_images', v),
'screens_url': build_url(request, 'api_screens', v),
'plates_url': build_url(request, 'api_plates', 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}
rv = {'url:projects': build_url(request, 'api_projects', v),
'url:datasets': build_url(request, 'api_datasets', v),
'url:images': build_url(request, 'api_images', v),
'url:screens': build_url(request, 'api_screens', v),
'url:plates': build_url(request, 'api_plates', v),
'url:token': build_url(request, 'api_token', v),
'url:servers': build_url(request, 'api_servers', v),
'url:login': build_url(request, 'api_login', v),
'url:save': build_url(request, 'api_save', v),
'url:schema': OME_SCHEMA_URL}
return rv


Expand All @@ -111,39 +111,71 @@ def api_servers(request, api_version, **kwargs):
return {'data': servers}


class ObjectView(View):
"""Handle access to an individual Object to GET or DELETE it."""
class ApiView(View):
"""Base class extended by ObjectView and ObjectsView."""

# urls extended by subclasses to add urls to marshalled objects
urls = {}

@method_decorator(login_required(useragent='OMERO.webapi'))
@method_decorator(json_response())
def dispatch(self, *args, **kwargs):
"""Wrap other methods to add decorators."""
return super(ObjectView, self).dispatch(*args, **kwargs)
return super(ApiView, self).dispatch(*args, **kwargs)

def add_data(self, marshalled, request, **kwargs):
"""
Post-process marshalled object to add any extra data.

Used to add urls to marshalled json.
Subclasses can configure self.urls to specify urls to add.
See ProjectsView urls as example
"""
object_id = marshalled['@id']
version = kwargs['api_version']
for key, args in self.urls.items():
name = args['name']
kwargs = args['kwargs'].copy()
# If kwargs has 'OBJECT_ID' placeholder, we replace with id
for k, v in kwargs.items():
if v == 'OBJECT_ID':
kwargs[k] = object_id
url = build_url(request, name, version, **kwargs)
marshalled[key] = url
return marshalled


class ObjectView(ApiView):
"""Handle access to an individual Object to GET or DELETE it."""

def get_opts(self, request):
"""Return a dict for use in conn.getObjects() based on request."""
return {}

def get(self, request, pid, conn=None, **kwargs):
def get(self, request, object_id, conn=None, **kwargs):
"""Simply GET a single Object and marshal it or 404 if not found."""
opts = self.get_opts(request)
obj = conn.getObject(self.OMERO_TYPE, pid, opts=opts)
obj = conn.getObject(self.OMERO_TYPE, object_id, opts=opts)
if obj is None:
raise NotFoundError('%s %s not found' % (self.OMERO_TYPE, pid))
raise NotFoundError('%s %s not found' % (self.OMERO_TYPE,
object_id))
encoder = get_encoder(obj._obj.__class__)
return encoder.encode(obj._obj)
marshalled = encoder.encode(obj._obj)
self.add_data(marshalled, request, **kwargs)
return marshalled

def delete(self, request, pid, conn=None, **kwargs):
def delete(self, request, object_id, conn=None, **kwargs):
"""
Delete the Object and return marshal of deleted Object.

Return 404 if not found.
"""
try:
obj = conn.getQueryService().get(self.OMERO_TYPE, long(pid),
obj = conn.getQueryService().get(self.OMERO_TYPE, long(object_id),
conn.SERVICE_OPTS)
except ValidationException:
raise NotFoundError('%s %s not found' % (self.OMERO_TYPE, pid))
raise NotFoundError('%s %s not found' % (self.OMERO_TYPE,
object_id))
encoder = get_encoder(obj.__class__)
json = encoder.encode(obj)
conn.deleteObject(obj)
Expand All @@ -155,12 +187,24 @@ class ProjectView(ObjectView):

OMERO_TYPE = 'Project'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:datasets': {'name': 'api_project_datasets',
'kwargs': {'project_id': 'OBJECT_ID'}},
}


class DatasetView(ObjectView):
"""Handle access to an individual Dataset to GET or DELETE it."""

OMERO_TYPE = 'Dataset'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:images': {'name': 'api_dataset_images',
'kwargs': {'dataset_id': 'OBJECT_ID'}},
}


class ImageView(ObjectView):
"""Handle access to an individual Image to GET or DELETE it."""
Expand All @@ -181,22 +225,22 @@ class ScreenView(ObjectView):

OMERO_TYPE = 'Screen'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:plates': {'name': 'api_screen_plates',
'kwargs': {'screen_id': 'OBJECT_ID'}},
}


class PlateView(ObjectView):
"""Handle access to an individual Plate to GET or DELETE it."""

OMERO_TYPE = 'Plate'


class ObjectsView(View):
class ObjectsView(ApiView):
"""Base class for listing objects."""

@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(ObjectsView, self).dispatch(*args, **kwargs)

def get_opts(self, request, **kwargs):
"""Return an options dict based on request parameters."""
try:
Expand Down Expand Up @@ -224,14 +268,29 @@ def get(self, request, conn=None, **kwargs):
group = getIntOrDefault(request, 'group', -1)
normalize = request.GET.get('normalize', False) == 'true'
# Get the data
return query_objects(conn, self.OMERO_TYPE, group, opts, normalize)
marshalled = query_objects(conn, self.OMERO_TYPE, group,
opts, normalize)
for m in marshalled['data']:
self.add_data(m, request, **kwargs)
return marshalled


class ProjectsView(ObjectsView):
"""Handles GET for /projects/ to list available Projects."""

OMERO_TYPE = 'Project'

# To add a url to marshalled object add to this dict
# 'name' is url name, kwargs are passed to reverse()
# If any kwargs values are 'OBJECT_ID' then this placeholder will be
# filled with the actual project_id
urls = {
'url:datasets': {'name': 'api_project_datasets',
'kwargs': {'project_id': 'OBJECT_ID'}},
'url:project': {'name': 'api_project',
'kwargs': {'object_id': 'OBJECT_ID'}}
}


class DatasetsView(ObjectsView):
"""Handles GET for /datasets/ to list available Datasets."""
Expand All @@ -251,12 +310,28 @@ def get_opts(self, request, **kwargs):
opts['project'] = project
return opts

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:images': {'name': 'api_dataset_images',
'kwargs': {'dataset_id': 'OBJECT_ID'}},
'url:dataset': {'name': 'api_dataset',
'kwargs': {'object_id': 'OBJECT_ID'}},
}


class ScreensView(ObjectsView):
"""Handles GET for /screens/ to list available Screens."""

OMERO_TYPE = 'Screen'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:plates': {'name': 'api_screen_plates',
'kwargs': {'screen_id': 'OBJECT_ID'}},
'url:screen': {'name': 'api_screen',
'kwargs': {'object_id': 'OBJECT_ID'}}
}


class PlatesView(ObjectsView):
"""Handles GET for /plates/ to list available Plates."""
Expand All @@ -276,12 +351,24 @@ def get_opts(self, request, **kwargs):
opts['screen'] = screen
return opts

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:plate': {'name': 'api_plate',
'kwargs': {'object_id': 'OBJECT_ID'}}
}


class ImagesView(ObjectsView):
"""Handles GET for /images/ to list available Images."""

OMERO_TYPE = 'Image'

# Urls to add to marshalled object. See ProjectsView for more details
urls = {
'url:image': {'name': 'api_image',
'kwargs': {'object_id': 'OBJECT_ID'}},
}

def get_opts(self, request, **kwargs):
"""Add filtering by 'dataset' and other params to the opts dict."""
opts = super(ImagesView, self).get_opts(request, **kwargs)
Expand Down
Loading