From fbedeae73e17ddce2139a555408fe4ae33d06ff4 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 4 Nov 2026 13:22:40 +0100 Subject: [PATCH] fix(caldav): render a resource's content once per request A multistatus response asks for a resource's iCal content once per requested property. It is now rendered once per resource and request. --- pkg/routes/caldav/listStorageProvider.go | 11 +++++++ pkg/routes/caldav/requested_props_test.go | 39 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 pkg/routes/caldav/requested_props_test.go diff --git a/pkg/routes/caldav/listStorageProvider.go b/pkg/routes/caldav/listStorageProvider.go index 19922c211..45bebfc9b 100644 --- a/pkg/routes/caldav/listStorageProvider.go +++ b/pkg/routes/caldav/listStorageProvider.go @@ -1110,6 +1110,9 @@ type VikunjaProjectResourceAdapter struct { isPrincipal bool isCollection bool + + // Rendered once per request: a multistatus response asks for it per requested property. + content *string } // IsCollection checks if the resoure in the adapter is a collection @@ -1153,6 +1156,14 @@ func (vlra *VikunjaProjectResourceAdapter) CalculateEtag() string { // GetContent returns the content string of a resource (a task in our case) func (vlra *VikunjaProjectResourceAdapter) GetContent() string { + if vlra.content == nil { + content := vlra.renderContent() + vlra.content = &content + } + return *vlra.content +} + +func (vlra *VikunjaProjectResourceAdapter) renderContent() string { if vlra.project != nil && vlra.projectTasks != nil { return caldav.GetCaldavTodosForTasks(vlra.project, vlra.projectTasks) } diff --git a/pkg/routes/caldav/requested_props_test.go b/pkg/routes/caldav/requested_props_test.go new file mode 100644 index 000000000..f9721c0dd --- /dev/null +++ b/pkg/routes/caldav/requested_props_test.go @@ -0,0 +1,39 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package caldav + +import ( + "testing" + + "code.vikunja.io/api/pkg/models" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResourceContentIsRenderedOnce(t *testing.T) { + task := &models.Task{UID: "uid-rendered-once", Title: "first"} + rr := &VikunjaProjectResourceAdapter{task: task} + + content := rr.GetContent() + require.Contains(t, content, "SUMMARY:first") + + // A second render would pick up the new title. + task.Title = "second" + assert.Equal(t, content, rr.GetContent()) + assert.Equal(t, int64(len(content)), rr.GetContentSize()) +}