Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2013-07-04 02:46:44 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2013-07-04 02:46:44 (GMT)
commit1c50340a98e5a55efac7c2ccdcd2000293409eec (patch)
tree439e1735297a6c6111d44c3c306e34ffbeedca87
parentfbfebe833b165b57084e9001080b6804dd51f36b (diff)
Process HEAD API requests to return property metadata
-rw-r--r--sugar_network/db/commands.py3
-rw-r--r--sugar_network/db/router.py3
-rw-r--r--sugar_network/db/volume.py26
-rwxr-xr-xtests/units/db/volume.py53
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,