mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-27 20:47:13 -05:00
groupware: fix deserialization of Event Alert Trigger types using mapstructure
This commit is contained in:
@@ -725,3 +725,51 @@ func TestUnmarshallingCalendarEventGetResponse(t *testing.T) {
|
||||
require.Equal("9a7ab91a-edca-4988-886f-25e00743430d", result.Uid)
|
||||
require.Equal(jscalendar.PrivacyPublic, result.Privacy)
|
||||
}
|
||||
|
||||
func TestAlertWithOffsetTriggerInResponse(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
text := `{
|
||||
"methodResponses":[
|
||||
["CalendarEvent/get",{
|
||||
"accountId":"b",
|
||||
"state":"ssecq",
|
||||
"list":[{
|
||||
"@type": "Event",
|
||||
"start":"2025-11-01T14:30:00",
|
||||
"alerts": {
|
||||
"M87fT82": {
|
||||
"@type": "Alert",
|
||||
"trigger": {
|
||||
"@type": "OffsetTrigger",
|
||||
"offset": "-PT15M",
|
||||
"relativeTo": "start"
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
"notFound":[]
|
||||
},"1"]
|
||||
],"sessionState":"7d3cae5b"
|
||||
}`
|
||||
|
||||
var response Response
|
||||
err := json.Unmarshal([]byte(text), &response)
|
||||
require.NoError(err)
|
||||
|
||||
resp := response.MethodResponses[0]
|
||||
require.Equal(CommandCalendarEventGet, resp.Command)
|
||||
require.IsType(CalendarEventGetResponse{}, resp.Parameters)
|
||||
params := resp.Parameters.(CalendarEventGetResponse)
|
||||
require.Len(params.List, 1)
|
||||
event := params.List[0]
|
||||
require.Contains(event.Alerts, "M87fT82")
|
||||
alert := event.Alerts["M87fT82"]
|
||||
require.NotNil(alert)
|
||||
trigger := alert.Trigger
|
||||
require.NotNil(trigger)
|
||||
require.IsType(jscalendar.OffsetTrigger{}, trigger)
|
||||
offsetTrigger := trigger.(jscalendar.OffsetTrigger)
|
||||
require.Equal(jscalendar.SignedDuration("-PT15M"), offsetTrigger.Offset)
|
||||
require.Equal(jscalendar.RelativeToStart, offsetTrigger.RelativeTo)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
)
|
||||
|
||||
@@ -139,8 +140,9 @@ func mapstructStringToTimeHook() mapstructure.DecodeHookFunc {
|
||||
// mapstruct isn't able to properly map RFC3339 date strings into Time
|
||||
// objects, which is why we require this custom hook,
|
||||
// see https://github.com/mitchellh/mapstructure/issues/41
|
||||
wanted := reflect.TypeOf(time.Time{})
|
||||
return func(from reflect.Type, to reflect.Type, data any) (any, error) {
|
||||
if to != reflect.TypeOf(time.Time{}) {
|
||||
if to != wanted {
|
||||
return data, nil
|
||||
}
|
||||
switch from.Kind() {
|
||||
@@ -159,8 +161,11 @@ func mapstructStringToTimeHook() mapstructure.DecodeHookFunc {
|
||||
func decodeMap(input map[string]any, target any) error {
|
||||
// https://github.com/mitchellh/mapstructure/issues/41
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(mapstructStringToTimeHook()),
|
||||
Metadata: nil,
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructStringToTimeHook(),
|
||||
jscalendar.MapstructTriggerHook(),
|
||||
),
|
||||
Result: &target,
|
||||
ErrorUnused: false,
|
||||
ErrorUnset: false,
|
||||
|
||||
@@ -3,7 +3,11 @@ package jscalendar
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// This is a date-time string with no time zone/offset information.
|
||||
@@ -1407,6 +1411,74 @@ var _ Trigger = UnknownTrigger{}
|
||||
|
||||
func (o UnknownTrigger) trigger() {}
|
||||
|
||||
func MapstructTriggerHook() mapstructure.DecodeHookFunc {
|
||||
fn := func(Trigger) {}
|
||||
wanted := reflect.TypeOf(fn).In(0)
|
||||
return func(from reflect.Type, to reflect.Type, data any) (any, error) {
|
||||
if to != wanted {
|
||||
return data, nil
|
||||
}
|
||||
m := data.(map[string]any)
|
||||
if typ, ok := m["@type"]; ok {
|
||||
switch typ {
|
||||
case string(OffsetTriggerType):
|
||||
return mapOffsetTrigger(m)
|
||||
case string(AbsoluteTriggerType):
|
||||
return mapAbsoluteTrigger(m)
|
||||
default:
|
||||
return UnknownTrigger(m), nil
|
||||
}
|
||||
} else {
|
||||
if _, ok := m["offset"]; ok {
|
||||
return mapOffsetTrigger(m)
|
||||
}
|
||||
if _, ok := m["when"]; ok {
|
||||
return mapAbsoluteTrigger(m)
|
||||
} else {
|
||||
return UnknownTrigger(m), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mapOffsetTrigger(m map[string]any) (OffsetTrigger, error) {
|
||||
trigger := OffsetTrigger{
|
||||
Type: OffsetTriggerType,
|
||||
}
|
||||
if value, ok := m["offset"]; ok {
|
||||
if str, ok := value.(string); ok {
|
||||
trigger.Offset = SignedDuration(str)
|
||||
}
|
||||
}
|
||||
if value, ok := m["relativeTo"]; ok {
|
||||
if str, ok := value.(string); ok {
|
||||
t := RelativeTo(str)
|
||||
if slices.Contains(RelativeTos, t) {
|
||||
trigger.RelativeTo = t
|
||||
} else {
|
||||
return trigger, fmt.Errorf("unsupported Trigger.relativeTo value: '%v'", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return trigger, nil
|
||||
}
|
||||
|
||||
func mapAbsoluteTrigger(m map[string]any) (AbsoluteTrigger, error) {
|
||||
trigger := AbsoluteTrigger{
|
||||
Type: AbsoluteTriggerType,
|
||||
}
|
||||
if value, ok := m["when"]; ok {
|
||||
if str, ok := value.(string); ok {
|
||||
if w, err := time.Parse(time.RFC3339, str); err != nil {
|
||||
trigger.When = w
|
||||
} else {
|
||||
return trigger, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return trigger, nil
|
||||
}
|
||||
|
||||
type Alert struct {
|
||||
// This specifies the type of this object. This MUST be `Alert`.
|
||||
Type TypeOfAlert `json:"@type,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user