<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[12167] CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/12167">12167</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-12-20 08:02:05 -0800 (Fri, 20 Dec 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Checkpoint: cross-pod searching.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerybuilderpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequeryfilterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerytesttest_filterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastoresqlpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequeryfilterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequerytesttest_filterpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastoresqlpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingconduitpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingtesttest_conduitpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastoresql_externalpy">CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerybuilderpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/builder.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -94,7 +94,7 @@
</span><span class="cx"> logical = expression.andExpression if compfilter.filter_test == "allof" else expression.orExpression
</span><span class="cx">
</span><span class="cx"> expressions = []
</span><del>- if isinstance(compfilter.filter_name, str):
</del><ins>+ if isinstance(compfilter.filter_name, str) or isinstance(compfilter.filter_name, unicode):
</ins><span class="cx"> expressions.append(expression.isExpression(fields["TYPE"], compfilter.filter_name, True))
</span><span class="cx"> else:
</span><span class="cx"> expressions.append(expression.inExpression(fields["TYPE"], compfilter.filter_name, True))
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequeryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -39,10 +39,44 @@
</span><span class="cx"> Determines which matching components are returned.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = None
+ deserialize_names = {}
+
+ @classmethod
+ def serialize_register(cls, register):
+ cls.deserialize_names[register.serialized_name] = register
+
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><del>- self.xmlelement = xml_element
</del><ins>+ pass
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @classmethod
+ def deserialize(cls, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ obj = cls.deserialize_names[data["type"]](None)
+ obj._deserialize(data)
+ return obj
+
+
+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ pass
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ return {
+ "type": self.serialized_name,
+ }
+
+
</ins><span class="cx"> def match(self, item, access=None):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><span class="lines">@@ -57,9 +91,13 @@
</span><span class="cx"> Determines which matching components are returned.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "Filter"
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(Filter, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> # One comp-filter element must be present
</span><span class="cx"> if len(xml_element.children) != 1 or xml_element.children[0].qname() != (caldav_namespace, "comp-filter"):
</span><span class="lines">@@ -68,6 +106,24 @@
</span><span class="cx"> self.child = ComponentFilter(xml_element.children[0])
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.child = FilterBase.deserialize(data["child"])
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(Filter, self).serialize()
+ result.update({
+ "child": self.child.serialize(),
+ })
+ return result
+
+
</ins><span class="cx"> def match(self, component, access=None):
</span><span class="cx"> """
</span><span class="cx"> Returns True if the given calendar component matches this filter, False
</span><span class="lines">@@ -148,8 +204,10 @@
</span><span class="cx">
</span><span class="cx"> return self.child.getmintimerange(None, False)
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(Filter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class FilterChildBase(FilterBase):
</span><span class="cx"> """
</span><span class="cx"> CalDAV filter element.
</span><span class="lines">@@ -158,6 +216,8 @@
</span><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(FilterChildBase, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> qualifier = None
</span><span class="cx"> filters = []
</span><span class="lines">@@ -205,6 +265,32 @@
</span><span class="cx"> self.filter_test = filter_test
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.qualifier = FilterBase.deserialize(data["qualifier"]) if data["qualifier"] else None
+ self.filters = [FilterBase.deserialize(filter) for filter in data["filters"]]
+ self.filter_name = data["filter_name"]
+ self.defined = data["defined"]
+ self.filter_test = data["filter_test"]
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(FilterChildBase, self).serialize()
+ result.update({
+ "qualifier": self.qualifier.serialize() if self.qualifier else None,
+ "filters": [filter.serialize() for filter in self.filters],
+ "filter_name": self.filter_name,
+ "defined": self.defined,
+ "filter_test": self.filter_test,
+ })
+ return result
+
+
</ins><span class="cx"> def match(self, item, access=None):
</span><span class="cx"> """
</span><span class="cx"> Returns True if the given calendar item (either a component, property or parameter value)
</span><span class="lines">@@ -235,6 +321,8 @@
</span><span class="cx"> Limits a search to only the chosen component types.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "ComponentFilter"
+
</ins><span class="cx"> def match(self, item, access):
</span><span class="cx"> """
</span><span class="cx"> Returns True if the given calendar item (which is a component)
</span><span class="lines">@@ -414,13 +502,17 @@
</span><span class="cx">
</span><span class="cx"> return currentMinimum, currentIsEndTime
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(ComponentFilter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class PropertyFilter (FilterChildBase):
</span><span class="cx"> """
</span><span class="cx"> Limits a search to specific properties.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "PropertyFilter"
+
</ins><span class="cx"> def _match(self, component, access):
</span><span class="cx"> # When access restriction is in force, we need to only allow matches against the properties
</span><span class="cx"> # allowed by the access restriction level.
</span><span class="lines">@@ -516,13 +608,17 @@
</span><span class="cx">
</span><span class="cx"> return currentMinimum, currentIsEndTime
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(PropertyFilter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ParameterFilter (FilterChildBase):
</span><span class="cx"> """
</span><span class="cx"> Limits a search to specific parameters.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "ParameterFilter"
+
</ins><span class="cx"> def _match(self, property, access):
</span><span class="cx">
</span><span class="cx"> # At least one parameter must match (or is-not-defined is set)
</span><span class="lines">@@ -534,13 +630,17 @@
</span><span class="cx">
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(ParameterFilter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class IsNotDefined (FilterBase):
</span><span class="cx"> """
</span><span class="cx"> Specifies that the named iCalendar item does not exist.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "IsNotDefined"
+
</ins><span class="cx"> def match(self, component, access=None):
</span><span class="cx"> # Oddly, this needs always to return True so that it appears there is
</span><span class="cx"> # a match - but we then "negate" the result if is-not-defined is set.
</span><span class="lines">@@ -548,6 +648,7 @@
</span><span class="cx"> # is-not-defined option.
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(IsNotDefined)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> class TextMatch (FilterBase):
</span><span class="lines">@@ -555,9 +656,13 @@
</span><span class="cx"> Specifies a substring match on a property or parameter value.
</span><span class="cx"> (CalDAV-access-09, section 9.6.4)
</span><span class="cx"> """
</span><ins>+ serialized_name = "TextMatch"
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(TextMatch, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> self.text = str(xml_element)
</span><span class="cx"> if "caseless" in xml_element.attributes:
</span><span class="lines">@@ -591,6 +696,30 @@
</span><span class="cx"> self.match_type = "contains"
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.text = data["text"]
+ self.caseless = data["caseless"]
+ self.negate = data["negate"]
+ self.match_type = data["match_type"]
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(TextMatch, self).serialize()
+ result.update({
+ "text": self.text,
+ "caseless": self.caseless,
+ "negate": self.negate,
+ "match_type": self.match_type,
+ })
+ return result
+
+
</ins><span class="cx"> def match(self, item, access):
</span><span class="cx"> """
</span><span class="cx"> Match the text for the item.
</span><span class="lines">@@ -637,16 +766,22 @@
</span><span class="cx">
</span><span class="cx"> return self.negate
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(TextMatch)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TimeRange (FilterBase):
</span><span class="cx"> """
</span><span class="cx"> Specifies a time for testing components against.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "TimeRange"
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(TimeRange, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> # One of start or end must be present
</span><span class="cx"> if "start" not in xml_element.attributes and "end" not in xml_element.attributes:
</span><span class="lines">@@ -657,6 +792,28 @@
</span><span class="cx"> self.tzinfo = None
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.start = DateTime.parseText(data["start"]) if data["start"] else None
+ self.end = DateTime.parseText(data["end"]) if data["end"] else None
+ self.tzinfo = Timezone(tzid=data["tzinfo"]) if data["tzinfo"] else None
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(TimeRange, self).serialize()
+ result.update({
+ "start": self.start.getText() if self.start else None,
+ "end": self.end.getText() if self.end else None,
+ "tzinfo": self.tzinfo.getTimezoneID() if self.tzinfo else None,
+ })
+ return result
+
+
</ins><span class="cx"> def settzinfo(self, tzinfo):
</span><span class="cx"> """
</span><span class="cx"> Set the default timezone to use with this query.
</span><span class="lines">@@ -752,3 +909,5 @@
</span><span class="cx"> return True
</span><span class="cx">
</span><span class="cx"> return False
</span><ins>+
+FilterBase.serialize_register(TimeRange)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastorequerytesttest_filterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/query/test/test_filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -24,12 +24,14 @@
</span><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.index_file import sqlcalendarquery
</span><span class="cx"> from txdav.caldav.datastore.query.builder import buildExpression
</span><del>-from txdav.caldav.datastore.query.filter import Filter
</del><ins>+from txdav.caldav.datastore.query.filter import Filter, FilterBase, TimeRange, \
+ PropertyFilter, TextMatch
</ins><span class="cx"> from txdav.caldav.datastore.query.generator import CalDAVSQLQueryGenerator
</span><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx">
</span><span class="cx"> from dateutil.tz import tzutc
</span><span class="cx"> import datetime
</span><ins>+from twistedcaldav.ical import Component
</ins><span class="cx">
</span><span class="cx"> class TestQueryFilter(TestCase):
</span><span class="cx">
</span><span class="lines">@@ -218,3 +220,216 @@
</span><span class="cx"> self.assertTrue(sql.find("TIMESPAN") == -1)
</span><span class="cx"> self.assertTrue(sql.find("TRANSPARENCY") == -1)
</span><span class="cx"> self.assertTrue("VEVENT" in args)
</span><ins>+
+
+
+class TestQueryFilterSerialize(TestCase):
+
+ def setUp(self):
+ super(TestQueryFilterSerialize, self).setUp()
+ TimezoneCache.create()
+
+
+ def test_query(self):
+ """
+ Basic query test - no time range
+ """
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[caldavxml.ComponentFilter(
+ **{"name":("VEVENT", "VFREEBUSY", "VAVAILABILITY")}
+ )],
+ **{"name": "VCALENDAR"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
+
+
+ def test_timerange_query(self):
+ """
+ Basic query test with time range
+ """
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[caldavxml.ComponentFilter(
+ *[caldavxml.TimeRange(**{"start":"20060605T160000Z", "end":"20060605T170000Z"})],
+ **{"name":("VEVENT", "VFREEBUSY", "VAVAILABILITY")}
+ )],
+ **{"name": "VCALENDAR"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
+ self.assertTrue(isinstance(f.child.filters[0].qualifier, TimeRange))
+ self.assertTrue(isinstance(f.child.filters[0].qualifier.tzinfo, Timezone))
+ self.assertEqual(f.child.filters[0].qualifier.tzinfo.getTimezoneID(), "America/New_York")
+
+
+ def test_query_not_extended(self):
+ """
+ Basic query test with time range
+ """
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[
+ caldavxml.ComponentFilter(
+ **{"name":("VEVENT")}
+ ),
+ caldavxml.ComponentFilter(
+ **{"name":("VTODO")}
+ ),
+ ],
+ **{"name": "VCALENDAR"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
+ self.assertEqual(len(f.child.filters), 2)
+
+
+ def test_query_extended(self):
+ """
+ Basic query test with time range
+ """
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[
+ caldavxml.ComponentFilter(
+ *[caldavxml.TimeRange(**{"start":"20060605T160000Z", })],
+ **{"name":("VEVENT")}
+ ),
+ caldavxml.ComponentFilter(
+ **{"name":("VTODO")}
+ ),
+ ],
+ **{"name": "VCALENDAR", "test": "anyof"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
+ self.assertEqual(len(f.child.filters), 2)
+ self.assertTrue(isinstance(f.child.filters[0].qualifier, TimeRange))
+
+
+ def test_query_text(self):
+ """
+ Basic query test with time range
+ """
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[
+ caldavxml.ComponentFilter(
+ caldavxml.PropertyFilter(
+ caldavxml.TextMatch.fromString("1234", False),
+ name="UID",
+ ),
+ **{"name":("VEVENT")}
+ ),
+ ],
+ **{"name": "VCALENDAR", "test": "anyof"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
+ self.assertTrue(isinstance(f.child.filters[0].filters[0], PropertyFilter))
+ self.assertTrue(isinstance(f.child.filters[0].filters[0].qualifier, TextMatch))
+ self.assertEqual(f.child.filters[0].filters[0].qualifier.text, "1234")
+
+
+
+class TestQueryFilterMatch(TestCase):
+
+ def setUp(self):
+ super(TestQueryFilterMatch, self).setUp()
+ TimezoneCache.create()
+
+
+ def test_vlarm_undefined(self):
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[caldavxml.ComponentFilter(
+ *[caldavxml.ComponentFilter(
+ caldavxml.IsNotDefined(),
+ **{"name":"VALARM"}
+ )],
+ **{"name":"VEVENT"}
+ )],
+ **{"name": "VCALENDAR"}
+ )
+ )
+ filter = Filter(filter)
+ filter.child.settzinfo(Timezone(tzid="America/New_York"))
+
+ self.assertFalse(filter.match(
+ Component.fromString("""BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T210412Z
+CREATED:20060102T150000Z
+DTSTART;TZID=US/Eastern:20130102T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=5
+SUMMARY:event 5
+UID:945113826375CBB89184DC36@ninevah.local
+CATEGORIES:cool,hot
+CATEGORIES:warm
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+"""
+ )))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/caldav/datastore/sql.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -1349,6 +1349,13 @@
</span><span class="cx"> C{name} is the resource name, C{uid} is the resource UID.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ # We might be passed an L{Filter} or a serialization of one
+ if isinstance(filter, dict):
+ try:
+ filter = Filter.deserialize(filter)
+ except Exception:
+ file = None
+
</ins><span class="cx"> # Make sure we have a proper Filter element and get the partial SQL statement to use.
</span><span class="cx"> sql_stmt = self._sqlquery(filter, useruid, fbtype)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequeryfilterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -34,10 +34,44 @@
</span><span class="cx"> Determines which matching components are returned.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = None
+ deserialize_names = {}
+
+ @classmethod
+ def serialize_register(cls, register):
+ cls.deserialize_names[register.serialized_name] = register
+
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><del>- self.xmlelement = xml_element
</del><ins>+ pass
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @classmethod
+ def deserialize(cls, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ obj = cls.deserialize_names[data["type"]](None)
+ obj._deserialize(data)
+ return obj
+
+
+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ pass
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ return {
+ "type": self.serialized_name,
+ }
+
+
</ins><span class="cx"> def match(self, item, access=None):
</span><span class="cx"> raise NotImplementedError
</span><span class="cx">
</span><span class="lines">@@ -52,9 +86,13 @@
</span><span class="cx"> Determines which matching components are returned.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "Filter"
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(Filter, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> filter_test = xml_element.attributes.get("test", "anyof")
</span><span class="cx"> if filter_test not in ("anyof", "allof"):
</span><span class="lines">@@ -65,6 +103,26 @@
</span><span class="cx"> self.children = [PropertyFilter(child) for child in xml_element.children]
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.filter_test = data["filter_test"]
+ self.child = [FilterBase.deserialize(child) for child in data["children"]]
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(Filter, self).serialize()
+ result.update({
+ "filter_test": self.filter_test,
+ "children": [child.serialize() for child in self.children],
+ })
+ return result
+
+
</ins><span class="cx"> def match(self, vcard):
</span><span class="cx"> """
</span><span class="cx"> Returns True if the given address property matches this filter, False
</span><span class="lines">@@ -96,8 +154,10 @@
</span><span class="cx"> else:
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(Filter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class FilterChildBase(FilterBase):
</span><span class="cx"> """
</span><span class="cx"> CardDAV filter element.
</span><span class="lines">@@ -106,6 +166,8 @@
</span><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(FilterChildBase, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> qualifier = None
</span><span class="cx"> filters = []
</span><span class="lines">@@ -147,6 +209,32 @@
</span><span class="cx"> self.defined = not self.qualifier or not isinstance(qualifier, IsNotDefined)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.propfilter_test = data["propfilter_test"]
+ self.qualifier = FilterBase.deserialize(data["qualifier"]) if data["qualifier"] else None
+ self.filters = [FilterBase.deserialize(filter) for filter in data["filters"]]
+ self.filter_name = data["filter_name"]
+ self.defined = data["defined"]
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(FilterChildBase, self).serialize()
+ result.update({
+ "propfilter_test": self.propfilter_test,
+ "qualifier": self.qualifier.serialize() if self.qualifier else None,
+ "filters": [filter.serialize() for filter in self.filters],
+ "filter_name": self.filter_name,
+ "defined": self.defined,
+ })
+ return result
+
+
</ins><span class="cx"> def match(self, item):
</span><span class="cx"> """
</span><span class="cx"> Returns True if the given address book item (either a property or parameter value)
</span><span class="lines">@@ -177,6 +265,8 @@
</span><span class="cx"> Limits a search to specific properties.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "PropertyFilter"
+
</ins><span class="cx"> def _match(self, vcard):
</span><span class="cx"> # At least one property must match (or is-not-defined is set)
</span><span class="cx"> for property in vcard.properties():
</span><span class="lines">@@ -198,13 +288,17 @@
</span><span class="cx"> # No tests
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(PropertyFilter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ParameterFilter (FilterChildBase):
</span><span class="cx"> """
</span><span class="cx"> Limits a search to specific parameters.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "ParameterFilter"
+
</ins><span class="cx"> def _match(self, property):
</span><span class="cx">
</span><span class="cx"> # At least one parameter must match (or is-not-defined is set)
</span><span class="lines">@@ -216,13 +310,17 @@
</span><span class="cx">
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(ParameterFilter)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class IsNotDefined (FilterBase):
</span><span class="cx"> """
</span><span class="cx"> Specifies that the named iCalendar item does not exist.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ serialized_name = "IsNotDefined"
+
</ins><span class="cx"> def match(self, component, access=None):
</span><span class="cx"> # Oddly, this needs always to return True so that it appears there is
</span><span class="cx"> # a match - but we then "negate" the result if is-not-defined is set.
</span><span class="lines">@@ -230,15 +328,21 @@
</span><span class="cx"> # is-not-defined option.
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+FilterBase.serialize_register(IsNotDefined)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TextMatch (FilterBase):
</span><span class="cx"> """
</span><span class="cx"> Specifies a substring match on a property or parameter value.
</span><span class="cx"> """
</span><ins>+ serialized_name = "TextMatch"
+
</ins><span class="cx"> def __init__(self, xml_element):
</span><span class="cx">
</span><span class="cx"> super(TextMatch, self).__init__(xml_element)
</span><ins>+ if xml_element is None:
+ return
</ins><span class="cx">
</span><span class="cx"> self.text = str(xml_element)
</span><span class="cx">
</span><span class="lines">@@ -268,6 +372,30 @@
</span><span class="cx"> self.match_type = "contains"
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def _deserialize(self, data):
+ """
+ Convert a JSON compatible serialization of this object into the actual object.
+ """
+ self.text = data["text"]
+ self.collation = data["collation"]
+ self.negate = data["negate"]
+ self.match_type = data["match_type"]
+
+
+ def serialize(self):
+ """
+ Create a JSON compatible serialization of this object - will be used in a cross-pod request.
+ """
+ result = super(TextMatch, self).serialize()
+ result.update({
+ "text": self.text,
+ "collation": self.collation,
+ "negate": self.negate,
+ "match_type": self.match_type,
+ })
+ return result
+
+
</ins><span class="cx"> def _match(self, item):
</span><span class="cx"> """
</span><span class="cx"> Match the text for the item.
</span><span class="lines">@@ -312,3 +440,5 @@
</span><span class="cx"> return not self.negate
</span><span class="cx">
</span><span class="cx"> return self.negate
</span><ins>+
+FilterBase.serialize_register(TextMatch)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastorequerytesttest_filterpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/query/test/test_filter.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -20,7 +20,7 @@
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import carddavxml
</span><span class="cx">
</span><del>-from txdav.carddav.datastore.query.filter import Filter
</del><ins>+from txdav.carddav.datastore.query.filter import Filter, FilterBase
</ins><span class="cx"> from txdav.common.datastore.sql_tables import schema
</span><span class="cx"> from txdav.carddav.datastore.query.builder import buildExpression
</span><span class="cx"> from txdav.common.datastore.query.generator import SQLQueryGenerator
</span><span class="lines">@@ -72,3 +72,25 @@
</span><span class="cx">
</span><span class="cx"> self.assertEqual(sql, " from RESOURCE where RESOURCE.UID GLOB :1")
</span><span class="cx"> self.assertEqual(args, ["*Example*"])
</span><ins>+
+
+
+class TestQueryFilterSerialize(TestCase):
+
+ def test_query(self):
+ """
+ Basic query test - no time range
+ """
+
+ filter = carddavxml.Filter(
+ *[carddavxml.PropertyFilter(
+ carddavxml.TextMatch.fromString("Example"),
+ **{"name":"UID"}
+ )]
+ )
+ filter = Filter(filter)
+ j = filter.serialize()
+ self.assertEqual(j["type"], "Filter")
+
+ f = FilterBase.deserialize(j)
+ self.assertTrue(isinstance(f, Filter))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/carddav/datastore/sql.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -806,6 +806,13 @@
</span><span class="cx"> C{name} is the resource name, C{uid} is the resource UID.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ # We might be passed an L{Filter} or a serialization of one
+ if isinstance(filter, dict):
+ try:
+ filter = Filter.deserialize(filter)
+ except Exception:
+ filter = None
+
</ins><span class="cx"> # Make sure we have a proper Filter element and get the partial SQL statement to use.
</span><span class="cx"> sql_stmt = self._sqlquery(filter)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingconduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/conduit.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -713,6 +713,7 @@
</span><span class="cx"> PoddingConduit._make_simple_homechild_action("resourcenamessincerevision", "resourceNamesSinceRevision", transform_send=PoddingConduit._to_tuple)
</span><span class="cx"> PoddingConduit._make_simple_homechild_action("resourceuidforname", "resourceUIDForName")
</span><span class="cx"> PoddingConduit._make_simple_homechild_action("resourcenameforuid", "resourceNameForUID")
</span><ins>+PoddingConduit._make_simple_homechild_action("search", "search")
</ins><span class="cx">
</span><span class="cx"> # Calls on L{CommonObjectResource} objects
</span><span class="cx"> PoddingConduit._make_simple_object_action("loadallobjects", "loadAllObjects", transform_recv=PoddingConduit._to_externalize)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastorepoddingtesttest_conduitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/podding/test/test_conduit.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -14,28 +14,35 @@
</span><span class="cx"> # limitations under the License.
</span><span class="cx"> ##
</span><span class="cx">
</span><ins>+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
+
</ins><span class="cx"> from twext.python.clsprop import classproperty
</span><del>-import twext.web2.dav.test.util
</del><ins>+
</ins><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed, returnValue
</span><ins>+
+from twistedcaldav import caldavxml
+from twistedcaldav.caldavxml import TimeRange
+from twistedcaldav.ical import Component, normalize_iCalStr
+
+from txdav.caldav.datastore.query.filter import Filter
+from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
</ins><span class="cx"> from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, Server
</span><span class="cx"> from txdav.caldav.datastore.test.util import buildCalendarStore, \
</span><span class="cx"> TestCalendarStoreDirectoryRecord
</span><del>-from txdav.common.datastore.podding.resource import ConduitResource
-from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
</del><span class="cx"> from txdav.common.datastore.podding.conduit import PoddingConduit, \
</span><span class="cx"> FailedCrossPodRequestError
</span><del>-from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
</del><ins>+from txdav.common.datastore.podding.resource import ConduitResource
</ins><span class="cx"> from txdav.common.datastore.podding.test.util import MultiStoreConduitTest, \
</span><span class="cx"> FakeConduitRequest
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_STATUS_ACCEPTED
</span><del>-from pycalendar.datetime import DateTime
-from twistedcaldav.ical import Component, normalize_iCalStr
</del><ins>+from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
</ins><span class="cx"> from txdav.common.icommondatastore import ObjectResourceNameAlreadyExistsError, \
</span><span class="cx"> ObjectResourceNameNotAllowedError
</span><del>-from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
-from twistedcaldav.caldavxml import TimeRange
-from pycalendar.period import Period
</del><ins>+from txdav.common.idirectoryservice import DirectoryRecordNotFoundError
</ins><span class="cx">
</span><ins>+import twext.web2.dav.test.util
+
</ins><span class="cx"> class TestConduit (CommonCommonTests, twext.web2.dav.test.util.TestCase):
</span><span class="cx">
</span><span class="cx"> class FakeConduit(object):
</span><span class="lines">@@ -533,6 +540,39 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><ins>+ def test_search(self):
+ """
+ Test that action=resourcenameforuid works.
+ """
+
+ yield self.createShare("user01", "puser01")
+
+ calendar1 = yield self.calendarUnderTest(home="user01", name="calendar")
+ yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1))
+ yield self.commit()
+
+ filter = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ *[caldavxml.ComponentFilter(
+ **{"name":("VEVENT", "VFREEBUSY", "VAVAILABILITY")}
+ )],
+ **{"name": "VCALENDAR"}
+ )
+ )
+ filter = Filter(filter)
+
+ calendar1 = yield self.calendarUnderTest(home="user01", name="calendar")
+ names = [item[0] for item in (yield calendar1.search(filter))]
+ self.assertEqual(names, ["1.ics", ])
+ yield self.commit()
+
+ shared = yield self.calendarUnderTest(txn=self.newOtherTransaction(), home="puser01", name="shared-calendar")
+ names = [item[0] for item in (yield shared.search(filter))]
+ self.assertEqual(names, ["1.ics", ])
+ yield self.otherCommit()
+
+
+ @inlineCallbacks
</ins><span class="cx"> def test_loadallobjects(self):
</span><span class="cx"> """
</span><span class="cx"> Test that action=loadallobjects works.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboocrosspodsharingtxdavcommondatastoresql_externalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py (12166 => 12167)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py        2013-12-20 15:57:11 UTC (rev 12166)
+++ CalendarServer/branches/users/cdaboo/cross-pod-sharing/txdav/common/datastore/sql_external.py        2013-12-20 16:02:05 UTC (rev 12167)
</span><span class="lines">@@ -341,7 +341,18 @@
</span><span class="cx"> returnValue(names)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def search(self, filter, **kwargs):
+ try:
+ results = yield self._txn.store().conduit.send_search(self, filter.serialize(), **kwargs)
+ except NonExistentExternalShare:
+ yield self.fixNonExistentExternalShare()
+ raise ExternalShareFailed("External share does not exist")
</ins><span class="cx">
</span><ins>+ returnValue(results)
+
+
+
</ins><span class="cx"> class CommonObjectResourceExternal(CommonObjectResource):
</span><span class="cx"> """
</span><span class="cx"> A CommonObjectResource for a resource not hosted on this system, but on another pod. This will forward
</span></span></pre>
</div>
</div>
</body>
</html>