protocol-caldav 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/protocol/caldav/collection.rb +99 -75
- data/lib/protocol/caldav/constants.rb +23 -27
- data/lib/protocol/caldav/content_line.rb +94 -91
- data/lib/protocol/caldav/ctag.rb +46 -50
- data/lib/protocol/caldav/etag.rb +24 -28
- data/lib/protocol/caldav/filter/addressbook.rb +41 -28
- data/lib/protocol/caldav/filter/calendar.rb +65 -44
- data/lib/protocol/caldav/filter/match.rb +632 -556
- data/lib/protocol/caldav/filter/parser.rb +273 -252
- data/lib/protocol/caldav/ical/component.rb +49 -46
- data/lib/protocol/caldav/ical/expand.rb +148 -134
- data/lib/protocol/caldav/ical/freebusy.rb +106 -74
- data/lib/protocol/caldav/ical/parser.rb +139 -139
- data/lib/protocol/caldav/ical/property.rb +22 -21
- data/lib/protocol/caldav/ical/rrule.rb +346 -235
- data/lib/protocol/caldav/item.rb +41 -43
- data/lib/protocol/caldav/multistatus.rb +43 -46
- data/lib/protocol/caldav/path.rb +82 -79
- data/lib/protocol/caldav/storage.rb +24 -28
- data/lib/protocol/caldav/vcard/card.rb +25 -27
- data/lib/protocol/caldav/vcard/parser.rb +56 -59
- data/lib/protocol/caldav/version.rb +1 -1
- data/lib/protocol/caldav/xml.rb +84 -80
- data/lib/protocol/caldav/xml_builder.rb +4 -2
- metadata +18 -4
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
|
|
6
3
|
require 'rexml/document'
|
|
7
4
|
require "protocol/caldav"
|
|
8
5
|
|
|
@@ -16,37 +13,44 @@ module Protocol
|
|
|
16
13
|
module_function
|
|
17
14
|
|
|
18
15
|
def parse_calendar(xml_string)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
if xml_string.nil? || xml_string.strip.empty?
|
|
17
|
+
nil
|
|
18
|
+
else
|
|
19
|
+
doc = REXML::Document.new(xml_string)
|
|
20
|
+
ns = { 'c' => CALDAV_NS }
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
return nil unless filter_el
|
|
22
|
+
filter_el = REXML::XPath.first(doc, './/c:filter', ns)
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
if filter_el
|
|
25
|
+
comp_el = REXML::XPath.first(filter_el, 'c:comp-filter', ns)
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
if comp_el
|
|
28
|
+
parse_comp_filter(comp_el, ns)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
31
32
|
rescue REXML::ParseException => e
|
|
32
33
|
raise ParseError, "Malformed XML: #{e.message}"
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def parse_addressbook(xml_string)
|
|
36
|
-
|
|
37
|
+
if xml_string.nil? || xml_string.strip.empty?
|
|
38
|
+
nil
|
|
39
|
+
else
|
|
40
|
+
doc = REXML::Document.new(xml_string)
|
|
41
|
+
ns = { 'cr' => CARDDAV_NS }
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
ns = { 'cr' => CARDDAV_NS }
|
|
43
|
+
filter_el = REXML::XPath.first(doc, './/cr:filter', ns)
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
if filter_el
|
|
46
|
+
test = filter_el.attributes['test'] || 'anyof'
|
|
47
|
+
prop_filters = REXML::XPath.match(filter_el, 'cr:prop-filter', ns).map do |el|
|
|
48
|
+
parse_addressbook_prop_filter(el, ns)
|
|
49
|
+
end
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
parse_addressbook_prop_filter(el, ns)
|
|
51
|
+
Addressbook::Filter.new(test: test, prop_filters: prop_filters)
|
|
52
|
+
end
|
|
47
53
|
end
|
|
48
|
-
|
|
49
|
-
Addressbook::Filter.new(test: test, prop_filters: prop_filters)
|
|
50
54
|
rescue REXML::ParseException => e
|
|
51
55
|
raise ParseError, "Malformed XML: #{e.message}"
|
|
52
56
|
end
|
|
@@ -55,7 +59,9 @@ module Protocol
|
|
|
55
59
|
|
|
56
60
|
def parse_comp_filter(el, ns)
|
|
57
61
|
name = el.attributes['name']
|
|
58
|
-
|
|
62
|
+
unless name
|
|
63
|
+
raise ParseError, "comp-filter missing required 'name' attribute"
|
|
64
|
+
end
|
|
59
65
|
|
|
60
66
|
is_not_defined = REXML::XPath.first(el, 'c:is-not-defined', ns) != nil
|
|
61
67
|
time_range = parse_time_range(REXML::XPath.first(el, 'c:time-range', ns))
|
|
@@ -69,17 +75,19 @@ module Protocol
|
|
|
69
75
|
end
|
|
70
76
|
|
|
71
77
|
Calendar::CompFilter.new(
|
|
72
|
-
name:
|
|
78
|
+
name: name,
|
|
73
79
|
is_not_defined: is_not_defined,
|
|
74
|
-
time_range:
|
|
75
|
-
prop_filters:
|
|
76
|
-
comp_filters:
|
|
80
|
+
time_range: time_range,
|
|
81
|
+
prop_filters: prop_filters,
|
|
82
|
+
comp_filters: comp_filters,
|
|
77
83
|
)
|
|
78
84
|
end
|
|
79
85
|
|
|
80
86
|
def parse_prop_filter(el, ns)
|
|
81
87
|
name = el.attributes['name']
|
|
82
|
-
|
|
88
|
+
unless name
|
|
89
|
+
raise ParseError, "prop-filter missing required 'name' attribute"
|
|
90
|
+
end
|
|
83
91
|
|
|
84
92
|
is_not_defined = REXML::XPath.first(el, 'c:is-not-defined', ns) != nil
|
|
85
93
|
time_range = parse_time_range(REXML::XPath.first(el, 'c:time-range', ns))
|
|
@@ -90,11 +98,11 @@ module Protocol
|
|
|
90
98
|
end
|
|
91
99
|
|
|
92
100
|
Calendar::PropFilter.new(
|
|
93
|
-
name:
|
|
101
|
+
name: name,
|
|
94
102
|
is_not_defined: is_not_defined,
|
|
95
|
-
time_range:
|
|
96
|
-
text_match:
|
|
97
|
-
param_filters:
|
|
103
|
+
time_range: time_range,
|
|
104
|
+
text_match: text_match,
|
|
105
|
+
param_filters: param_filters,
|
|
98
106
|
)
|
|
99
107
|
end
|
|
100
108
|
|
|
@@ -107,37 +115,45 @@ module Protocol
|
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
def parse_text_match(el)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
if el
|
|
119
|
+
Calendar::TextMatch.new(
|
|
120
|
+
value: el.text&.strip || '',
|
|
121
|
+
collation: el.attributes['collation'] || 'i;ascii-casemap',
|
|
122
|
+
match_type: el.attributes['match-type'] || 'contains',
|
|
123
|
+
negate_condition: el.attributes['negate-condition'] == 'yes',
|
|
124
|
+
)
|
|
125
|
+
else
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
118
128
|
end
|
|
119
129
|
|
|
120
130
|
def parse_time_range(el)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
if el
|
|
132
|
+
Calendar::TimeRange.new(
|
|
133
|
+
start_time: el.attributes['start'],
|
|
134
|
+
end_time: el.attributes['end'],
|
|
135
|
+
)
|
|
136
|
+
else
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
127
139
|
end
|
|
128
140
|
|
|
129
141
|
def parse_addressbook_prop_filter(el, ns)
|
|
130
142
|
name = el.attributes['name']
|
|
131
|
-
|
|
143
|
+
unless name
|
|
144
|
+
raise ParseError, "prop-filter missing required 'name' attribute"
|
|
145
|
+
end
|
|
132
146
|
|
|
133
147
|
is_not_defined = REXML::XPath.first(el, 'cr:is-not-defined', ns) != nil
|
|
134
148
|
text_match_el = REXML::XPath.first(el, 'cr:text-match', ns)
|
|
135
|
-
text_match =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
149
|
+
text_match = nil
|
|
150
|
+
|
|
151
|
+
if text_match_el
|
|
152
|
+
text_match = Addressbook::TextMatch.new(
|
|
153
|
+
value: text_match_el.text&.strip || '',
|
|
154
|
+
collation: text_match_el.attributes['collation'] || 'i;ascii-casemap',
|
|
155
|
+
match_type: text_match_el.attributes['match-type'] || 'contains',
|
|
156
|
+
negate_condition: text_match_el.attributes['negate-condition'] == 'yes',
|
|
141
157
|
)
|
|
142
158
|
end
|
|
143
159
|
|
|
@@ -145,27 +161,33 @@ module Protocol
|
|
|
145
161
|
pf_name = pf_el.attributes['name']
|
|
146
162
|
pf_is_not_defined = REXML::XPath.first(pf_el, 'cr:is-not-defined', ns) != nil
|
|
147
163
|
pf_text_match_el = REXML::XPath.first(pf_el, 'cr:text-match', ns)
|
|
148
|
-
pf_text_match =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
164
|
+
pf_text_match = nil
|
|
165
|
+
|
|
166
|
+
if pf_text_match_el
|
|
167
|
+
pf_text_match = Addressbook::TextMatch.new(
|
|
168
|
+
value: pf_text_match_el.text&.strip || '',
|
|
169
|
+
collation: pf_text_match_el.attributes['collation'] || 'i;ascii-casemap',
|
|
170
|
+
match_type: pf_text_match_el.attributes['match-type'] || 'contains',
|
|
171
|
+
negate_condition: pf_text_match_el.attributes['negate-condition'] == 'yes',
|
|
154
172
|
)
|
|
155
173
|
end
|
|
156
174
|
Addressbook::ParamFilter.new(name: pf_name, is_not_defined: pf_is_not_defined, text_match: pf_text_match)
|
|
157
175
|
end
|
|
158
176
|
|
|
159
177
|
Addressbook::PropFilter.new(
|
|
160
|
-
name:
|
|
178
|
+
name: name,
|
|
161
179
|
is_not_defined: is_not_defined,
|
|
162
|
-
text_match:
|
|
163
|
-
param_filters:
|
|
180
|
+
text_match: text_match,
|
|
181
|
+
param_filters: param_filters,
|
|
164
182
|
)
|
|
165
183
|
end
|
|
166
184
|
|
|
167
|
-
private_class_method :parse_comp_filter,
|
|
168
|
-
|
|
185
|
+
private_class_method :parse_comp_filter,
|
|
186
|
+
:parse_prop_filter,
|
|
187
|
+
:parse_param_filter,
|
|
188
|
+
:parse_text_match,
|
|
189
|
+
:parse_time_range,
|
|
190
|
+
:parse_addressbook_prop_filter
|
|
169
191
|
end
|
|
170
192
|
end
|
|
171
193
|
|
|
@@ -173,219 +195,218 @@ module Protocol
|
|
|
173
195
|
end
|
|
174
196
|
end
|
|
175
197
|
|
|
198
|
+
__END__
|
|
176
199
|
|
|
177
|
-
|
|
178
|
-
describe "
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
end
|
|
200
|
+
describe "Protocol::Caldav::Filter::Parser" do
|
|
201
|
+
describe "calendar parser" do
|
|
202
|
+
it "parses a single comp-filter" do
|
|
203
|
+
xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter name="VCALENDAR"/></c:filter>'
|
|
204
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
205
|
+
f.name.should.equal "VCALENDAR"
|
|
206
|
+
end
|
|
185
207
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
it "parses nested comp-filters" do
|
|
209
|
+
xml = <<~XML
|
|
210
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
211
|
+
<c:comp-filter name="VCALENDAR">
|
|
212
|
+
<c:comp-filter name="VEVENT"/>
|
|
213
|
+
</c:comp-filter>
|
|
214
|
+
</c:filter>
|
|
215
|
+
XML
|
|
216
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
217
|
+
f.comp_filters.length.should.equal 1
|
|
218
|
+
f.comp_filters[0].name.should.equal "VEVENT"
|
|
219
|
+
end
|
|
198
220
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
221
|
+
it "parses is-not-defined" do
|
|
222
|
+
xml = <<~XML
|
|
223
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
224
|
+
<c:comp-filter name="VCALENDAR">
|
|
225
|
+
<c:comp-filter name="VTODO"><c:is-not-defined/></c:comp-filter>
|
|
226
|
+
</c:comp-filter>
|
|
227
|
+
</c:filter>
|
|
228
|
+
XML
|
|
229
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
230
|
+
f.comp_filters[0].is_not_defined.should.equal true
|
|
231
|
+
end
|
|
210
232
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
233
|
+
it "parses prop-filter with no children as defined-only check" do
|
|
234
|
+
xml = <<~XML
|
|
235
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
236
|
+
<c:comp-filter name="VCALENDAR">
|
|
237
|
+
<c:prop-filter name="SUMMARY"/>
|
|
238
|
+
</c:comp-filter>
|
|
239
|
+
</c:filter>
|
|
240
|
+
XML
|
|
241
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
242
|
+
f.prop_filters.length.should.equal 1
|
|
243
|
+
f.prop_filters[0].name.should.equal "SUMMARY"
|
|
244
|
+
f.prop_filters[0].text_match.should.be.nil
|
|
245
|
+
end
|
|
224
246
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
it "parses text-match with default collation and match-type" do
|
|
248
|
+
xml = <<~XML
|
|
249
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
250
|
+
<c:comp-filter name="VCALENDAR">
|
|
251
|
+
<c:prop-filter name="SUMMARY">
|
|
252
|
+
<c:text-match>Meeting</c:text-match>
|
|
253
|
+
</c:prop-filter>
|
|
254
|
+
</c:comp-filter>
|
|
255
|
+
</c:filter>
|
|
256
|
+
XML
|
|
257
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
258
|
+
tm = f.prop_filters[0].text_match
|
|
259
|
+
tm.value.should.equal "Meeting"
|
|
260
|
+
tm.collation.should.equal "i;ascii-casemap"
|
|
261
|
+
tm.match_type.should.equal "contains"
|
|
262
|
+
end
|
|
241
263
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
264
|
+
it "parses text-match with explicit attributes" do
|
|
265
|
+
xml = <<~XML
|
|
266
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
267
|
+
<c:comp-filter name="VCALENDAR">
|
|
268
|
+
<c:prop-filter name="SUMMARY">
|
|
269
|
+
<c:text-match collation="i;octet" match-type="equals">X</c:text-match>
|
|
270
|
+
</c:prop-filter>
|
|
271
|
+
</c:comp-filter>
|
|
272
|
+
</c:filter>
|
|
273
|
+
XML
|
|
274
|
+
tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
|
|
275
|
+
tm.collation.should.equal "i;octet"
|
|
276
|
+
tm.match_type.should.equal "equals"
|
|
277
|
+
end
|
|
256
278
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
279
|
+
it "parses negate-condition" do
|
|
280
|
+
xml = <<~XML
|
|
281
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
282
|
+
<c:comp-filter name="VCALENDAR">
|
|
283
|
+
<c:prop-filter name="SUMMARY">
|
|
284
|
+
<c:text-match negate-condition="yes">X</c:text-match>
|
|
285
|
+
</c:prop-filter>
|
|
286
|
+
</c:comp-filter>
|
|
287
|
+
</c:filter>
|
|
288
|
+
XML
|
|
289
|
+
tm = Protocol::Caldav::Filter::Parser.parse_calendar(xml).prop_filters[0].text_match
|
|
290
|
+
tm.negate_condition.should.equal true
|
|
291
|
+
end
|
|
270
292
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
</c:comp-filter>
|
|
293
|
+
it "parses time-range with start and end" do
|
|
294
|
+
xml = <<~XML
|
|
295
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
296
|
+
<c:comp-filter name="VCALENDAR">
|
|
297
|
+
<c:comp-filter name="VEVENT">
|
|
298
|
+
<c:time-range start="20260101T000000Z" end="20260201T000000Z"/>
|
|
278
299
|
</c:comp-filter>
|
|
279
|
-
</c:filter>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
300
|
+
</c:comp-filter>
|
|
301
|
+
</c:filter>
|
|
302
|
+
XML
|
|
303
|
+
tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
|
|
304
|
+
tr.start_time.should.equal "20260101T000000Z"
|
|
305
|
+
tr.end_time.should.equal "20260201T000000Z"
|
|
306
|
+
end
|
|
285
307
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
</c:comp-filter>
|
|
308
|
+
it "parses time-range with only start" do
|
|
309
|
+
xml = <<~XML
|
|
310
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
311
|
+
<c:comp-filter name="VCALENDAR">
|
|
312
|
+
<c:comp-filter name="VEVENT">
|
|
313
|
+
<c:time-range start="20260101T000000Z"/>
|
|
293
314
|
</c:comp-filter>
|
|
294
|
-
</c:filter>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
315
|
+
</c:comp-filter>
|
|
316
|
+
</c:filter>
|
|
317
|
+
XML
|
|
318
|
+
tr = Protocol::Caldav::Filter::Parser.parse_calendar(xml).comp_filters[0].time_range
|
|
319
|
+
tr.start_time.should.equal "20260101T000000Z"
|
|
320
|
+
tr.end_time.should.be.nil
|
|
321
|
+
end
|
|
300
322
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
323
|
+
it "returns nil for missing filter element" do
|
|
324
|
+
Protocol::Caldav::Filter::Parser.parse_calendar('<d:propfind xmlns:d="DAV:"/>').should.be.nil
|
|
325
|
+
end
|
|
304
326
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
327
|
+
it "returns nil for empty filter" do
|
|
328
|
+
Protocol::Caldav::Filter::Parser.parse_calendar('<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"/>').should.be.nil
|
|
329
|
+
end
|
|
308
330
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
331
|
+
it "returns nil for nil input" do
|
|
332
|
+
Protocol::Caldav::Filter::Parser.parse_calendar(nil).should.be.nil
|
|
333
|
+
end
|
|
312
334
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
it "accepts any namespace prefix bound to caldav NS" do
|
|
336
|
+
xml = '<cal:filter xmlns:cal="urn:ietf:params:xml:ns:caldav"><cal:comp-filter name="VCALENDAR"/></cal:filter>'
|
|
337
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
338
|
+
f.name.should.equal "VCALENDAR"
|
|
339
|
+
end
|
|
318
340
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
</c:comp-filter>
|
|
341
|
+
it "parses param-filter inside prop-filter" do
|
|
342
|
+
xml = <<~XML
|
|
343
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
344
|
+
<c:comp-filter name="VCALENDAR">
|
|
345
|
+
<c:comp-filter name="VEVENT">
|
|
346
|
+
<c:prop-filter name="ATTENDEE">
|
|
347
|
+
<c:param-filter name="PARTSTAT">
|
|
348
|
+
<c:text-match>ACCEPTED</c:text-match>
|
|
349
|
+
</c:param-filter>
|
|
350
|
+
</c:prop-filter>
|
|
330
351
|
</c:comp-filter>
|
|
331
|
-
</c:filter>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
352
|
+
</c:comp-filter>
|
|
353
|
+
</c:filter>
|
|
354
|
+
XML
|
|
355
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
356
|
+
pf = f.comp_filters[0].prop_filters[0]
|
|
357
|
+
pf.name.should.equal "ATTENDEE"
|
|
358
|
+
pf.param_filters.length.should.equal 1
|
|
359
|
+
pf.param_filters[0].name.should.equal "PARTSTAT"
|
|
360
|
+
pf.param_filters[0].text_match.value.should.equal "ACCEPTED"
|
|
361
|
+
end
|
|
340
362
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
</c:comp-filter>
|
|
363
|
+
it "parses param-filter with is-not-defined" do
|
|
364
|
+
xml = <<~XML
|
|
365
|
+
<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
366
|
+
<c:comp-filter name="VCALENDAR">
|
|
367
|
+
<c:comp-filter name="VEVENT">
|
|
368
|
+
<c:prop-filter name="ATTENDEE">
|
|
369
|
+
<c:param-filter name="PARTSTAT">
|
|
370
|
+
<c:is-not-defined/>
|
|
371
|
+
</c:param-filter>
|
|
372
|
+
</c:prop-filter>
|
|
352
373
|
</c:comp-filter>
|
|
353
|
-
</c:filter>
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
374
|
+
</c:comp-filter>
|
|
375
|
+
</c:filter>
|
|
376
|
+
XML
|
|
377
|
+
f = Protocol::Caldav::Filter::Parser.parse_calendar(xml)
|
|
378
|
+
pf = f.comp_filters[0].prop_filters[0].param_filters[0]
|
|
379
|
+
pf.is_not_defined.should.equal true
|
|
380
|
+
end
|
|
359
381
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
end
|
|
382
|
+
it "raises on comp-filter with no name attribute" do
|
|
383
|
+
xml = '<c:filter xmlns:c="urn:ietf:params:xml:ns:caldav"><c:comp-filter/></c:filter>'
|
|
384
|
+
lambda { Protocol::Caldav::Filter::Parser.parse_calendar(xml) }.should.raise Protocol::Caldav::ParseError
|
|
364
385
|
end
|
|
386
|
+
end
|
|
365
387
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
388
|
+
describe "addressbook parser" do
|
|
389
|
+
it "parses a prop-filter" do
|
|
390
|
+
xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
|
|
391
|
+
f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
|
|
392
|
+
f.prop_filters.length.should.equal 1
|
|
393
|
+
f.prop_filters[0].name.should.equal "FN"
|
|
394
|
+
end
|
|
373
395
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
396
|
+
it "parses test attribute on filter" do
|
|
397
|
+
xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav" test="allof"><cr:prop-filter name="FN"/></cr:filter>'
|
|
398
|
+
f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
|
|
399
|
+
f.test.should.equal "allof"
|
|
400
|
+
end
|
|
379
401
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
402
|
+
it "defaults test to anyof" do
|
|
403
|
+
xml = '<cr:filter xmlns:cr="urn:ietf:params:xml:ns:carddav"><cr:prop-filter name="FN"/></cr:filter>'
|
|
404
|
+
f = Protocol::Caldav::Filter::Parser.parse_addressbook(xml)
|
|
405
|
+
f.test.should.equal "anyof"
|
|
406
|
+
end
|
|
385
407
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
end
|
|
408
|
+
it "returns nil for nil input" do
|
|
409
|
+
Protocol::Caldav::Filter::Parser.parse_addressbook(nil).should.be.nil
|
|
389
410
|
end
|
|
390
411
|
end
|
|
391
412
|
end
|