[CalendarServer-changes] [5917] CalendarServer/branches/new-store/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jul 19 15:51:06 PDT 2010
Revision: 5917
http://trac.macosforge.org/projects/calendarserver/changeset/5917
Author: glyph at apple.com
Date: 2010-07-19 15:51:06 -0700 (Mon, 19 Jul 2010)
Log Message:
-----------
add and test a response filter that will roll back any uncommitted transactions by the time the response is being relayed to the client
Modified Paths:
--------------
CalendarServer/branches/new-store/twistedcaldav/static.py
CalendarServer/branches/new-store/twistedcaldav/test/test_wrapping.py
Modified: CalendarServer/branches/new-store/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/static.py 2010-07-19 22:46:06 UTC (rev 5916)
+++ CalendarServer/branches/new-store/twistedcaldav/static.py 2010-07-19 22:51:06 UTC (rev 5917)
@@ -66,6 +66,7 @@
from twext.web2.dav.resource import davPrivilegeSet
from twext.web2.dav.util import parentForURL, bindMethods, joinURL
from twext.web2.http_headers import generateContentType, MimeType
+from txdav.idav import AlreadyFinishedError
from twistedcaldav import caldavxml
from twistedcaldav import carddavxml
@@ -890,6 +891,44 @@
+def _transactionFromRequest(request, newStore):
+ """
+ Return the associated transaction from the given HTTP request, creating a
+ new one from the given data store if none has yet been associated.
+
+ Also, if the request was not previously associated with a transaction, add
+ a failsafe transaction-abort response filter to abort any transaction which
+ has not been committed or aborted by the resource which responds to the
+ request.
+
+ @param request: The request to inspect.
+ @type request: L{IRequest}
+
+ @param newStore: The store to create a transaction from.
+ @type newStore: L{IDataStore}
+
+ @return: a transaction that should be used to read and write data
+ associated with the request.
+ @rtype: L{ITransaction} (and possibly L{ICalendarTransaction} and
+ L{IAddressBookTransaction} as well.
+ """
+ TRANSACTION_KEY = '_newStoreTransaction'
+ transaction = getattr(request, TRANSACTION_KEY, None)
+ if transaction is None:
+ transaction = newStore.newTransaction(repr(request))
+ def abortIfUncommitted(request, response):
+ try:
+ transaction.abort()
+ except AlreadyFinishedError:
+ pass
+ return response
+ abortIfUncommitted.handleErrors = True
+ request.addResponseFilter(abortIfUncommitted)
+ setattr(request, TRANSACTION_KEY, transaction)
+ return transaction
+
+
+
class CalendarHomeUIDProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeUIDProvisioningResource, DAVFile):
def __init__(self, path, parent, homeResourceClass=None):
"""
@@ -917,11 +956,7 @@
def homeResourceForRecord(self, record, request):
self.provision()
- TRANSACTION_KEY = '_newStoreTransaction'
- transaction = getattr(request, TRANSACTION_KEY, None)
- if transaction is None:
- transaction = self.parent._newStore.newTransaction(repr(request))
- setattr(request, TRANSACTION_KEY, transaction)
+ transaction = _transactionFromRequest(request, self.parent._newStore)
name = record.uid
@@ -1599,11 +1634,7 @@
def homeResourceForRecord(self, record, request):
self.provision()
- TRANSACTION_KEY = '_newStoreTransaction'
- transaction = getattr(request, TRANSACTION_KEY, None)
- if transaction is None:
- transaction = self.parent._newStore.newTransaction(repr(request))
- setattr(request, TRANSACTION_KEY, transaction)
+ transaction = _transactionFromRequest(request, self.parent._newStore)
name = record.uid
Modified: CalendarServer/branches/new-store/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/test/test_wrapping.py 2010-07-19 22:46:06 UTC (rev 5916)
+++ CalendarServer/branches/new-store/twistedcaldav/test/test_wrapping.py 2010-07-19 22:51:06 UTC (rev 5917)
@@ -18,8 +18,12 @@
Tests for the interaction between model-level and protocol-level logic.
"""
+from twext.web2.server import Request
+from twext.web2.responsecode import OK, UNAUTHORIZED
+from twext.web2.http_headers import Headers
+from txdav.idav import AlreadyFinishedError
+
from twext.web2.dav import davxml
-from twext.web2.dav.element.base import dav_namespace
from twistedcaldav.config import config
from twisted.internet.defer import inlineCallbacks, returnValue
@@ -39,6 +43,28 @@
from txcarddav.addressbookstore.test.test_file import vcard4_text
+class FakeChanRequest(object):
+ def writeHeaders(self, code, headers):
+ self.code = code
+ self.headers = headers
+ def registerProducer(self, producer, streaming):
+ pass
+ def write(self, data):
+ pass
+ def unregisterProducer(self):
+ pass
+ def abortConnection(self):
+ pass
+ def getHostInfo(self):
+ return '127.0.0.1', False
+ def getRemoteHost(self):
+ return '127.0.0.1'
+ def finish(self):
+ pass
+
+
+
+
class WrappingTests(TestCase):
"""
Tests for L{twistedcaldav.static.CalDAVFile} creating the appropriate type
@@ -65,7 +91,7 @@
@param objectName: The name of a calendar object.
@type objectName: str
@param objectText: Some iCalendar text to populate it with.
- @type objectText: str
+ @type objectText: str
"""
record = self.directoryService.recordWithShortName("users", "wsanchez")
uid = record.uid
@@ -89,7 +115,7 @@
@param objectName: The name of a addressbook object.
@type objectName: str
@param objectText: Some iVcard text to populate it with.
- @type objectText: str
+ @type objectText: str
"""
record = self.directoryService.recordWithShortName("users", "wsanchez")
uid = record.uid
@@ -109,6 +135,8 @@
txn.commit()
+ requestUnderTest = None
+
@inlineCallbacks
def getResource(self, path):
"""
@@ -119,11 +147,15 @@
@type path: C{str}
"""
- segments = path.split("/")
- resource = self.site.resource
- while segments:
- resource, segments = yield resource.locateChild(self, segments)
- returnValue(resource)
+ if self.requestUnderTest is None:
+ req = self.requestForPath(path)
+ self.requestUnderTest = req
+ else:
+ req = self.requestUnderTest
+ aResource = yield req.locateResource(
+ "http://localhost:8008/" + path
+ )
+ returnValue(aResource)
def commit(self):
@@ -132,9 +164,59 @@
an associated transaction. Commit that transaction to bring the
filesystem into a consistent state.
"""
- self._newStoreTransaction.commit()
+ self.requestUnderTest._newStoreTransaction.commit()
+ def requestForPath(self, path):
+ """
+ Get a L{Request} with a L{FakeChanRequest} for a given path.
+ """
+ headers = Headers()
+ headers.addRawHeader("Host", "localhost:8008")
+ chanReq = FakeChanRequest()
+ req = Request(
+ site=self.site,
+ chanRequest=chanReq,
+ command='GET',
+ path=path,
+ version=('1', '1'),
+ contentLength=0,
+ headers=headers
+ )
+ req.credentialFactories = {}
+ return req
+
+
+ @inlineCallbacks
+ def test_autoRevertUnCommitted(self):
+ """
+ Resources that need to read from the back-end in a transaction will be
+ reverted by a response filter in the case where the request does not
+ commit them. This can happen, for example, with resources that are
+ children of non-existent (proto-)resources.
+ """
+ for pathType in ['calendar', 'addressbook']:
+ req = self.requestForPath('/%ss/users/wsanchez/%s/forget/it'
+ % (pathType, pathType))
+ yield req.process()
+ self.assertEquals(req.chanRequest.code, 404)
+ self.assertRaises(AlreadyFinishedError,
+ req._newStoreTransaction.commit)
+
+
+ @inlineCallbacks
+ def test_simpleRequest(self):
+ """
+ Sanity check and integration test: an unauthorized request of calendar
+ and addressbook resources results in an L{UNAUTHORIZED} response code.
+ """
+ for pathType in ['calendar', 'addressbook']:
+ req = self.requestForPath('/%ss/users/wsanchez/%s/'
+ % (pathType, pathType))
+ yield req.process()
+ self.assertEquals(req.chanRequest.code, UNAUTHORIZED)
+
+
def test_createStore(self):
"""
Creating a CalendarHomeProvisioningFile will create a paired
@@ -221,6 +303,7 @@
self.assertIdentical(
getattr(calDavFile, "_newStoreCalendar", None), None
)
+ self.commit()
@inlineCallbacks
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100719/49ec87af/attachment.html>
More information about the calendarserver-changes
mailing list