diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2013-07-04 02:46:44 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2013-07-04 02:46:44 (GMT) |
commit | 1c50340a98e5a55efac7c2ccdcd2000293409eec (patch) | |
tree | 439e1735297a6c6111d44c3c306e34ffbeedca87 | |
parent | fbfebe833b165b57084e9001080b6804dd51f36b (diff) |
Process HEAD API requests to return property metadata
-rw-r--r-- | sugar_network/db/commands.py | 3 | ||||
-rw-r--r-- | sugar_network/db/router.py | 3 | ||||
-rw-r--r-- | sugar_network/db/volume.py | 26 | ||||
-rwxr-xr-x | tests/units/db/volume.py | 53 |
4 files changed, 83 insertions, 2 deletions
diff --git a/sugar_network/db/commands.py b/sugar_network/db/commands.py index 07a733b..5cee8c3 100644 --- a/sugar_network/db/commands.py +++ b/sugar_network/db/commands.py @@ -203,6 +203,9 @@ class Response(dict): args = ['%s=%r' % i for i in self.items()] return '<Response %s>' % ' '.join(args) + def __contains__(self, key): + dict.__contains__(self, key.lower()) + def __getitem__(self, key): return self.get(key.lower()) diff --git a/sugar_network/db/router.py b/sugar_network/db/router.py index e3e56eb..19eab12 100644 --- a/sugar_network/db/router.py +++ b/sugar_network/db/router.py @@ -184,7 +184,8 @@ class Router(object): elif not result_streamed: if response.content_type == 'application/json': result = json.dumps(result) - response.content_length = len(result) if result else 0 + if 'content-length' not in response: + response.content_length = len(result) if result else 0 _logger.trace('Called %s: response=%r result=%r streamed=%r', request_repr, response, result, result_streamed) diff --git a/sugar_network/db/volume.py b/sugar_network/db/volume.py index 1408a83..7b28b75 100644 --- a/sugar_network/db/volume.py +++ b/sugar_network/db/volume.py @@ -17,6 +17,7 @@ import os import re import sys import time +import json import hashlib import logging from contextlib import contextmanager @@ -201,7 +202,7 @@ class VolumeCommands(CommandsProcessor): return self._get_props(doc, request, reply) @property_command(method='GET', mime_type='application/json') - def get_prop(self, document, guid, prop, request, response, part=None): + def get_prop(self, document, guid, prop, request, response): directory = self.volume[document] prop = directory.metadata[prop] doc = directory.get(guid) @@ -221,6 +222,29 @@ class VolumeCommands(CommandsProcessor): http.NotFound, 'BLOB does not exist') return meta + @property_command(method='HEAD') + def get_prop_meta(self, document, guid, prop, request, response): + directory = self.volume[document] + prop = directory.metadata[prop] + doc = directory.get(guid) + doc.request = request + + prop.assert_access(env.ACCESS_READ) + + if isinstance(prop, StoredProperty): + meta = doc.meta(prop.name) + value = meta.pop('value') + response.content_length = len(json.dumps(value)) + else: + meta = prop.on_get(doc, doc.meta(prop.name)) + enforce(meta is not None and ('blob' in meta or 'url' in meta), + http.NotFound, 'BLOB does not exist') + if 'blob' in meta: + meta.pop('blob') + meta['url'] = '/'.join([request.static_prefix] + request.path) + response.content_length = meta['blob_size'] + response['SN-property'] = json.dumps(meta) + def on_create(self, request, props, event): if 'guid' in props: # TODO Temporal security hole, see TODO diff --git a/tests/units/db/volume.py b/tests/units/db/volume.py index 70f3adf..6af78ee 100755 --- a/tests/units/db/volume.py +++ b/tests/units/db/volume.py @@ -4,6 +4,7 @@ import os import sys import time +import json import shutil import hashlib from cStringIO import StringIO @@ -1131,6 +1132,58 @@ class VolumeTest(tests.Test): guid = self.call('POST', document='testdocument', content={'prop2': 'value2'}) self.assertEqual('value2', self.call('GET', document='testdocument', guid=guid, prop='prop1')) + def test_prop_meta(self): + + class TestDocument(db.Document): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.blob_property() + def blob1(self, value): + return value + + @db.blob_property() + def blob2(self, value): + return value + + @blob2.setter + def blob2(self, value): + return {'url': 'http://new', 'foo': 'bar', 'blob_size': 100} + + volume = db.Volume(tests.tmpdir, [TestDocument]) + cp = VolumeCommands(volume) + + request = db.Request(method='POST', document='testdocument') + request.content = {'prop': 'prop', 'blob1': 'blob', 'blob2': ''} + guid = cp.call(request, db.Response()) + + request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='prop') + response = db.Response() + assert cp.call(request, response) is None + meta = volume['testdocument'].get(guid).meta('prop') + meta.pop('value') + self.assertEqual(meta, json.loads(response['SN-property'])) + + request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='blob1') + request.static_prefix = 'http://localhost' + request.path = ['path'] + response = db.Response() + assert cp.call(request, response) is None + meta = volume['testdocument'].get(guid).meta('blob1') + meta.pop('blob') + meta['url'] = 'http://localhost/path' + self.assertEqual(meta, json.loads(response['SN-property'])) + self.assertEqual(len('blob'), response.content_length) + + request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='blob2') + response = db.Response() + assert cp.call(request, response) is None + meta = volume['testdocument'].get(guid).meta('blob2') + self.assertEqual(meta, json.loads(response['SN-property'])) + self.assertEqual(100, response.content_length) + def call(self, method, document=None, guid=None, prop=None, accept_language=None, content=None, content_stream=None, content_type=None, if_modified_since=None, static_prefix=None, |