[CalendarServer-changes] [11934] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 12 11:16:18 PDT 2014
Revision: 11934
http://trac.calendarserver.org//changeset/11934
Author: sagen at apple.com
Date: 2013-11-12 13:38:00 -0800 (Tue, 12 Nov 2013)
Log Message:
-----------
New APNS protocol with support for priority levels
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/push/amppush.py
CalendarServer/trunk/calendarserver/push/applepush.py
CalendarServer/trunk/calendarserver/push/notifier.py
CalendarServer/trunk/calendarserver/push/test/test_amppush.py
CalendarServer/trunk/calendarserver/push/test/test_applepush.py
CalendarServer/trunk/calendarserver/push/test/test_notifier.py
CalendarServer/trunk/calendarserver/push/util.py
CalendarServer/trunk/calendarserver/tools/ampnotifications.py
CalendarServer/trunk/calendarserver/tools/gateway.py
CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/common.py
CalendarServer/trunk/txdav/carddav/datastore/test/common.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
CalendarServer/trunk/txdav/common/datastore/test/util.py
CalendarServer/trunk/txdav/idav.py
Modified: CalendarServer/trunk/calendarserver/push/amppush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/amppush.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/amppush.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -23,7 +23,9 @@
import time
import uuid
+from calendarserver.push.util import PushPriority
+
log = Logger()
@@ -49,7 +51,8 @@
class NotificationForID(amp.Command):
arguments = [('id', amp.String()),
- ('dataChangedTimestamp', amp.Integer(optional=True))]
+ ('dataChangedTimestamp', amp.Integer(optional=True)),
+ ('priority', amp.Integer(optional=True))]
response = [('status', amp.String())]
@@ -82,12 +85,14 @@
@inlineCallbacks
- def enqueue(self, transaction, id, dataChangedTimestamp=None):
+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=PushPriority.high):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
for protocol in self.protocols:
yield protocol.callRemote(NotificationForID, id=id,
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
@@ -103,10 +108,12 @@
@NotificationForID.responder
- def enqueueFromWorker(self, id, dataChangedTimestamp=None):
+ def enqueueFromWorker(self, id, dataChangedTimestamp=None,
+ priority=PushPriority.high.value):
if dataChangedTimestamp is None:
dataChangedTimestamp = int(time.time())
- self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp)
+ self.master.enqueue(None, id, dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.lookupByValue(priority))
return {"status" : "OK"}
@@ -167,7 +174,8 @@
self.subscribers.remove(p)
- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
"""
Sends an AMP push notification to any clients subscribing to this pushKey.
@@ -192,23 +200,26 @@
if token is not None:
tokens.append(token)
if tokens:
- return self.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
+ return self.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
@inlineCallbacks
- def sendNotification(self, token, id, dataChangedTimestamp):
+ def sendNotification(self, token, id, dataChangedTimestamp, priority):
for subscriber in self.subscribers:
if subscriber.subscribedToID(id):
- yield subscriber.notify(token, id, dataChangedTimestamp)
+ yield subscriber.notify(token, id, dataChangedTimestamp,
+ priority)
@inlineCallbacks
- def scheduleNotifications(self, tokens, id, dataChangedTimestamp):
+ def scheduleNotifications(self, tokens, id, dataChangedTimestamp, priority):
if self.scheduler is not None:
- self.scheduler.schedule(tokens, id, dataChangedTimestamp)
+ self.scheduler.schedule(tokens, id, dataChangedTimestamp, priority)
else:
for token in tokens:
- yield self.sendNotification(token, id, dataChangedTimestamp)
+ yield self.sendNotification(token, id, dataChangedTimestamp,
+ priority)
@@ -238,11 +249,12 @@
return {"status" : "OK"}
UnsubscribeFromID.responder(unsubscribe)
- def notify(self, token, id, dataChangedTimestamp):
+ def notify(self, token, id, dataChangedTimestamp, priority):
if self.subscribedToID(id) == token:
self.log.debug("Sending notification for %s to %s" % (id, token))
return self.callRemote(NotificationForID, id=id,
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=priority.value)
def subscribedToID(self, id):
@@ -288,8 +300,8 @@
@inlineCallbacks
- def notificationForID(self, id, dataChangedTimestamp):
- yield self.callback(id, dataChangedTimestamp)
+ def notificationForID(self, id, dataChangedTimestamp, priority):
+ yield self.callback(id, dataChangedTimestamp, PushPriority.lookupByValue(priority))
returnValue({"status" : "OK"})
NotificationForID.responder(notificationForID)
Modified: CalendarServer/trunk/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/applepush.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/applepush.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -36,16 +36,27 @@
import struct
import time
from txdav.common.icommondatastore import InvalidSubscriptionValues
-
-from calendarserver.push.util import validToken, TokenHistory, PushScheduler
-
+from calendarserver.push.util import (
+ validToken, TokenHistory, PushScheduler, PushPriority
+)
from twext.internet.adaptendpoint import connect
from twext.internet.gaiendpoint import GAIEndpoint
+from twisted.python.constants import Values, ValueConstant
log = Logger()
+class ApplePushPriority(Values):
+ """
+ Maps calendarserver.push.util.PushPriority values to APNS-specific values
+ """
+ low = ValueConstant(PushPriority.low.value)
+ medium = ValueConstant(PushPriority.medium.value)
+ high = ValueConstant(PushPriority.high.value)
+
+
+
class ApplePushNotifierService(service.MultiService):
"""
ApplePushNotifierService is a MultiService responsible for
@@ -55,7 +66,7 @@
The Apple Push Notification protocol is described here:
- http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html
+ https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html
"""
log = Logger()
@@ -177,7 +188,8 @@
@inlineCallbacks
- def enqueue(self, transaction, pushKey, dataChangedTimestamp=None):
+ def enqueue(self, transaction, pushKey, dataChangedTimestamp=None,
+ priority=PushPriority.high):
"""
Sends an Apple Push Notification to any device token subscribed to
this pushKey.
@@ -191,6 +203,8 @@
@param dataChangedTimestamp: Timestamp (epoch seconds) for the data change
which triggered this notification (Only used for unit tests)
@type key: C{int}
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
try:
@@ -219,7 +233,8 @@
if token and uid:
tokens.append(token)
if tokens:
- provider.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
+ provider.scheduleNotifications(tokens, pushKey,
+ dataChangedTimestamp, priority)
@@ -230,8 +245,7 @@
log = Logger()
# Sent by provider
- COMMAND_SIMPLE = 0
- COMMAND_ENHANCED = 1
+ COMMAND_PROVIDER = 2
# Received by provider
COMMAND_ERROR = 8
@@ -333,7 +347,7 @@
yield txn.commit()
- def sendNotification(self, token, key, dataChangedTimestamp):
+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
"""
Sends a push notification message for the key to the device associated
with the token.
@@ -357,6 +371,7 @@
return
identifier = self.history.add(token)
+ apnsPriority = ApplePushPriority.lookupByValue(priority.value).value
payload = json.dumps(
{
"key" : key,
@@ -365,23 +380,79 @@
}
)
payloadLength = len(payload)
- self.log.debug("Sending APNS notification to {token}: id={id} payload={payload}",
- token=token, id=identifier, payload=payload)
+ self.log.debug("Sending APNS notification to {token}: id={id} payload={payload} priority={priority}",
+ token=token, id=identifier, payload=payload, priority=apnsPriority)
+ """
+ Notification format
+
+ Top level: Command (1 byte), Frame length (4 bytes), Frame data (variable)
+ Within Frame data: Item ...
+ Item: Item number (1 byte), Item data length (2 bytes), Item data (variable)
+ Item 1: Device token (32 bytes)
+ Item 2: Payload (variable length) in JSON format, not null-terminated
+ Item 3: Notification ID (4 bytes) an opaque value used for reporting errors
+ Item 4: Expiration date (4 bytes) UNIX epoch in secondcs UTC
+ Item 5: Priority (1 byte): 10 (push sent immediately) or 5 (push sent
+ at a time that conservces power on the device receiving it)
+ """
+
+ # Frame struct.pack format
+ # ! Network byte order
+ command = self.COMMAND_PROVIDER # B
+ frameLength = ( # I
+ # Item 1 (Device token)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 32 + # device token # 32s
+ # Item 2 (Payload)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ payloadLength + # the JSON payload # %d s
+ # Item 3 (Notification ID)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Notification ID # I
+ # Item 4 (Expiration)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 4 + # Expiration seconds since epoch # I
+ # Item 5 (Priority)
+ 1 + # Item number # B
+ 2 + # Item length # H
+ 1 # Priority # B
+ )
+
self.transport.write(
- struct.pack("!BIIH32sH%ds" % (payloadLength,),
- self.COMMAND_ENHANCED, # Command
- identifier, # Identifier
- int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
+ struct.pack("!BIBH32sBH%dsBHIBHIBHB" % (payloadLength,),
+
+ command, # Command
+ frameLength, # Frame length
+
+ 1, # Item 1 (Device token)
32, # Token Length
binaryToken, # Token
- payloadLength, # Payload Length
- payload, # Payload in JSON format
+
+ 2, # Item 2 (Payload)
+ payloadLength, # Payload length
+ payload, # Payload
+
+ 3, # Item 3 (Notification ID)
+ 4, # Notification ID Length
+ identifier, # Notification ID
+
+ 4, # Item 4 (Expiration)
+ 4, # Expiration length
+ int(time.time()) + 72 * 60 * 60, # Expires in 72 hours
+
+ 5, # Item 5 (Priority)
+ 1, # Priority length
+ apnsPriority, # Priority
+
)
)
-
class APNProviderFactory(ReconnectingClientFactory):
log = Logger()
@@ -509,12 +580,13 @@
# sent will be put back into the queue.
queued = list(self.queue)
self.queue = []
- for (token, key), dataChangedTimestamp in queued:
- if token and key and dataChangedTimestamp:
- self.sendNotification(token, key, dataChangedTimestamp)
+ for (token, key), dataChangedTimestamp, priority in queued:
+ if token and key and dataChangedTimestamp and priority:
+ self.sendNotification(token, key, dataChangedTimestamp,
+ priority)
- def scheduleNotifications(self, tokens, key, dataChangedTimestamp):
+ def scheduleNotifications(self, tokens, key, dataChangedTimestamp, priority):
"""
The starting point for getting notifications to the APNS server. If there is
a connection to the APNS server, these notifications are scheduled (or directly
@@ -533,15 +605,15 @@
connection = getattr(self.factory, "connection", None)
if connection is not None:
if self.scheduler is not None:
- self.scheduler.schedule(tokens, key, dataChangedTimestamp)
+ self.scheduler.schedule(tokens, key, dataChangedTimestamp, priority)
else:
for token in tokens:
- self.sendNotification(token, key, dataChangedTimestamp)
+ self.sendNotification(token, key, dataChangedTimestamp, priority)
else:
- self._saveForWhenConnected(tokens, key, dataChangedTimestamp)
+ self._saveForWhenConnected(tokens, key, dataChangedTimestamp, priority)
- def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp):
+ def _saveForWhenConnected(self, tokens, key, dataChangedTimestamp, priority):
"""
Called in order to save notifications that can't be sent now because there
is no connection to the APNS server. (token, key) tuples are appended to
@@ -557,16 +629,16 @@
"""
for token in tokens:
tokenKeyPair = (token, key)
- for existingPair, ignored in self.queue:
+ for existingPair, timstamp, priority in self.queue:
if tokenKeyPair == existingPair:
self.log.debug("APNProviderService has no connection; skipping duplicate: %s %s" % (token, key))
break # Already scheduled
else:
self.log.debug("APNProviderService has no connection; queuing: %s %s" % (token, key))
- self.queue.append(((token, key), dataChangedTimestamp))
+ self.queue.append(((token, key), dataChangedTimestamp, priority))
- def sendNotification(self, token, key, dataChangedTimestamp):
+ def sendNotification(self, token, key, dataChangedTimestamp, priority):
"""
If there is a connection the notification is sent right away, otherwise
the notification is saved for later.
@@ -579,15 +651,15 @@
which triggered this notification
@type key: C{int}
"""
- if not (token and key and dataChangedTimestamp):
+ if not (token and key and dataChangedTimestamp, priority):
return
# Service has reference to factory has reference to protocol instance
connection = getattr(self.factory, "connection", None)
if connection is None:
- self._saveForWhenConnected([token], key, dataChangedTimestamp)
+ self._saveForWhenConnected([token], key, dataChangedTimestamp, priority)
else:
- connection.sendNotification(token, key, dataChangedTimestamp)
+ connection.sendNotification(token, key, dataChangedTimestamp, priority)
Modified: CalendarServer/trunk/calendarserver/push/notifier.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/notifier.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/notifier.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -19,7 +19,7 @@
"""
from twext.enterprise.dal.record import fromTable
-from twext.enterprise.dal.syntax import Delete
+from twext.enterprise.dal.syntax import Delete, Select, Parameter
from twext.enterprise.queue import WorkItem
from twext.python.log import Logger
@@ -32,10 +32,13 @@
import datetime
+from calendarserver.push.util import PushPriority
log = Logger()
+
+
class PushNotificationWork(WorkItem, fromTable(schema.PUSH_NOTIFICATION_WORK)):
group = property(lambda self: self.pushID)
@@ -43,14 +46,36 @@
@inlineCallbacks
def doWork(self):
- # Delete all other work items with the same pushID
- yield Delete(From=self.table,
- Where=self.table.PUSH_ID == self.pushID
- ).on(self.transaction)
+ # Find all work items with the same push ID and find the highest
+ # priority. Delete matching work items.
+ results = (yield Select([self.table.WORK_ID, self.table.PRIORITY,
+ self.table.PUSH_ID],
+ From=self.table, Where=self.table.PUSH_ID == self.pushID).on(
+ self.transaction))
+ maxPriority = self.priority
+
+ # If there are other enqueued work items for this push ID, find the
+ # highest priority one and use that value
+ if results:
+ workIDs = []
+ for workID, priority, pushID in results:
+ if priority > maxPriority:
+ maxPriority = priority
+ workIDs.append(workID)
+
+ # Delete the work items we selected
+ yield Delete(From=self.table,
+ Where=self.table.WORK_ID.In(
+ Parameter("workIDs", len(workIDs)))
+ ).on(self.transaction, workIDs=workIDs)
+
pushDistributor = self.transaction._pushDistributor
if pushDistributor is not None:
- yield pushDistributor.enqueue(self.transaction, self.pushID)
+ # Convert the integer priority value back into a constant
+ priority = PushPriority.lookupByValue(maxPriority)
+ yield pushDistributor.enqueue(self.transaction, self.pushID,
+ priority=priority)
@@ -84,13 +109,15 @@
@inlineCallbacks
- def notify(self, txn):
+ def notify(self, txn, priority=PushPriority.high):
"""
Send the notification. For a home object we just push using the home id. For a home
child we push both the owner home id and the owned home child id.
@param txn: The transaction to create the work item with
@type txn: L{CommonStoreTransaction}
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
# Push ids from the store objects are a tuple of (prefix, name,) and we need to compose that
# into a single token.
@@ -102,10 +129,13 @@
for prefix, id in ids:
if self._notify:
- self.log.debug("Notifications are enabled: %s %s/%s" % (self._storeObject, prefix, id,))
- yield self._notifierFactory.send(prefix, id, txn)
+ self.log.debug("Notifications are enabled: %s %s/%s priority=%d" %
+ (self._storeObject, prefix, id, priority.value))
+ yield self._notifierFactory.send(prefix, id, txn,
+ priority=priority)
else:
- self.log.debug("Skipping notification for: %s %s/%s" % (self._storeObject, prefix, id,))
+ self.log.debug("Skipping notification for: %s %s/%s" %
+ (self._storeObject, prefix, id,))
def clone(self, storeObject):
@@ -150,12 +180,14 @@
@inlineCallbacks
- def send(self, prefix, id, txn):
+ def send(self, prefix, id, txn, priority=PushPriority.high):
"""
Enqueue a push notification work item on the provided transaction.
"""
notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=self.coalesceSeconds)
- yield txn.enqueue(PushNotificationWork, pushID=self.pushKeyForId(prefix, id), notBefore=notBefore)
+ yield txn.enqueue(PushNotificationWork,
+ pushID=self.pushKeyForId(prefix, id), notBefore=notBefore,
+ priority=priority.value)
def newNotifier(self, storeObject):
@@ -212,7 +244,7 @@
@inlineCallbacks
- def enqueue(self, transaction, pushKey):
+ def enqueue(self, transaction, pushKey, priority=PushPriority.high):
"""
Pass along enqueued pushKey to any observers
@@ -221,6 +253,10 @@
@param pushKey: the push key to distribute to the observers
@type pushKey: C{str}
+
+ @param priority: the priority level
+ @type priority: L{PushPriority}
"""
for observer in self.observers:
- yield observer.enqueue(transaction, pushKey)
+ yield observer.enqueue(transaction, pushKey,
+ dataChangedTimestamp=None, priority=priority)
Modified: CalendarServer/trunk/calendarserver/push/test/test_amppush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_amppush.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/test/test_amppush.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -18,6 +18,7 @@
from calendarserver.push.amppush import NotificationForID
from twistedcaldav.test.util import StoreTestCase
from twisted.internet.task import Clock
+from calendarserver.push.util import PushPriority
class AMPPushMasterTests(StoreTestCase):
@@ -57,27 +58,81 @@
self.assertTrue(client3.subscribedToID("/CalDAV/localhost/user03/"))
dataChangedTimestamp = 1354815999
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.high)
self.assertEquals(len(client1.history), 0)
self.assertEquals(len(client2.history), 0)
self.assertEquals(len(client3.history), 0)
clock.advance(1)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
self.assertEquals(len(client2.history), 0)
self.assertEquals(len(client3.history), 0)
clock.advance(3)
- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
+
self.assertEquals(len(client3.history), 0)
clock.advance(3)
- self.assertEquals(client3.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp': 1354815999})])
+ self.assertEquals(
+ client3.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.high.value,
+ }
+ )
+ ]
+ )
client1.reset()
client2.reset()
client2.unsubscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.low)
self.assertEquals(len(client1.history), 0)
clock.advance(1)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.low.value,
+ }
+ )
+ ]
+ )
+
self.assertEquals(len(client2.history), 0)
clock.advance(3)
self.assertEquals(len(client2.history), 0)
@@ -87,9 +142,35 @@
client1.reset()
client2.reset()
client2.subscribe("token2", "/CalDAV/localhost/user01/")
- service.enqueue(None, "/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
- self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
- self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
+ service.enqueue(None, "/CalDAV/localhost/user01/",
+ dataChangedTimestamp=dataChangedTimestamp,
+ priority=PushPriority.medium)
+ self.assertEquals(
+ client1.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
+ self.assertEquals(
+ client2.history,
+ [
+ (
+ NotificationForID,
+ {
+ 'id' : '/CalDAV/localhost/user01/',
+ 'dataChangedTimestamp' : 1354815999,
+ 'priority' : PushPriority.medium.value,
+ }
+ )
+ ]
+ )
Modified: CalendarServer/trunk/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_applepush.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/test/test_applepush.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -18,14 +18,15 @@
import struct
import time
from calendarserver.push.applepush import (
- ApplePushNotifierService, APNProviderProtocol
+ ApplePushNotifierService, APNProviderProtocol, ApplePushPriority
)
-from calendarserver.push.util import validToken, TokenHistory
+from calendarserver.push.util import validToken, TokenHistory, PushPriority
from twistedcaldav.test.util import StoreTestCase
from twisted.internet.defer import inlineCallbacks, succeed
from twisted.internet.task import Clock
from txdav.common.icommondatastore import InvalidSubscriptionValues
+
class ApplePushNotifierServiceTests(StoreTestCase):
@inlineCallbacks
@@ -120,12 +121,14 @@
dataChangedTimestamp = 1354815999
txn = self._sqlCalendarStore.newTransaction()
yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
- dataChangedTimestamp=dataChangedTimestamp)
+ dataChangedTimestamp=dataChangedTimestamp, priority=PushPriority.high)
yield txn.commit()
# The notifications should be in the queue
- self.assertTrue(((token, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
- self.assertTrue(((token2, key1), dataChangedTimestamp) in service.providers["CalDAV"].queue)
+ self.assertTrue(((token, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
+ self.assertTrue(((token2, key1), dataChangedTimestamp, PushPriority.high)
+ in service.providers["CalDAV"].queue)
# Start the service, making the connection which should service the
# queue
@@ -137,17 +140,40 @@
# Verify data sent to APN
providerConnector = service.providers["CalDAV"].testConnector
rawData = providerConnector.transport.data
- self.assertEquals(len(rawData), 183)
- data = struct.unpack("!BIIH32sH", rawData[:45])
- self.assertEquals(data[0], 1) # command
- self.assertEquals(data[4].encode("hex"), token.replace(" ", "")) # token
- payloadLength = data[5]
- payload = struct.unpack("%ds" % (payloadLength,),
- rawData[45:])
+ self.assertEquals(len(rawData), 199)
+ data = struct.unpack("!BI", rawData[:5])
+ self.assertEquals(data[0], 2) # command
+ self.assertEquals(data[1], 194) # frame length
+ # Item 1 (device token)
+ data = struct.unpack("!BH32s", rawData[5:40])
+ self.assertEquals(data[0], 1)
+ self.assertEquals(data[1], 32)
+ self.assertEquals(data[2].encode("hex"), token.replace(" ", "")) # token
+ # Item 2 (payload)
+ data = struct.unpack("!BH", rawData[40:43])
+ self.assertEquals(data[0], 2)
+ payloadLength = data[1]
+ self.assertEquals(payloadLength, 138)
+ payload = struct.unpack("!%ds" % (payloadLength,), rawData[43:181])
payload = json.loads(payload[0])
self.assertEquals(payload["key"], u"/CalDAV/calendars.example.com/user01/calendar/")
self.assertEquals(payload["dataChangedTimestamp"], dataChangedTimestamp)
self.assertTrue("pushRequestSubmittedTimestamp" in payload)
+ # Item 3 (notification id)
+ data = struct.unpack("!BHI", rawData[181:188])
+ self.assertEquals(data[0], 3)
+ self.assertEquals(data[1], 4)
+ self.assertEquals(data[2], 2)
+ # Item 4 (expiration)
+ data = struct.unpack("!BHI", rawData[188:195])
+ self.assertEquals(data[0], 4)
+ self.assertEquals(data[1], 4)
+ # Item 5 (priority)
+ data = struct.unpack("!BHB", rawData[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.high.value)
+
# Verify token history is updated
self.assertTrue(token in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
self.assertTrue(token2 in [t for (_ignore_i, t) in providerConnector.service.protocol.history.history])
@@ -160,14 +186,21 @@
providerConnector.transport.data = None
# Send notification while service is connected
txn = self._sqlCalendarStore.newTransaction()
- yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/")
+ yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
+ priority=PushPriority.low)
yield txn.commit()
clock.advance(1) # so that first push is sent
- self.assertEquals(len(providerConnector.transport.data), 183)
+ self.assertEquals(len(providerConnector.transport.data), 199)
+ # Ensure that the priority is "low"
+ data = struct.unpack("!BHB", providerConnector.transport.data[195:199])
+ self.assertEquals(data[0], 5)
+ self.assertEquals(data[1], 1)
+ self.assertEquals(data[2], ApplePushPriority.low.value)
+
# Reset sent data
providerConnector.transport.data = None
clock.advance(3) # so that second push is sent
- self.assertEquals(len(providerConnector.transport.data), 183)
+ self.assertEquals(len(providerConnector.transport.data), 199)
history = []
Modified: CalendarServer/trunk/calendarserver/push/test/test_notifier.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_notifier.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/test/test_notifier.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -22,6 +22,8 @@
from twistedcaldav.config import ConfigDict
from txdav.common.datastore.test.util import populateCalendarsFrom
from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
+from calendarserver.push.util import PushPriority
+from txdav.idav import ChangeCategory
class StubService(object):
@@ -33,8 +35,9 @@
self.history = []
- def enqueue(self, transaction, id):
- self.history.append(id)
+ def enqueue(self, transaction, id, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((id, priority))
return(succeed(None))
@@ -45,8 +48,8 @@
def test_enqueue(self):
stub = StubService()
dist = PushDistributor([stub])
- yield dist.enqueue(None, "testing")
- self.assertEquals(stub.history, ["testing"])
+ yield dist.enqueue(None, "testing", PushPriority.high)
+ self.assertEquals(stub.history, [("testing", PushPriority.high)])
def test_getPubSubAPSConfiguration(self):
@@ -91,8 +94,9 @@
self.history = []
- def enqueue(self, transaction, pushID):
- self.history.append(pushID)
+ def enqueue(self, transaction, pushID, dataChangedTimestamp=None,
+ priority=None):
+ self.history.append((pushID, priority))
@@ -111,35 +115,62 @@
txn = self._sqlCalendarStore.newTransaction()
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/foo/",
+ priority=PushPriority.high.value
))
yield txn.commit()
yield wp.whenExecuted()
- self.assertEquals(pushDistributor.history, ["/CalDAV/localhost/foo/"])
+ self.assertEquals(pushDistributor.history,
+ [("/CalDAV/localhost/foo/", PushPriority.high)])
pushDistributor.reset()
txn = self._sqlCalendarStore.newTransaction()
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
))
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
))
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
))
# Enqueue a different pushID to ensure those are not grouped with
# the others:
wp = (yield txn.enqueue(PushNotificationWork,
pushID="/CalDAV/localhost/baz/",
+ priority=PushPriority.high.value
))
yield txn.commit()
yield wp.whenExecuted()
+ self.assertEquals(set(pushDistributor.history),
+ set([("/CalDAV/localhost/bar/", PushPriority.high),
+ ("/CalDAV/localhost/baz/", PushPriority.high)]))
+
+ # Ensure only the high-water-mark priority push goes out, by
+ # enqueuing low, medium, and high notifications
+ pushDistributor.reset()
+ txn = self._sqlCalendarStore.newTransaction()
+ wp = (yield txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.low.value
+ ))
+ wp = (yield txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.high.value
+ ))
+ wp = (yield txn.enqueue(PushNotificationWork,
+ pushID="/CalDAV/localhost/bar/",
+ priority=PushPriority.medium.value
+ ))
+ yield txn.commit()
+ yield wp.whenExecuted()
self.assertEquals(pushDistributor.history,
- ["/CalDAV/localhost/bar/", "/CalDAV/localhost/baz/"])
+ [("/CalDAV/localhost/bar/", PushPriority.high)])
-
class NotifierFactory(StoreTestCase):
requirements = {
@@ -168,8 +199,9 @@
def test_homeNotifier(self):
home = yield self.homeUnderTest()
- yield home.notifyChanged()
- self.assertEquals(self.notifierFactory.history, ["/CalDAV/example.com/home1/"])
+ yield home.notifyChanged(category=ChangeCategory.default)
+ self.assertEquals(self.notifierFactory.history,
+ [("/CalDAV/example.com/home1/", PushPriority.high)])
yield self.commit()
@@ -177,10 +209,12 @@
def test_calendarNotifier(self):
calendar = yield self.calendarUnderTest()
- yield calendar.notifyChanged()
+ yield calendar.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
)
yield self.commit()
@@ -194,9 +228,9 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
])
)
yield self.commit()
@@ -207,9 +241,9 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home2/"
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home2/", PushPriority.high),
])
)
yield self.commit()
@@ -225,10 +259,12 @@
self.notifierFactory.reset()
shared = yield self.calendarUnderTest(home="home2", name=shareName)
- yield shared.notifyChanged()
+ yield shared.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/calendar_1/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high)])
)
yield self.commit()
@@ -237,9 +273,11 @@
def test_notificationNotifier(self):
notifications = yield self.transactionUnderTest().notificationsWithUID("home1")
- yield notifications.notifyChanged()
+ yield notifications.notifyChanged(category=ChangeCategory.default)
self.assertEquals(
set(self.notifierFactory.history),
- set(["/CalDAV/example.com/home1/", "/CalDAV/example.com/home1/notification/"])
+ set([
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high)])
)
yield self.commit()
Modified: CalendarServer/trunk/calendarserver/push/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/util.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/push/util.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -16,7 +16,20 @@
from OpenSSL import crypto
from twext.python.log import Logger
+from twisted.python.constants import Values, ValueConstant
+
+
+class PushPriority(Values):
+ """
+ Constants to use for push priorities
+ """
+ low = ValueConstant(1)
+ medium = ValueConstant(5)
+ high = ValueConstant(10)
+
+
+
def getAPNTopicFromCertificate(certPath):
"""
Given the path to a certificate, extract the UID value portion of the
@@ -128,7 +141,7 @@
self.staggerSeconds = staggerSeconds
- def schedule(self, tokens, key, dataChangedTimestamp):
+ def schedule(self, tokens, key, dataChangedTimestamp, priority):
"""
Schedules a batch of notifications for the given tokens, staggered
with self.staggerSeconds between each one. Duplicates are ignored,
@@ -151,13 +164,14 @@
(internalKey,))
else:
self.outstanding[internalKey] = self.reactor.callLater(
- scheduleTime, self.send, token, key, dataChangedTimestamp)
+ scheduleTime, self.send, token, key, dataChangedTimestamp,
+ priority)
self.log.debug("PushScheduler scheduled: %s in %.0f sec" %
(internalKey, scheduleTime))
scheduleTime += self.staggerSeconds
- def send(self, token, key, dataChangedTimestamp):
+ def send(self, token, key, dataChangedTimestamp, priority):
"""
This method is what actually gets scheduled. Its job is to remove
its corresponding entry from the outstanding dict and call the
@@ -173,7 +187,7 @@
"""
self.log.debug("PushScheduler fired for %s %s %d" % (token, key, dataChangedTimestamp))
del self.outstanding[(token, key)]
- return self.callback(token, key, dataChangedTimestamp)
+ return self.callback(token, key, dataChangedTimestamp, priority)
def stop(self):
Modified: CalendarServer/trunk/calendarserver/tools/ampnotifications.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/ampnotifications.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/tools/ampnotifications.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -141,8 +141,8 @@
-def notificationCallback(id, dataChangedTimestamp):
- print("Received notification for:", id)
+def notificationCallback(id, dataChangedTimestamp, priority):
+ print("Received notification for:", id, "Priority", priority)
return succeed(True)
Modified: CalendarServer/trunk/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/gateway.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -156,6 +156,7 @@
'ZIP' : { 'extras' : True, 'attr' : 'zip', },
'Country' : { 'extras' : True, 'attr' : 'country', },
'Phone' : { 'extras' : True, 'attr' : 'phone', },
+ 'Geo' : { 'extras' : True, 'attr' : 'geo', },
'AutoSchedule' : { 'attr' : 'autoSchedule', },
'AutoAcceptGroup' : { 'attr' : 'autoAcceptGroup', },
}
Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -193,6 +193,7 @@
self.assertEquals(record.extras["zip"], "95014")
self.assertEquals(record.extras["country"], "USA")
self.assertEquals(record.extras["phone"], "(408) 555-1212")
+ self.assertEquals(record.extras["geo"], "geo:37.331,-122.030")
results = yield self.runCommand(command_getLocationAttributes)
self.assertEquals(set(results["result"]["ReadProxies"]), set(['user03', 'user04']))
@@ -424,6 +425,8 @@
<string>USA</string>
<key>Phone</key>
<string>(408) 555-1212</string>
+ <key>Geo</key>
+ <string>geo:37.331,-122.030</string>
<key>ReadProxies</key>
<array>
<string>users:user03</string>
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -89,6 +89,8 @@
InvalidUIDError, UIDExistsError, UIDExistsElsewhereError, \
InvalidResourceMove, InvalidComponentForStoreError
+from txdav.idav import ChangeCategory
+
from pycalendar.datetime import DateTime
from pycalendar.duration import Duration
from pycalendar.timezone import Timezone
@@ -2192,8 +2194,18 @@
else:
yield self._calendar._updateRevision(self._name)
- yield self._calendar.notifyChanged()
+ # Determine change category
+ category = ChangeCategory.default
+ if internal_state == ComponentUpdateState.INBOX:
+ category = ChangeCategory.inbox
+ elif internal_state == ComponentUpdateState.ORGANIZER_ITIP_UPDATE:
+ category = ChangeCategory.organizerITIPUpdate
+ elif (internal_state == ComponentUpdateState.ATTENDEE_ITIP_UPDATE and
+ hasattr(self._txn, "doing_attende_refresh")):
+ category = ChangeCategory.attendeeITIPUpdate
+ yield self._calendar.notifyChanged(category=category)
+
# Finally check if a split is needed
if internal_state not in (ComponentUpdateState.SPLIT_OWNER, ComponentUpdateState.SPLIT_ATTENDEE,) and schedule_state == "organizer":
yield self.checkSplit()
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -53,7 +53,9 @@
from txdav.common.icommondatastore import ConcurrentModification
from twistedcaldav.ical import Component
from twistedcaldav.config import config
+from calendarserver.push.util import PushPriority
+
storePath = FilePath(__file__).parent().child("calendar_store")
homeRoot = storePath.child("ho").child("me").child("home1")
@@ -456,8 +458,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
])
)
yield self.commit()
@@ -474,8 +476,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/notification/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/notification/", PushPriority.high),
])
)
yield self.commit()
@@ -698,7 +700,7 @@
calendarProperties = (yield home.calendarWithName(name)).properties()
self.assertEqual(len(calendarProperties), 0)
# notify is called prior to commit
- self.assertTrue("/CalDAV/example.com/home1/" in self.notifierFactory.history)
+ self.assertTrue(("/CalDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
yield self.commit()
# Make sure it's available in a new transaction; i.e. test the commit.
@@ -741,10 +743,10 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
- "/CalDAV/example.com/home1/calendar_2/",
- "/CalDAV/example.com/home1/calendar_empty/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_2/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_empty/", PushPriority.high),
])
)
@@ -918,8 +920,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
@@ -1474,8 +1476,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
@@ -1593,8 +1595,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CalDAV/example.com/home1/",
- "/CalDAV/example.com/home1/calendar_1/",
+ ("/CalDAV/example.com/home1/", PushPriority.high),
+ ("/CalDAV/example.com/home1/calendar_1/", PushPriority.high),
])
)
yield self.commit()
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -39,7 +39,9 @@
from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError
from txdav.idav import IPropertyStore, IDataStore
from txdav.xml.element import WebDAVUnknownElement
+from calendarserver.push.util import PushPriority
+
storePath = FilePath(__file__).parent().child("addressbook_store")
home1Root = storePath.child("ho").child("me").child("home1")
@@ -372,7 +374,7 @@
yield home.removeAddressBookWithName(name)
self.assertNotIdentical((yield home.addressbookWithName(name)), None)
# notify is called prior to commit
- self.assertTrue("/CardDAV/example.com/home1/" in self.notifierFactory.history)
+ self.assertTrue(("/CardDAV/example.com/home1/", PushPriority.high) in self.notifierFactory.history)
yield self.commit()
# Make sure it's available in a new transaction; i.e. test the commit.
@@ -399,8 +401,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -532,8 +534,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -693,8 +695,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
@@ -809,8 +811,8 @@
self.assertEquals(
set(self.notifierFactory.history),
set([
- "/CardDAV/example.com/home1/",
- "/CardDAV/example.com/home1/addressbook/",
+ ("/CardDAV/example.com/home1/", PushPriority.high),
+ ("/CardDAV/example.com/home1/addressbook/", PushPriority.high),
])
)
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -73,6 +73,7 @@
from txdav.common.inotifications import INotificationCollection, \
INotificationObject
from txdav.xml.parser import WebDAVDocument
+from txdav.idav import ChangeCategory
from uuid import uuid4, UUID
@@ -2236,7 +2237,7 @@
@inlineCallbacks
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -2260,7 +2261,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
@classproperty
@@ -4297,11 +4298,11 @@
return self.ownerHome().notifierID()
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications when a child resource is changed.
"""
- return self._notifyChanged(property_change=False)
+ return self._notifyChanged(property_change=False, category=category)
def notifyPropertyChanged(self):
@@ -4312,7 +4313,8 @@
@inlineCallbacks
- def _notifyChanged(self, property_change=False):
+ def _notifyChanged(self, property_change=False,
+ category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -4348,7 +4350,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
@classproperty
@@ -5162,7 +5164,7 @@
@inlineCallbacks
- def notifyChanged(self):
+ def notifyChanged(self, category=ChangeCategory.default):
"""
Send notifications, change sync token and bump last modified because
the resource has changed. We ensure we only do this once per object
@@ -5181,7 +5183,7 @@
# push notifiers add their work items immediately
notifier = self._notifiers.get("push", None)
if notifier:
- yield notifier.notify(self._txn)
+ yield notifier.notify(self._txn, priority=category.value)
returnValue(None)
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql 2013-11-12 21:38:00 UTC (rev 11934)
@@ -347,7 +347,8 @@
create table PUSH_NOTIFICATION_WORK (
"WORK_ID" integer primary key not null,
"NOT_BEFORE" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
- "PUSH_ID" nvarchar2(255)
+ "PUSH_ID" nvarchar2(255),
+ "PRIORITY" integer not null
);
create table GROUP_CACHER_POLLING_WORK (
@@ -366,7 +367,7 @@
"VALUE" nvarchar2(255)
);
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '27');
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '28');
insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '2');
create index CALENDAR_HOME_METADAT_3cb9049e on CALENDAR_HOME_METADATA (
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql 2013-11-12 21:38:00 UTC (rev 11934)
@@ -663,7 +663,8 @@
create table PUSH_NOTIFICATION_WORK (
WORK_ID integer primary key default nextval('WORKITEM_SEQ') not null, -- implicit index
NOT_BEFORE timestamp default timezone('UTC', CURRENT_TIMESTAMP),
- PUSH_ID varchar(255) not null
+ PUSH_ID varchar(255) not null,
+ PRIORITY integer not null -- 1:low 5:medium 10:high
);
-----------------
@@ -698,6 +699,6 @@
VALUE varchar(255)
);
-insert into CALENDARSERVER values ('VERSION', '27');
+insert into CALENDARSERVER values ('VERSION', '28');
insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '5');
insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '2');
Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -726,8 +726,8 @@
return "/%s/%s/%s/" % (prefix, self.hostname, id)
- def send(self, prefix, id, txn):
- self.history.append(self.pushKeyForId(prefix, id))
+ def send(self, prefix, id, txn, priority):
+ self.history.append((self.pushKeyForId(prefix, id), priority))
def reset(self):
Modified: CalendarServer/trunk/txdav/idav.py
===================================================================
--- CalendarServer/trunk/txdav/idav.py 2013-11-12 00:22:37 UTC (rev 11933)
+++ CalendarServer/trunk/txdav/idav.py 2013-11-12 21:38:00 UTC (rev 11934)
@@ -34,6 +34,9 @@
from zope.interface import Attribute, Interface
from zope.interface.common.mapping import IMapping
+from twisted.python.constants import Values, ValueConstant
+from calendarserver.push.util import PushPriority
+
#
# Exceptions
#
@@ -231,6 +234,19 @@
+class ChangeCategory(Values):
+ """
+ Constants to use for notifyChanged's category parameter. Maps
+ types of changes to the appropriate push priority level.
+ TODO: make these values configurable in plist perhaps.
+ """
+ default = ValueConstant(PushPriority.high)
+ inbox = ValueConstant(PushPriority.medium)
+ attendeeITIPUpdate = ValueConstant(PushPriority.medium)
+ organizerITIPUpdate = ValueConstant(PushPriority.medium)
+
+
+
class INotifier(Interface):
"""
Interface for an object that can send change notifications. Notifiers are associated with specific notifier factories
@@ -260,9 +276,12 @@
@rtype: L{IStoreNotifier} or C{None}
"""
- def notifyChanged(): #@NoSelf
+ def notifyChanged(category): #@NoSelf
"""
Send a change notification to any notifiers assigned to the object.
+
+ @param category: the kind of change triggering this notification
+ @type: L{ChangeCategory}
"""
def notifierID(): #@NoSelf
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/5ec58497/attachment.html>
More information about the calendarserver-changes
mailing list