[CalendarServer-changes] [11093] CalendarServer/branches/users/cdaboo/store-scheduling
source_changes at macosforge.org
source_changes at macosforge.org
Tue Apr 23 12:49:27 PDT 2013
Revision: 11093
http://trac.calendarserver.org//changeset/11093
Author: cdaboo at apple.com
Date: 2013-04-23 12:49:27 -0700 (Tue, 23 Apr 2013)
Log Message:
-----------
Checkpoint: move managed attachment processing fully into the store.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py 2013-04-23 16:37:12 UTC (rev 11092)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py 2013-04-23 19:49:27 UTC (rev 11093)
@@ -52,7 +52,6 @@
from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource, \
DefaultAlarmPropertyMixin
from twistedcaldav.scheduling_store.caldav.resource import ScheduleInboxResource
-from twistedcaldav.scheduling.implicit import ImplicitScheduler
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError
from txdav.base.propertystore.base import PropertyName
@@ -62,7 +61,7 @@
TooManyAttendeesError, InvalidCalendarAccessError, ValidOrganizerError, \
UIDExistsError, InvalidUIDError, InvalidPerUserDataMerge, \
AttendeeAllowedError, ResourceDeletedError, InvalidComponentForStoreError, \
- InvalidResourceMove, UIDExistsElsewhereError
+ InvalidResourceMove, UIDExistsElsewhereError, InvalidAttachmentOperation
from txdav.common.datastore.sql_tables import _BIND_MODE_READ, _BIND_MODE_WRITE, \
_BIND_MODE_DIRECT
from txdav.common.icommondatastore import NoSuchObjectResourceError, \
@@ -2578,6 +2577,16 @@
InvalidComponentTypeError: (caldav_namespace, "supported-component"),
}
+ StoreAttachmentValidErrors = set((
+ AttachmentStoreFailed,
+ InvalidAttachmentOperation,
+ ))
+
+ StoreAttachmentExceptionsErrors = {
+ AttachmentStoreValidManagedID: (caldav_namespace, "valid-managed-id-parameter",),
+ AttachmentRemoveFailed: (caldav_namespace, "valid-attachment-remove",),
+ }
+
@inlineCallbacks
def http_PUT(self, request):
@@ -2783,105 +2792,77 @@
"attachment-remove": "valid-attachment-remove",
}
- # Only allow organizers to manipulate managed attachments for now
- calendar = (yield self.iCalendarForUser(request))
- scheduler = ImplicitScheduler()
- is_attendee = (yield scheduler.testAttendeeEvent(request, self, calendar,))
- if is_attendee and action in valid_preconditions:
- raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, valid_preconditions[action],),
- "Attendees are not allowed to manipulate managed attachments",
- ))
-
# Dispatch to store object
- if action == "attachment-add":
+ try:
+ if action == "attachment-add":
+ rids = _getRIDs()
+ content_type, filename = _getContentInfo()
+ attachment, location = (yield self._newStoreObject.addAttachment(rids, content_type, filename, request.stream))
+ post_result = Response(CREATED)
- # Add an attachment property
- rids = _getRIDs()
- content_type, filename = _getContentInfo()
- try:
- attachment, location = (yield self._newStoreObject.addAttachment(rids, content_type, filename, request.stream, calendar))
- except AttachmentStoreFailed:
+ elif action == "attachment-update":
+ mid = _getMID()
+ content_type, filename = _getContentInfo()
+ attachment, location = (yield self._newStoreObject.updateAttachment(mid, content_type, filename, request.stream))
+ post_result = Response(NO_CONTENT)
+
+ elif action == "attachment-remove":
+ rids = _getRIDs()
+ mid = _getMID()
+ yield self._newStoreObject.removeAttachment(rids, mid)
+ post_result = Response(NO_CONTENT)
+
+ else:
raise HTTPError(ErrorResponse(
FORBIDDEN,
- (caldav_namespace, "valid-attachment-add",),
- "Could not store the supplied attachment",
+ (caldav_namespace, "valid-action-parameter",),
+ "The action parameter in the request-URI is not valid",
))
- except QuotaExceeded:
- raise HTTPError(ErrorResponse(
- INSUFFICIENT_STORAGE_SPACE,
- (dav_namespace, "quota-not-exceeded"),
- "Could not store the supplied attachment because user quota would be exceeded",
- ))
- post_result = Response(CREATED)
+ except QuotaExceeded:
+ raise HTTPError(ErrorResponse(
+ INSUFFICIENT_STORAGE_SPACE,
+ (dav_namespace, "quota-not-exceeded"),
+ "Could not store the supplied attachment because user quota would be exceeded",
+ ))
- elif action == "attachment-update":
- mid = _getMID()
- content_type, filename = _getContentInfo()
- try:
- attachment, location = (yield self._newStoreObject.updateAttachment(mid, content_type, filename, request.stream, calendar))
- except AttachmentStoreValidManagedID:
+ # Map store exception to HTTP errors
+ except Exception as err:
+
+ if type(err) in self.StoreAttachmentValidErrors:
raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, "valid-managed-id-parameter",),
- "The managed-id parameter does not refer to an attachment in this calendar object resource",
+ responsecode.FORBIDDEN,
+ (caldav_namespace, valid_preconditions[action],),
+ str(err),
))
- except AttachmentStoreFailed:
+
+ elif type(err) in self.StoreAttachmentExceptionsErrors:
raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, "valid-attachment-update",),
- "Could not store the supplied attachment",
+ responsecode.FORBIDDEN,
+ self.StoreAttachmentExceptionsErrors[type(err)],
+ str(err),
))
- except QuotaExceeded:
- raise HTTPError(ErrorResponse(
- INSUFFICIENT_STORAGE_SPACE,
- (dav_namespace, "quota-not-exceeded"),
- "Could not store the supplied attachment because user quota would be exceeded",
- ))
- post_result = Response(NO_CONTENT)
+ elif type(err) in self.StoreExceptionsStatusErrors:
+ raise HTTPError(StatusResponse(responsecode.FORBIDDEN, str(err)))
- elif action == "attachment-remove":
- rids = _getRIDs()
- mid = _getMID()
- try:
- yield self._newStoreObject.removeAttachment(rids, mid, calendar)
- except AttachmentStoreValidManagedID:
+ elif type(err) in self.StoreExceptionsErrors:
raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, "valid-managed-id-parameter",),
- "The managed-id parameter does not refer to an attachment in this calendar object resource",
+ responsecode.FORBIDDEN,
+ self.StoreExceptionsErrors[type(err)],
+ str(err),
))
- except AttachmentRemoveFailed:
- raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, "valid-attachment-remove",),
- "Could not remove the specified attachment",
- ))
- post_result = Response(NO_CONTENT)
+ else:
+ raise
- else:
- raise HTTPError(ErrorResponse(
- FORBIDDEN,
- (caldav_namespace, "valid-action-parameter",),
- "The action parameter in the request-URI is not valid",
- ))
-
- # TODO: The storing piece here should go away once we do implicit in the store
- # Store new resource
- parent = (yield request.locateResource(parentForURL(request.path)))
- storer = self.storeResource(request, None, self, request.uri, parent, False, calendar, attachmentProcessingDone=True)
- result = (yield storer.run())
-
# Look for Prefer header
prefer = request.headers.getHeader("prefer", {})
returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
- if returnRepresentation and result.code / 100 == 2:
+ if returnRepresentation:
result = (yield self.render(request))
result.code = OK
+ result.headers.removeHeader("content-location")
result.headers.setHeader("content-location", request.path)
else:
result = post_result
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py 2013-04-23 16:37:12 UTC (rev 11092)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/test/test_implicit.py 2013-04-23 19:49:27 UTC (rev 11093)
@@ -873,7 +873,7 @@
class ImplicitRequests(CommonCommonTests, TestCase):
"""
- Test twistedcaldav.scheduling.implicit with a Request object.
+ Test txdav.caldav.datastore.scheduling.implicit.
"""
@inlineCallbacks
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py 2013-04-23 16:37:12 UTC (rev 11092)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py 2013-04-23 19:49:27 UTC (rev 11093)
@@ -73,7 +73,7 @@
AttendeeAllowedError, InvalidPerUserDataMerge, ComponentUpdateState, \
ValidOrganizerError, ShareeAllowedError, ComponentRemoveState, \
InvalidComponentForStoreError, InvalidResourceMove, InvalidDefaultCalendar, \
- UIDExistsElsewhereError
+ UIDExistsElsewhereError, InvalidAttachmentOperation
from txdav.caldav.icalendarstore import QuotaExceeded
from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
CommonObjectResource, ECALENDARTYPE
@@ -1831,6 +1831,8 @@
new_component = None
did_implicit_action = False
+ is_internal = internal_state not in (ComponentUpdateState.NORMAL, ComponentUpdateState.ATTACHMENT_UPDATE,)
+
# Do scheduling
if not self.calendar().isInbox():
scheduler = ImplicitScheduler()
@@ -1840,10 +1842,10 @@
self.calendar(),
None if inserting else self,
component,
- internal_request=(internal_state != ComponentUpdateState.NORMAL),
+ internal_request=is_internal,
))
- if do_implicit_action and internal_state == ComponentUpdateState.NORMAL:
+ if do_implicit_action and not is_internal:
# Cannot do implicit in sharee's shared calendar
if not self.calendar().owned():
@@ -2817,8 +2819,37 @@
@inlineCallbacks
- def addAttachment(self, rids, content_type, filename, stream, calendar):
+ def _checkValidManagedAttachmentChange(self):
+ """
+ Make sure a managed attachment add, update or remover operation is valid.
+ """
+ # Only allow organizers to manipulate managed attachments for now
+ calendar = (yield self.componentForUser())
+ scheduler = ImplicitScheduler()
+ is_attendee = (yield scheduler.testAttendeeEvent(self.calendar(), self, calendar,))
+ if is_attendee:
+ raise InvalidAttachmentOperation("Attendees are not allowed to manipulate managed attachments")
+
+
+ @inlineCallbacks
+ def addAttachment(self, rids, content_type, filename, stream):
+ """
+ Add a new managed attachment to this calendar object.
+
+ @param rids: list of recurrence-ids for components to add to, or C{None} to add to all.
+ @type rids: C{str} or C{None}
+ @param content_type: the MIME media type/subtype of the attachment
+ @type content_type: L{MimeType}
+ @param filename: the name for the attachment
+ @type filename: C{str}
+ @param stream: the stream to read attachment data from
+ @type stream: L{IStream}
+ """
+
+ # Check validity of request
+ yield self._checkValidManagedAttachmentChange()
+
# First write the data stream
# We need to know the resource_ID of the home collection of the owner
@@ -2836,8 +2867,11 @@
self._dropboxID = str(uuid.uuid4())
attachment._objectDropboxID = self._dropboxID
- # Now try and adjust the actual calendar data
- #calendar = (yield self.component())
+ # Now try and adjust the actual calendar data.
+ # NB We need a copy of the original calendar data as implicit scheduling will need to compare that to
+ # the original in order to detect changes that would case scheduling.
+ calendar = (yield self.componentForUser())
+ calendar = calendar.duplicate()
attach, location = (yield attachment.attachProperty())
if rids is None:
@@ -2846,15 +2880,30 @@
# TODO - per-recurrence attachments
pass
- # TODO: Here is where we want to store data implicitly - for now we have to let app layer deal with it
- #yield self.setComponent(calendar)
+ # Here is where we want to store data implicitly
+ yield self._setComponentInternal(calendar, internal_state=ComponentUpdateState.ATTACHMENT_UPDATE)
returnValue((attachment, location,))
@inlineCallbacks
- def updateAttachment(self, managed_id, content_type, filename, stream, calendar):
+ def updateAttachment(self, managed_id, content_type, filename, stream):
+ """
+ Update a managed attachment in this calendar object.
+ @param managed_id: the attachment's managed-id
+ @type managed_id: C{str}
+ @param content_type: the new MIME media type/subtype of the attachment
+ @type content_type: L{MIMEType}
+ @param filename: the new name for the attachment
+ @type filename: C{str}
+ @param stream: the stream to read new attachment data from
+ @type stream: L{IStream}
+ """
+
+ # Check validity of request
+ yield self._checkValidManagedAttachmentChange()
+
# First check the supplied managed-id is associated with this resource
cobjs = (yield ManagedAttachment.referencesTo(self._txn, managed_id))
if self._resourceID not in cobjs:
@@ -2881,29 +2930,47 @@
raise AttachmentStoreFailed
yield t.loseConnection()
- # Now try and adjust the actual calendar data
- #calendar = (yield self.component())
+ # Now try and adjust the actual calendar data.
+ # NB We need a copy of the original calendar data as implicit scheduling will need to compare that to
+ # the original in order to detect changes that would case scheduling.
+ calendar = (yield self.componentForUser())
+ calendar = calendar.duplicate()
attach, location = (yield attachment.attachProperty())
calendar.replaceAllPropertiesWithParameterMatch(attach, "MANAGED-ID", managed_id)
- # TODO: Here is where we want to store data implicitly - for now we have to let app layer deal with it
- #yield self.setComponent(calendar)
+ # Here is where we want to store data implicitly
+ yield self._setComponentInternal(calendar, internal_state=ComponentUpdateState.ATTACHMENT_UPDATE)
returnValue((attachment, location,))
@inlineCallbacks
- def removeAttachment(self, rids, managed_id, calendar):
+ def removeAttachment(self, rids, managed_id):
+ """
+ Remove a managed attachment from this calendar object.
+ @param rids: list of recurrence-ids for components to add to, or C{None} to add to all.
+ @type rids: C{str} or C{None}
+ @param managed_id: the attachment's managed-id
+ @type managed_id: C{str}
+ """
+
+ # Check validity of request
+ yield self._checkValidManagedAttachmentChange()
+
# First check the supplied managed-id is associated with this resource
cobjs = (yield ManagedAttachment.referencesTo(self._txn, managed_id))
if self._resourceID not in cobjs:
raise AttachmentStoreValidManagedID
- # Now try and adjust the actual calendar data
+ # Now try and adjust the actual calendar data.
+ # NB We need a copy of the original calendar data as implicit scheduling will need to compare that to
+ # the original in order to detect changes that would case scheduling.
all_removed = False
- #calendar = (yield self.component())
+ calendar = (yield self.componentForUser())
+ calendar = calendar.duplicate()
+
if rids is None:
calendar.removeAllPropertiesWithParameterMatch("ATTACH", "MANAGED-ID", managed_id)
all_removed = True
@@ -2911,8 +2978,8 @@
# TODO: per-recurrence removal
pass
- # TODO: Here is where we want to store data implicitly - for now we have to let app layer deal with it
- #yield self.setComponent(calendar)
+ # Here is where we want to store data implicitly
+ yield self._setComponentInternal(calendar, internal_state=ComponentUpdateState.ATTACHMENT_UPDATE)
# Remove it - this will take care of actually removing it from the store if there are
# no more references to the attachment
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py 2013-04-23 16:37:12 UTC (rev 11092)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py 2013-04-23 19:49:27 UTC (rev 11093)
@@ -74,6 +74,8 @@
iTIP message. Some validation and implicit scheduling is not done. Schedule-Tag
is changed.
+ ATTACHMENT_UPDATE - change to a managed attachment that is re-writing calendar data.
+
RAW - store the supplied data as-is without any processing or validation. This is used
for unit testing purposes only.
"""
@@ -82,12 +84,14 @@
INBOX = NamedConstant()
ORGANIZER_ITIP_UPDATE = NamedConstant()
ATTENDEE_ITIP_UPDATE = NamedConstant()
+ ATTACHMENT_UPDATE = NamedConstant()
RAW = NamedConstant()
NORMAL.description = "normal"
INBOX.description = "inbox"
ORGANIZER_ITIP_UPDATE.description = "organizer-update"
ATTENDEE_ITIP_UPDATE.description = "attendee-update"
+ ATTACHMENT_UPDATE.description = "attachment-update"
RAW.description = "raw"
@@ -214,6 +218,13 @@
+class InvalidAttachmentOperation(Exception):
+ """
+ Unable to store an attachment because some aspect of the request is invalid.
+ """
+
+
+
class AttachmentStoreFailed(Exception):
"""
Unable to store an attachment.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130423/194d3867/attachment-0001.html>
More information about the calendarserver-changes
mailing list