Revision: 4121 http://trac.macosforge.org/projects/calendarserver/changeset/4121 Author: sagen@apple.com Date: 2009-04-30 16:09:50 -0700 (Thu, 30 Apr 2009) Log Message: ----------- Replacing the email-address index with a calendar-user-address index, to allow multiple records with the same email address, but not calendar user address. Also, we only index (internally and in memcached) on the attribute+value that we faulted the record in on, in order to avoid index collisions. Modified Paths: -------------- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py CalendarServer/trunk/twistedcaldav/directory/cachingappleopendirectory.py CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py CalendarServer/trunk/twistedcaldav/directory/cachingxmlfile.py CalendarServer/trunk/twistedcaldav/directory/directory.py CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -42,6 +42,7 @@ from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord from twistedcaldav.directory.directory import DirectoryError, UnknownRecordTypeError +from twistedcaldav.scheduling.cuaddress import normalizeCUAddr class OpenDirectoryService(DirectoryService): """ @@ -280,9 +281,17 @@ self._storage(recordType)["disabled names"].add(shortName) return record - def recordWithEmailAddress(self, emailAddress): - return self._recordWithAttribute("emails", "disabled emails", "email", emailAddress) + def recordWithCalendarUserAddress(self, address): + address = normalizeCUAddr(address) + record = None + if address.startswith("urn:uuid:"): + guid = address[9:] + record = self.recordWithGUID(guid) + elif address.startswith("mailto:"): + record = self._recordWithAttribute("cuas", "disabled cuas", "cua", address) + return record if record and record.enabledForCalendaring else None + def recordWithGUID(self, guid): return self._recordWithAttribute("guids", "disabled guids", "guid", guid) @@ -510,13 +519,13 @@ records = {} guids = {} authIDs = {} - emails = {} + cuas = {} disabledNames = set() disabledGUIDs = set() disabledAuthIDs = set() - disabledEmails = set() - + disabledCUAs = set() + if recordType == self.recordType_groups: groupsForGUID = {} elif recordType in (self.recordType_resources, self.recordType_locations): @@ -528,13 +537,13 @@ records = storage["records"] guids = storage["guids"] authIDs = storage["authIDs"] - emails = storage["emails"] + cuas = storage["cuas"] disabledNames = storage["disabled names"] disabledGUIDs = storage["disabled guids"] disabledAuthIDs = storage["disabled authIDs"] - disabledEmails = storage["disabled emails"] - + disabledCUAs = storage["disabled cuas"] + if recordType == self.recordType_groups: groupsForGUID = storage["groupsForGUID"] elif recordType in (self.recordType_resources, self.recordType_locations): @@ -657,7 +666,7 @@ disabledGUIDs.add(guid) disabledNames.update(record.shortNames) disabledAuthIDs.update(record.authIDs) - disabledEmails.update(record.emailAddresses) + disabledCUAs.update(record.calendarUserAddresses) if guid in guids: try: @@ -674,9 +683,9 @@ del authIDs[authID] except KeyError: pass - for email in record.emailAddresses: + for cua in record.calendarUserAddresses: try: - del emails[email] + del cuas[cua] except KeyError: pass @@ -747,28 +756,31 @@ else: authIDs[authID] = record - # Index non-duplicate emails - def disableEmail(emailAddress, record): - self.log_warn("Email address %s disabled due to conflict for record: %s" - % (emailAddress, record)) + # Index non-duplicate CUAs + def disableCUA(cua, record): + self.log_warn("CUA %s disabled due to conflict for record: %s" + % (cua, record)) - record.emailAddresses.remove(emailAddress) - disabledEmails.add(emailAddress) + record.calendarUserAddresses.remove(cua) + disabledCUAs.add(cua) - if emailAddress in emails: - del emails[emailAddress] + if cua in cuas: + del cuas[cua] + + if cua in records: + del records[cua] - for email in frozenset(recordEmailAddresses): - if email in disabledEmails: - disableEmail(email, record) + for cua in frozenset(calendarUserAddresses): + if cua in disabledCUAs: + disableCUA(cua, record) else: # Check for duplicates - existing_record = emails.get(email) + existing_record = cuas.get(cua) if existing_record is not None: - disableEmail(email, record) - disableEmail(email, existing_record) + disableCUA(cua, record) + disableCUA(cua, existing_record) else: - emails[email] = record + cuas[cua] = record if lookup is None: # @@ -779,11 +791,11 @@ "records" : records, "guids" : guids, "authIDs" : authIDs, - "emails" : emails, + "cuas" : cuas, "disabled names" : disabledNames, "disabled guids" : disabledGUIDs, "disabled authIDs" : disabledAuthIDs, - "disabled emails" : disabledEmails, + "disabled cuas" : disabledCUAs, } # Add group indexing if needed @@ -819,6 +831,7 @@ "Added %d (%d enabled) records to %s OD record cache; expires in %d seconds" % (len(self._records[recordType]["guids"]), enabled_count, recordType, cacheTimeout) ) + print self._records[recordType] def _queryDirectory(self, recordType, lookup=None): attrs = [ @@ -891,15 +904,28 @@ query = None if lookup is not None: - queryattr = { - "shortName" : dsattributes.kDSNAttrRecordName, - "guid" : dsattributes.kDS1AttrGeneratedUID, - "authID" : dsattributes.kDSNAttrAltSecurityIdentities, - "email" : dsattributes.kDSNAttrEMailAddress, - }.get(lookup[0]) - assert queryattr is not None, "Invalid type for record faulting query" - query = dsquery.match(queryattr, lookup[1], dsattributes.eDSExact) + indexType, indexKey = lookup + origIndexKey = indexKey + if indexType == "cua": + # The directory doesn't contain CUAs, so we need to convert + # the CUA to the appropriate field name and value: + queryattr, indexKey = cuAddressConverter(indexKey) + # queryattr will be one of: + # guid, emailAddresses, or recordName + # ...which will need to be mapped to DS + queryattr = self._ODFields[queryattr]['odField'] + + else: + queryattr = { + "shortName" : dsattributes.kDSNAttrRecordName, + "guid" : dsattributes.kDS1AttrGeneratedUID, + "authID" : dsattributes.kDSNAttrAltSecurityIdentities, + }.get(indexType) + assert queryattr is not None, "Invalid type for record faulting query" + + query = dsquery.match(queryattr, indexKey, dsattributes.eDSExact) + try: if query: self.log_debug("opendirectory.queryRecordsWithAttribute_list(%r,%r,%r,%r,%r,%r,%r)" % ( Modified: CalendarServer/trunk/twistedcaldav/directory/cachingappleopendirectory.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/cachingappleopendirectory.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/cachingappleopendirectory.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -44,6 +44,7 @@ CachingDirectoryRecord from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord from twistedcaldav.directory.directory import DirectoryError, UnknownRecordTypeError +from twistedcaldav.directory.principal import cuAddressConverter class OpenDirectoryService(CachingDirectoryService): """ @@ -444,6 +445,26 @@ dsattributes.kDSNAttrMetaNodeLocation, ] + origIndexKey = indexKey + if indexType == self.INDEX_TYPE_CUA: + # The directory doesn't contain CUAs, so we need to convert + # the CUA to the appropriate field name and value: + queryattr, indexKey = cuAddressConverter(indexKey) + # queryattr will be one of: + # guid, emailAddresses, or recordName + # ...which will need to be mapped to DS + queryattr = self._ODFields[queryattr]['odField'] + + else: + queryattr = { + self.INDEX_TYPE_SHORTNAME : dsattributes.kDSNAttrRecordName, + self.INDEX_TYPE_GUID : dsattributes.kDS1AttrGeneratedUID, + }.get(indexType) + assert queryattr is not None, "Invalid type for record faulting query" + + query = dsquery.match(queryattr, indexKey, dsattributes.eDSExact) + + listRecordTypes = [] for recordType in recordTypes: if recordType == DirectoryService.recordType_users: @@ -455,15 +476,13 @@ attrs.append(dsattributes.kDSNAttrNestedGroups) elif recordType == DirectoryService.recordType_locations: - # Email addresses and locations don't mix - if indexType != self.INDEX_TYPE_EMAIL: + if queryattr != dsattributes.kDSNAttrEMailAddress: listRecordTypes.append(dsattributes.kDSStdRecordTypePlaces) # MOR: possibly can be removed attrs.append(dsattributes.kDSNAttrResourceInfo) elif recordType == DirectoryService.recordType_resources: - # Email addresses and resources don't mix - if indexType != self.INDEX_TYPE_EMAIL: + if queryattr != dsattributes.kDSNAttrEMailAddress: listRecordTypes.append(dsattributes.kDSStdRecordTypeResources) # MOR: possibly can be removed attrs.append(dsattributes.kDSNAttrResourceInfo) @@ -471,13 +490,6 @@ else: raise UnknownRecordTypeError("Unknown Open Directory record type: %s" % (recordType)) - queryattr = { - self.INDEX_TYPE_SHORTNAME : dsattributes.kDSNAttrRecordName, - self.INDEX_TYPE_GUID : dsattributes.kDS1AttrGeneratedUID, - self.INDEX_TYPE_EMAIL : dsattributes.kDSNAttrEMailAddress, - }.get(indexType) - assert queryattr is not None, "Invalid type for record faulting query" - query = dsquery.match(queryattr, indexKey, dsattributes.eDSExact) try: self.log_debug("opendirectory.queryRecordsWithAttribute_list(%r,%r,%r,%r,%r,%r,%r)" % ( @@ -508,8 +520,6 @@ self.log_error("Open Directory (node=%s) error: %s" % (self.realmName, str(ex))) raise - enabled_count = 0 - def _uniqueTupleFromAttribute(attribute): if attribute: if isinstance(attribute, str): @@ -528,7 +538,10 @@ return set([item.lower() if lower else item for item in attribute]) else: return () - + + enabledRecords = [] + disabledRecords = [] + for (recordShortName, value) in results: # Now get useful record info. @@ -607,7 +620,6 @@ enabledForCalendaring = True if enabledForCalendaring: - enabled_count += 1 calendarUserAddresses = self._calendarUserAddresses(recordType, value) else: # Some records we want to keep even though they are not enabled for calendaring. @@ -658,8 +670,21 @@ enabledForCalendaring = enabledForCalendaring, memberGUIDs = memberGUIDs, ) - self.recordCacheForType(recordType).addRecord(record) + if enabledForCalendaring: + enabledRecords.append(record) + else: + disabledRecords.append(record) + record = None + if len(enabledRecords) == 1: + record = enabledRecords[0] + elif len(enabledRecords) == 0 and len(disabledRecords) == 1: + record = disabledRecords[0] + + if record: + self.log_debug("Storing (%s %s) %s in internal cache" % (indexType, origIndexKey, record)) + self.recordCacheForType(recordType).addRecord(record, indexType, origIndexKey) + def _parseResourceInfo(self, plist, guid, recordType, shortname): """ Parse OD ResourceInfo attribute and extract information that the server needs. Modified: CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/cachingdirectory.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -30,8 +30,10 @@ import memcacheclient import base64 -from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError from twistedcaldav.config import config +from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError +from twistedcaldav.log import LoggingMixIn +from twistedcaldav.scheduling.cuaddress import normalizeCUAddr class RecordTypeCache(object): @@ -44,19 +46,16 @@ self.directoryService = directoryService self.recordType = recordType - def addRecord(self, record): + def addRecord(self, record, indexType, indexKey, useMemcache=True): raise NotImplementedError() def removeRecord(self, record): raise NotImplementedError() - def replaceRecord(self, oldRecord, newRecord): - raise NotImplementedError() - def findRecord(self, indexType, indexKey): raise NotImplementedError() -class DictRecordTypeCache(RecordTypeCache): +class DictRecordTypeCache(RecordTypeCache, LoggingMixIn): """ Cache implementation using a dict. Does not share the cache with other instances. """ @@ -68,28 +67,23 @@ self.recordsIndexedBy = { CachingDirectoryService.INDEX_TYPE_GUID : {}, CachingDirectoryService.INDEX_TYPE_SHORTNAME: {}, - CachingDirectoryService.INDEX_TYPE_EMAIL : {}, + CachingDirectoryService.INDEX_TYPE_CUA : {}, CachingDirectoryService.INDEX_TYPE_AUTHID : {}, } - def addRecord(self, record): - + def addRecord(self, record, indexType, indexKey, useMemcache=True): + + useMemcache == useMemcache and config.Memcached.ClientEnabled + self.records.add(record) - for indexType in self.directoryService.indexTypes(): - try: - indexData = getattr(record, CachingDirectoryService.indexTypeToRecordAttribute[indexType]) - except AttributeError: - continue - if isinstance(indexData, str): - indexData = (indexData,) - if type(indexData) in (types.ListType, types.TupleType, set): - for item in indexData: - self.recordsIndexedBy[indexType][item] = record - elif indexData is None: - pass - else: - raise AssertionError("Data from record attribute must be str, list or tuple") - + + self.recordsIndexedBy[indexType][indexKey] = record + if useMemcache: + key = "dir|%s|%s" % (indexType, indexKey) + self.log_debug("Memcache: storing %s" % (key,)) + self.directoryService.memcacheSet(key, record) + + def removeRecord(self, record): if record in self.records: @@ -110,10 +104,6 @@ else: raise AssertionError("Data from record attribute must be str, list or tuple") - def replaceRecord(self, oldRecord, newRecord): - self.removeRecord(oldRecord) - self.addRecord(newRecord) - def findRecord(self, indexType, indexKey): return self.recordsIndexedBy[indexType].get(indexKey) @@ -126,13 +116,13 @@ INDEX_TYPE_GUID = "guid" INDEX_TYPE_SHORTNAME = "shortname" - INDEX_TYPE_EMAIL = "email" + INDEX_TYPE_CUA = "cua" INDEX_TYPE_AUTHID = "authid" indexTypeToRecordAttribute = { "guid" : "guid", "shortname": "shortNames", - "email" : "emailAddresses", + "cua" : "calendarUserAddresses", "authid" : "authIDs", } @@ -209,7 +199,7 @@ return ( CachingDirectoryService.INDEX_TYPE_GUID, CachingDirectoryService.INDEX_TYPE_SHORTNAME, - CachingDirectoryService.INDEX_TYPE_EMAIL, + CachingDirectoryService.INDEX_TYPE_CUA, CachingDirectoryService.INDEX_TYPE_AUTHID, ) @@ -222,9 +212,17 @@ def recordWithShortName(self, recordType, shortName): return self._lookupRecord((recordType,), CachingDirectoryService.INDEX_TYPE_SHORTNAME, shortName) - def recordWithEmailAddress(self, emailAddress): - return self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_EMAIL, emailAddress) + def recordWithCalendarUserAddress(self, address): + address = normalizeCUAddr(address) + record = None + if address.startswith("urn:uuid:"): + guid = address[9:] + record = self.recordWithGUID(guid) + elif address.startswith("mailto:"): + record = self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_CUA, address) + return record if record and record.enabledForCalendaring else None + def recordWithAuthID(self, authID): return self._lookupRecord(None, CachingDirectoryService.INDEX_TYPE_AUTHID, authID) @@ -273,7 +271,7 @@ self.log_debug("Memcache: miss %s" % (key,)) else: self.log_debug("Memcache: hit %s" % (key,)) - self.recordCacheForType(record.recordType).addRecord(record) + self.recordCacheForType(record.recordType).addRecord(record, indexType, indexKey, useMemcache=False) return record # Check negative memcache @@ -291,24 +289,6 @@ record = lookup() if record: self.log_debug("Found record for attribute '%s' with value '%s'" % (indexType, indexKey,)) - - if config.Memcached.ClientEnabled: - # share with others via memcache - for shortName in record.shortNames: - key = "dir|%s|%s" % (CachingDirectoryService.INDEX_TYPE_SHORTNAME, shortName) - self.log_debug("Memcache: storing %s" % (key,)) - self.memcacheSet(key, record) - for emailAddress in record.emailAddresses: - key = "dir|%s|%s" % (CachingDirectoryService.INDEX_TYPE_EMAIL, emailAddress) - self.log_debug("Memcache: storing %s" % (key,)) - self.memcacheSet(key, record) - for authID in record.authIDs: - key = "dir|%s|%s" % (CachingDirectoryService.INDEX_TYPE_AUTHID, authID) - self.log_debug("Memcache: storing %s" % (key,)) - self.memcacheSet(key, record) - key = "dir|%s|%s" % (CachingDirectoryService.INDEX_TYPE_GUID, record.guid) - self.log_debug("Memcache: storing %s" % (key,)) - self.memcacheSet(key, record) return record Modified: CalendarServer/trunk/twistedcaldav/directory/cachingxmlfile.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/cachingxmlfile.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/cachingxmlfile.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -76,8 +76,8 @@ matched = indexKey == xmlPrincipal.guid elif indexType == self.INDEX_TYPE_SHORTNAME: matched = indexKey in xmlPrincipal.shortNames - elif indexType == self.INDEX_TYPE_EMAIL: - matched = indexKey in xmlPrincipal.emailAddresses + elif indexType == self.INDEX_TYPE_CUA: + matched = indexKey in xmlPrincipal.calendarUserAddresses if matched: record = XMLDirectoryRecord( Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/directory.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/directory.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -149,18 +149,14 @@ guid = address[9:] record = self.recordWithGUID(guid) elif address.startswith("mailto:"): - email = address[7:] - record = self.recordWithEmailAddress(email) + for record in self.allRecords(): + if address in record.calendarUserAddresses: + break + else: + return None return record if record and record.enabledForCalendaring else None - def recordWithEmailAddress(self, email): - for record in self.allRecords(): - if email in record.emailAddresses: - return record - - return None - def allRecords(self): for recordType in self.recordTypes(): for record in self.listRecords(recordType): Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/test/test_cachedirectory.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -42,7 +42,7 @@ cacheIt = False if indexType in ( CachingDirectoryService.INDEX_TYPE_SHORTNAME, - CachingDirectoryService.INDEX_TYPE_EMAIL, + CachingDirectoryService.INDEX_TYPE_CUA, CachingDirectoryService.INDEX_TYPE_AUTHID, ): if indexKey in record[indexType]: @@ -62,10 +62,11 @@ firstName = "", lastName = "", emailAddresses = record.get("email"), - calendarUserAddresses = record.get("email"), + calendarUserAddresses = record.get("cua"), enabledForCalendaring = True, ) - self.recordCacheForType(recordType).addRecord(cacheRecord) + self.recordCacheForType(recordType).addRecord(cacheRecord, + indexType, indexKey) class CachingDirectoryTest(TestCase): @@ -109,6 +110,7 @@ "guid": guid, "shortname": shortNames, "email": emails, + "cua": tuple(["mailto:%s" % email for email in emails]), "authid": tuple(["Kerberos:%s" % email for email in emails]) } @@ -202,8 +204,8 @@ def test_cacheoneemail(self): self.dummyRecords() - self.assertTrue(self.service.recordWithEmailAddress( - "user03@example.com" + self.assertTrue(self.service.recordWithCalendarUserAddress( + "mailto:user03@example.com" ) is not None) self.assertTrue(self.service.queried) self.verifyRecords(DirectoryService.recordType_users, set(( @@ -215,8 +217,8 @@ # Make sure it really is cached and won't cause another query self.service.queried = False - self.assertTrue(self.service.recordWithEmailAddress( - "user03@example.com" + self.assertTrue(self.service.recordWithCalendarUserAddress( + "mailto:user03@example.com" ) is not None) self.assertFalse(self.service.queried) Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py =================================================================== --- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py 2009-04-30 21:49:32 UTC (rev 4120) +++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryrecords.py 2009-04-30 23:09:50 UTC (rev 4121) @@ -308,7 +308,7 @@ self.assertFalse(self.service.recordWithShortName(DirectoryService.recordType_users, "user02").authIDs) self.assertFalse(self.service.recordWithShortName(DirectoryService.recordType_users, "user03").authIDs) - def test_duplicateEmail(self): + def test_duplicateCUAs(self): self.loadRecords({ DirectoryService.recordType_users: [ fakeODRecord("User 01"), @@ -320,9 +320,9 @@ self.verifyRecords(DirectoryService.recordType_users, ("user01", "user02", "user03")) self.verifyDisabledRecords(DirectoryService.recordType_users, (), ()) - self.assertTrue (self.service.recordWithShortName(DirectoryService.recordType_users, "user01").emailAddresses) - self.assertFalse(self.service.recordWithShortName(DirectoryService.recordType_users, "user02").emailAddresses) - self.assertFalse(self.service.recordWithShortName(DirectoryService.recordType_users, "user03").emailAddresses) + self.assertTrue (self.service.recordWithShortName(DirectoryService.recordType_users, "user01").calendarUserAddresses) + self.assertFalse("mailto:shared@example.com" in self.service.recordWithShortName(DirectoryService.recordType_users, "user02").calendarUserAddresses) + self.assertFalse("mailto:shared@example.com" in self.service.recordWithShortName(DirectoryService.recordType_users, "user03").calendarUserAddresses) def test_duplicateRecords(self): self.loadRecords({ @@ -648,7 +648,7 @@ self.verifyQuery(self.service.recordWithAuthID, "Kerberos:location05@example.com") self.verifyNoQuery(self.service.recordWithAuthID, "Kerberos:location05@example.com") - def test_negativeCacheEmailAddress(self): + def test_negativeCacheCalendarUserAddress(self): self.loadRecords({ DirectoryService.recordType_users: [ fakeODRecord("User 01"), @@ -676,21 +676,22 @@ ], }) - self.assertTrue(self.service.recordWithEmailAddress("user01@example.com")) - self.verifyQuery(self.service.recordWithEmailAddress, "user05@example.com") - self.verifyNoQuery(self.service.recordWithEmailAddress, "user05@example.com") + self.assertTrue(self.service.recordWithCalendarUserAddress("mailto:user01@example.com")) + self.verifyQuery(self.service.recordWithCalendarUserAddress, "mailto:user05@example.com") + self.verifyNoQuery(self.service.recordWithCalendarUserAddress, "mailto:user05@example.com") - self.assertTrue(self.service.recordWithEmailAddress("group01@example.com")) - self.verifyQuery(self.service.recordWithEmailAddress, "group05@example.com") - self.verifyNoQuery(self.service.recordWithEmailAddress, "group05@example.com") + # Groups don't have CUAs + # self.assertTrue(self.service.recordWithCalendarUserAddress("mailto:group01@example.com")) + # self.verifyQuery(self.service.recordWithCalendarUserAddress, "mailto:group05@example.com") + # self.verifyNoQuery(self.service.recordWithCalendarUserAddress, "mailto:group05@example.com") - self.assertTrue(self.service.recordWithEmailAddress("resource01@example.com")) - self.verifyQuery(self.service.recordWithEmailAddress, "resource05@example.com") - self.verifyNoQuery(self.service.recordWithEmailAddress, "resource05@example.com") + self.assertTrue(self.service.recordWithCalendarUserAddress("mailto:resource01@example.com")) + self.verifyQuery(self.service.recordWithCalendarUserAddress, "mailto:resource05@example.com") + self.verifyNoQuery(self.service.recordWithCalendarUserAddress, "mailto:resource05@example.com") - self.assertTrue(self.service.recordWithEmailAddress("location01@example.com")) - self.verifyQuery(self.service.recordWithEmailAddress, "location05@example.com") - self.verifyNoQuery(self.service.recordWithEmailAddress, "location05@example.com") + self.assertTrue(self.service.recordWithCalendarUserAddress("mailto:location01@example.com")) + self.verifyQuery(self.service.recordWithCalendarUserAddress, "mailto:location05@example.com") + self.verifyNoQuery(self.service.recordWithCalendarUserAddress, "mailto:location05@example.com")
participants (1)
-
source_changes@macosforge.org