mirror of
https://github.com/1f349/go-webdav.git
synced 2024-12-22 16:24:14 +00:00
caldav: extend query filter types
The basic types related to queries and filtering are missing some features specified in the RFC (as also noted in the TODO comments). This adds several of the missing elements, working towards being able to handle all RFC-compliant queries. The work is not fully done, e.g. the collation for text-match is still not handled, but it's getting pretty close.
This commit is contained in:
parent
7dafedd290
commit
6401d9ed45
@ -28,18 +28,29 @@ type CalendarCompRequest struct {
|
|||||||
|
|
||||||
type CompFilter struct {
|
type CompFilter struct {
|
||||||
Name string
|
Name string
|
||||||
|
IsNotDefined bool
|
||||||
Start, End time.Time
|
Start, End time.Time
|
||||||
Props []PropFilter
|
Props []PropFilter
|
||||||
Comps []CompFilter
|
Comps []CompFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ParamFilter struct {
|
||||||
|
Name string
|
||||||
|
IsNotDefined bool
|
||||||
|
TextMatch *TextMatch
|
||||||
|
}
|
||||||
|
|
||||||
type PropFilter struct {
|
type PropFilter struct {
|
||||||
Name string
|
Name string
|
||||||
|
IsNotDefined bool
|
||||||
|
Start, End time.Time
|
||||||
TextMatch *TextMatch
|
TextMatch *TextMatch
|
||||||
|
ParamFilter []ParamFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextMatch struct {
|
type TextMatch struct {
|
||||||
Text string
|
Text string
|
||||||
|
NegateCondition bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CalendarQuery struct {
|
type CalendarQuery struct {
|
||||||
|
@ -22,6 +22,7 @@ var (
|
|||||||
calendarMultigetName = xml.Name{namespace, "calendar-multiget"}
|
calendarMultigetName = xml.Name{namespace, "calendar-multiget"}
|
||||||
|
|
||||||
calendarName = xml.Name{namespace, "calendar"}
|
calendarName = xml.Name{namespace, "calendar"}
|
||||||
|
calendarDataName = xml.Name{namespace, "calendar-data"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc4791#section-6.2.1
|
// https://tools.ietf.org/html/rfc4791#section-6.2.1
|
||||||
@ -103,14 +104,44 @@ type propFilter struct {
|
|||||||
IsNotDefined *struct{} `xml:"is-not-defined,omitempty"`
|
IsNotDefined *struct{} `xml:"is-not-defined,omitempty"`
|
||||||
TimeRange *timeRange `xml:"time-range,omitempty"`
|
TimeRange *timeRange `xml:"time-range,omitempty"`
|
||||||
TextMatch *textMatch `xml:"text-match,omitempty"`
|
TextMatch *textMatch `xml:"text-match,omitempty"`
|
||||||
// TODO: param-filter
|
ParamFilter []paramFilter `xml:"param-filter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tools.ietf.org/html/rfc4791#section-9.7.3
|
||||||
|
type paramFilter struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav param-filter"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
IsNotDefined *struct{} `xml:"is-not-defined,omitempty"`
|
||||||
|
TextMatch *textMatch `xml:"text-match,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc4791#section-9.7.5
|
// https://tools.ietf.org/html/rfc4791#section-9.7.5
|
||||||
type textMatch struct {
|
type textMatch struct {
|
||||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav text-match"`
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav text-match"`
|
||||||
Text string `xml:",chardata"`
|
Text string `xml:",chardata"`
|
||||||
// TODO: collation, negate-condition
|
Collation string `xml:"collation,attr,omitempty"`
|
||||||
|
NegateCondition negateCondition `xml:"negate-condition,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type negateCondition bool
|
||||||
|
|
||||||
|
func (nc *negateCondition) UnmarshalText(b []byte) error {
|
||||||
|
switch s := string(b); s {
|
||||||
|
case "yes":
|
||||||
|
*nc = true
|
||||||
|
case "no":
|
||||||
|
*nc = false
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("caldav: invalid negate-condition value: %q", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nc negateCondition) MarshalText() ([]byte, error) {
|
||||||
|
if nc {
|
||||||
|
return []byte("yes"), nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc4791#section-9.9
|
// https://tools.ietf.org/html/rfc4791#section-9.9
|
||||||
|
@ -69,17 +69,53 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return internal.HTTPErrorf(http.StatusBadRequest, "caldav: expected calendar-query or calendar-multiget element in REPORT request")
|
return internal.HTTPErrorf(http.StatusBadRequest, "caldav: expected calendar-query or calendar-multiget element in REPORT request")
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodePropFilter(el *propFilter) (*PropFilter, error) {
|
func decodeParamFilter(el *paramFilter) (*ParamFilter, error) {
|
||||||
pf := &PropFilter{Name: el.Name}
|
pf := &ParamFilter{Name: el.Name}
|
||||||
|
if el.IsNotDefined != nil {
|
||||||
|
if el.TextMatch != nil {
|
||||||
|
return nil, fmt.Errorf("caldav: failed to parse param-filter: if is-not-defined is provided, text-match can't be provided")
|
||||||
|
}
|
||||||
|
pf.IsNotDefined = true
|
||||||
|
}
|
||||||
if el.TextMatch != nil {
|
if el.TextMatch != nil {
|
||||||
pf.TextMatch = &TextMatch{Text: el.TextMatch.Text}
|
pf.TextMatch = &TextMatch{Text: el.TextMatch.Text}
|
||||||
}
|
}
|
||||||
// TODO: IsNotDefined, TimeRange
|
return pf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePropFilter(el *propFilter) (*PropFilter, error) {
|
||||||
|
pf := &PropFilter{Name: el.Name}
|
||||||
|
if el.IsNotDefined != nil {
|
||||||
|
if el.TextMatch != nil || el.TimeRange != nil || len(el.ParamFilter) > 0 {
|
||||||
|
return nil, fmt.Errorf("caldav: failed to parse prop-filter: if is-not-defined is provided, text-match, time-range, or param-filter can't be provided")
|
||||||
|
}
|
||||||
|
pf.IsNotDefined = true
|
||||||
|
}
|
||||||
|
if el.TextMatch != nil {
|
||||||
|
pf.TextMatch = &TextMatch{Text: el.TextMatch.Text}
|
||||||
|
}
|
||||||
|
if el.TimeRange != nil {
|
||||||
|
pf.Start = time.Time(el.TimeRange.Start)
|
||||||
|
pf.End = time.Time(el.TimeRange.End)
|
||||||
|
}
|
||||||
|
for _, paramEl := range el.ParamFilter {
|
||||||
|
paramFi, err := decodeParamFilter(¶mEl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf.ParamFilter = append(pf.ParamFilter, *paramFi)
|
||||||
|
}
|
||||||
return pf, nil
|
return pf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeCompFilter(el *compFilter) (*CompFilter, error) {
|
func decodeCompFilter(el *compFilter) (*CompFilter, error) {
|
||||||
cf := &CompFilter{Name: el.Name}
|
cf := &CompFilter{Name: el.Name}
|
||||||
|
if el.IsNotDefined != nil {
|
||||||
|
if el.TimeRange != nil || len(el.PropFilters) > 0 || len(el.CompFilters) > 0 {
|
||||||
|
return nil, fmt.Errorf("caldav: failed to parse comp-filter: if is-not-defined is provided, time-range, prop-filter, or comp-filter can't be provided")
|
||||||
|
}
|
||||||
|
cf.IsNotDefined = true
|
||||||
|
}
|
||||||
if el.TimeRange != nil {
|
if el.TimeRange != nil {
|
||||||
cf.Start = time.Time(el.TimeRange.Start)
|
cf.Start = time.Time(el.TimeRange.Start)
|
||||||
cf.End = time.Time(el.TimeRange.End)
|
cf.End = time.Time(el.TimeRange.End)
|
||||||
@ -98,7 +134,6 @@ func decodeCompFilter(el *compFilter) (*CompFilter, error) {
|
|||||||
}
|
}
|
||||||
cf.Comps = append(cf.Comps, *child)
|
cf.Comps = append(cf.Comps, *child)
|
||||||
}
|
}
|
||||||
// TODO: IsNotDefined
|
|
||||||
return cf, nil
|
return cf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user