def run(): objects = SecureObjectAPI(session.auth.user) try: searchQueries = objects.search(valuesByQuery.keys()) except UnknownPathError as error: session.log.exception(error) unknownPath = error.paths[0] raise TNonexistentTag(unknownPath.encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] if operation == Operation.CREATE_OBJECT: raise TUnauthorized() else: raise TNonexistentTag(path_) # Run queries. try: with session.timer.track('index-search'): result = blockingCallFromThread(reactor, searchQueries.get) except SearchError as error: session.log.exception(error) raise TParseError(query, error.message) # Build a result set from the searches. values = {} for parsedQuery, objectIDs in result.iteritems(): for objectID in objectIDs: for tagAndValue in valuesByQuery[parsedQuery]: value = guessValue(tagAndValue.value) # FIXME: this code sucks, but I rather not having # to modify guessValue to return a list, as that # would break other code. # Hopefully, we'll be able to remove this pretty # soon. if isinstance(value, list): value = [item.decode('utf-8') for item in value] if objectID not in values: values[objectID] = {} values[objectID][tagAndValue.path] = value # Update values. if values: tagValues = SecureTagValueAPI(session.auth.user) try: result = tagValues.set(values) except UnknownPathError as error: session.log.exception(error) path = error.paths[0] raise TNonexistentTag(path.encode('utf-8')) except MalformedPathError as error: # FIXME: Modify MalformedPathError to have a path field. raise TInvalidPath(str(error).encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] category, action = getCategoryAndAction(operation) raise TPathPermissionDenied(category, action, path_)
def _GET_HEAD_common(self, request, verb): """ The following code is reproduced almost verbatim in deferred_render_DELETE in about.py. So if you change this code, you'll likely need to change that, and vice versa. """ usage = registry.findUsage(httpObjectCategoryName, verb, TagInstanceResource) registry.checkRequest(usage, request) # This will raise TNoInstanceOnObject if there's no instance, # and that will return a 404 (see util.py). tvalue, tagValue = yield self.facadeClient.getTagInstance( self.session, self.path, self.objectId) value = guessValue(tvalue) accept = request.getHeader('accept') or '*/*' if tvalue.valueType == ThriftValueType.BINARY_TYPE: contentType = tvalue.binaryKeyMimeType if mimeparse.best_match([contentType], accept) == '': raise error.NotAcceptable() body = value # Mark this value as unwrappable for JSON request._fluiddb_jsonp_unwrappable = None else: contentType = mimeparse.best_match( contentTypesForPrimitiveValue, accept) if contentType == '': raise error.NotAcceptable() try: serializer = util.primitiveTypeSerializer[contentType] except KeyError: raise TInternalError('No serializer for %r.' % contentType) else: body = serializer(value) typeValue = self._getTypeHeader(tvalue.valueType) request.setHeader(util.buildHeader('Type'), typeValue) request.setHeader('Content-length', str(len(body))) request.setHeader('Content-type', contentType) # setting the Last-Modified header for fluiddb/id makes no sense if tagValue and tagValue.creationTime: request.setHeader( 'Last-modified', tagValue.creationTime.strftime('%a, %d %b %Y %H:%M:%S')) request.setResponseCode(usage.successCode) defer.returnValue(body)
def _GET_HEAD_common(self, request, verb): """ The following code is reproduced almost verbatim in deferred_render_DELETE in about.py. So if you change this code, you'll likely need to change that, and vice versa. """ usage = registry.findUsage(httpObjectCategoryName, verb, TagInstanceResource) registry.checkRequest(usage, request) # This will raise TNoInstanceOnObject if there's no instance, # and that will return a 404 (see util.py). tvalue, tagValue = yield self.facadeClient.getTagInstance( self.session, self.path, self.objectId) value = guessValue(tvalue) accept = request.getHeader('accept') or '*/*' if tvalue.valueType == ThriftValueType.BINARY_TYPE: contentType = tvalue.binaryKeyMimeType if mimeparse.best_match([contentType], accept) == '': raise error.NotAcceptable() body = value # Mark this value as unwrappable for JSON request._fluiddb_jsonp_unwrappable = None else: contentType = mimeparse.best_match(contentTypesForPrimitiveValue, accept) if contentType == '': raise error.NotAcceptable() try: serializer = util.primitiveTypeSerializer[contentType] except KeyError: raise TInternalError('No serializer for %r.' % contentType) else: body = serializer(value) typeValue = self._getTypeHeader(tvalue.valueType) request.setHeader(util.buildHeader('Type'), typeValue) request.setHeader('Content-length', str(len(body))) request.setHeader('Content-type', contentType) # setting the Last-Modified header for fluiddb/id makes no sense if tagValue and tagValue.creationTime: request.setHeader( 'Last-modified', tagValue.creationTime.strftime('%a, %d %b %Y %H:%M:%S')) request.setResponseCode(usage.successCode) defer.returnValue(body)
def setTagInstance(self, session, path, objectId, thriftValue): """Set a L{TagValue} for an object. @param session: The L{FluidinfoSession} for the request. @param path: The L{Tag.path} of the value to set. @param objectId: The object ID to set the value for. @param thriftValue: The value to set. @raise TNonexistentPath: Raised if the L{Tag.path} doesn't exist. @raise TPathPermissionDenied: Raised if the L{User} doesn't have permission to set the value. @return: A C{Deferred} that will fire when the value has been stored. """ path = path.decode('utf-8') objectID = UUID(objectId) if thriftValue.valueType == ThriftValueType.BINARY_TYPE: value = {'mime-type': thriftValue.binaryKeyMimeType, 'contents': thriftValue.binaryKey} else: value = guessValue(thriftValue) if isinstance(value, list): value = [item.decode('utf-8') for item in value] values = {objectID: {path: value}} def run(): try: SecureTagValueAPI(session.auth.user).set(values) except UnknownPathError as error: session.log.exception(error) raise TNonexistentTag(path.encode('utf-8')) except MalformedPathError as error: raise TInvalidPath(path.encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] category, action = getCategoryAndAction(operation) raise TPathPermissionDenied(category, action, path_) return session.transact.run(run)
def testRatingForObjectAboutBarcelona(self): """ Run a series of tests on the ntoll/rating tag on the object about barcelona: - the tag should initially not be present. - we should successfully be able to do a PUT. - we should then be able to do a GET, HEAD, and DELETE in that order. - a final GET should then indicate the tag is gone. """ about = 'barcelona' tag = 'ntoll/rating' rating = 6 objectIdAboutBarcelona = util.generateObjectId() fakeQueryResolver = FakeQueryResolver(about, [objectIdAboutBarcelona], self) fakeTagsClient = FakeTagsClient() facadeClient = FakeFacadeClient(fakeTagsClient=fakeTagsClient, fakeQueryResolver=fakeQueryResolver) session = FakeSession() resource = AboutTagInstanceResource(facadeClient, session, about, tag) # Test GET when there is no instance on the object. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) yield d self.assertEqual(request.status, http.NOT_FOUND) # Test PUT. payload = json.dumps(rating) headers = { 'Content-Length': [str(len(payload))], 'Content-Type': [contentTypeForPrimitiveJSON], } d = defer.Deferred() request = FakeRequest('PUT', d, headers) request.content = StringIO.StringIO(payload) resource.render(request) yield d self.assertEqual(request.status, http.NO_CONTENT) self.assertEqual( guessValue(fakeTagsClient.values[tag][objectIdAboutBarcelona]), rating) # Check that the right path, objectId, and value was passed to the # fake tags client. self.assertEqual(fakeTagsClient.values, { tag: { objectIdAboutBarcelona: createThriftValue(rating) }, }) # Test GET. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.OK) self.assertEqual(request.getResponseHeader('Content-Type'), contentTypeForPrimitiveJSON) receivedRating = json.loads(body) self.assertEqual(receivedRating, rating) # Test HEAD d = defer.Deferred() request = FakeRequest('HEAD', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.OK) self.assertEqual(body, '') # Test DELETE d = defer.Deferred() request = FakeRequest('DELETE', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.NO_CONTENT) # Test GET when, finally, there is no instance on the object. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) yield d self.assertEqual(request.status, http.NOT_FOUND) # Check that the value really was deleted. self.assertEqual(fakeTagsClient.values, {tag: {}})
def testRatingForObjectAboutBarcelona(self): """ Run a series of tests on the ntoll/rating tag on the object about barcelona: - the tag should initially not be present. - we should successfully be able to do a PUT. - we should then be able to do a GET, HEAD, and DELETE in that order. - a final GET should then indicate the tag is gone. """ about = 'barcelona' tag = 'ntoll/rating' rating = 6 objectIdAboutBarcelona = util.generateObjectId() fakeQueryResolver = FakeQueryResolver( about, [objectIdAboutBarcelona], self) fakeTagsClient = FakeTagsClient() facadeClient = FakeFacadeClient(fakeTagsClient=fakeTagsClient, fakeQueryResolver=fakeQueryResolver) session = FakeSession() resource = AboutTagInstanceResource(facadeClient, session, about, tag) # Test GET when there is no instance on the object. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) yield d self.assertEqual(request.status, http.NOT_FOUND) # Test PUT. payload = json.dumps(rating) headers = { 'Content-Length': [str(len(payload))], 'Content-Type': [contentTypeForPrimitiveJSON], } d = defer.Deferred() request = FakeRequest('PUT', d, headers) request.content = StringIO.StringIO(payload) resource.render(request) yield d self.assertEqual(request.status, http.NO_CONTENT) self.assertEqual( guessValue(fakeTagsClient.values[tag][objectIdAboutBarcelona]), rating) # Check that the right path, objectId, and value was passed to the # fake tags client. self.assertEqual(fakeTagsClient.values, { tag: {objectIdAboutBarcelona: createThriftValue(rating)}, }) # Test GET. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.OK) self.assertEqual(request.getResponseHeader('Content-Type'), contentTypeForPrimitiveJSON) receivedRating = json.loads(body) self.assertEqual(receivedRating, rating) # Test HEAD d = defer.Deferred() request = FakeRequest('HEAD', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.OK) self.assertEqual(body, '') # Test DELETE d = defer.Deferred() request = FakeRequest('DELETE', d) request.content = StringIO.StringIO() resource.render(request) body = yield d self.assertEqual(request.status, http.NO_CONTENT) # Test GET when, finally, there is no instance on the object. d = defer.Deferred() request = FakeRequest('GET', d) request.content = StringIO.StringIO() resource.render(request) yield d self.assertEqual(request.status, http.NOT_FOUND) # Check that the value really was deleted. self.assertEqual(fakeTagsClient.values, {tag: {}})
def _GET_HEAD_common(self, request, verb): """ I handle the common actions taken by GET and HEAD requests, which are virtually identical, except HEAD drops the body. This code, apart from the yield self._setObjectId(), is taken verbatim from _GET_HEAD_common in objects.py. So if you change this code, you'll likely need to change that, and vice versa. @param request: The HTTP request. @param verb: A C{str}, either 'GET' or 'HEAD'. @return: A C{Deferred} which fires with the body of a GET request, having set all the response headers that are common to GET and HEAD. (The body will be dropped (below) for HEAD requests.) """ usage = registry.findUsage(httpAboutCategoryName, verb, AboutTagInstanceResource) registry.checkRequest(usage, request) yield self._setObjectId() # This will raise TNoInstanceOnObject if there's no instance, # and that will return a 404 (see util.py). tvalue, tagValue = yield self.facadeClient.getTagInstance( self.session, self.path, self.objectId) value = guessValue(tvalue) accept = request.getHeader('accept') or '*/*' if tvalue.valueType == ThriftValueType.BINARY_TYPE: contentType = tvalue.binaryKeyMimeType if mimeparse.best_match([contentType], accept) == '': raise error.NotAcceptable() body = value # Mark this value as unwrappable for JSON request._fluiddb_jsonp_unwrappable = None else: contentType = mimeparse.best_match(contentTypesForPrimitiveValue, accept) if contentType == '': raise error.NotAcceptable() try: serializer = util.primitiveTypeSerializer[contentType] except KeyError: raise TInternalError('No serializer for %r.' % contentType) else: body = serializer(value) typeValue = self._getTypeHeader(tvalue.valueType) request.setHeader(util.buildHeader('Type'), typeValue) request.setHeader('Content-length', str(len(body))) request.setHeader('Content-type', contentType) # setting the Last-Modified header for fluiddb/id makes no sense if tagValue and tagValue.creationTime: request.setHeader( 'Last-modified', tagValue.creationTime.strftime('%a, %d %b %Y %H:%M:%S')) request.setResponseCode(usage.successCode) defer.returnValue(body)
def _GET_HEAD_common(self, request, verb): """ I handle the common actions taken by GET and HEAD requests, which are virtually identical, except HEAD drops the body. This code, apart from the yield self._setObjectId(), is taken verbatim from _GET_HEAD_common in objects.py. So if you change this code, you'll likely need to change that, and vice versa. @param request: The HTTP request. @param verb: A C{str}, either 'GET' or 'HEAD'. @return: A C{Deferred} which fires with the body of a GET request, having set all the response headers that are common to GET and HEAD. (The body will be dropped (below) for HEAD requests.) """ usage = registry.findUsage(httpAboutCategoryName, verb, AboutTagInstanceResource) registry.checkRequest(usage, request) yield self._setObjectId() # This will raise TNoInstanceOnObject if there's no instance, # and that will return a 404 (see util.py). tvalue, tagValue = yield self.facadeClient.getTagInstance( self.session, self.path, self.objectId) value = guessValue(tvalue) accept = request.getHeader('accept') or '*/*' if tvalue.valueType == ThriftValueType.BINARY_TYPE: contentType = tvalue.binaryKeyMimeType if mimeparse.best_match([contentType], accept) == '': raise error.NotAcceptable() body = value # Mark this value as unwrappable for JSON request._fluiddb_jsonp_unwrappable = None else: contentType = mimeparse.best_match( contentTypesForPrimitiveValue, accept) if contentType == '': raise error.NotAcceptable() try: serializer = util.primitiveTypeSerializer[contentType] except KeyError: raise TInternalError('No serializer for %r.' % contentType) else: body = serializer(value) typeValue = self._getTypeHeader(tvalue.valueType) request.setHeader(util.buildHeader('Type'), typeValue) request.setHeader('Content-length', str(len(body))) request.setHeader('Content-type', contentType) # setting the Last-Modified header for fluiddb/id makes no sense if tagValue and tagValue.creationTime: request.setHeader( 'Last-modified', tagValue.creationTime.strftime('%a, %d %b %Y %H:%M:%S')) request.setResponseCode(usage.successCode) defer.returnValue(body)