mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-25 13:59:14 +02:00
fix:#36 fixed tests, naming, and removed old duplicated code
This commit is contained in:
@@ -18,6 +18,7 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pocketbase/pocketbase/daos"
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
"htwkalender/data-manager/model"
|
"htwkalender/data-manager/model"
|
||||||
@@ -138,11 +139,11 @@ func buildIcalQueryForModules(modulesUuid []string) dbx.Expression {
|
|||||||
// following the pattern of only containing alphanumeric characters and dashes
|
// following the pattern of only containing alphanumeric characters and dashes
|
||||||
|
|
||||||
for _, moduleUuid := range modulesUuid {
|
for _, moduleUuid := range modulesUuid {
|
||||||
if !IsSafeIdentifier(moduleUuid) {
|
err := uuid.Validate(moduleUuid)
|
||||||
|
if err != nil {
|
||||||
slog.Warn("Module UUID is not safe: ", "moduleUuid", moduleUuid)
|
slog.Warn("Module UUID is not safe: ", "moduleUuid", moduleUuid)
|
||||||
return dbx.HashExp{}
|
return dbx.HashExp{}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// build where conditions for each module
|
// build where conditions for each module
|
||||||
|
@@ -38,13 +38,13 @@ func Test_buildIcalQueryForModules(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "one module",
|
name: "one module",
|
||||||
args: args{modules: []string{"test"}},
|
args: args{modules: []string{"77eddc32-c49d-5d0a-8c36-17b266396641"}},
|
||||||
want: dbx.HashExp{"uuid": "test"},
|
want: dbx.HashExp{"uuid": "77eddc32-c49d-5d0a-8c36-17b266396641"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "two modules",
|
name: "two modules",
|
||||||
args: args{modules: []string{"test", "test2"}},
|
args: args{modules: []string{"9e5081e6-4c56-57b9-9965-f6dc74559755", "48cd8c4e-fb70-595c-9dfb-7035f56326d9"}},
|
||||||
want: dbx.Or(dbx.HashExp{"uuid": "test"}, dbx.HashExp{"uuid": "test2"}),
|
want: dbx.Or(dbx.HashExp{"uuid": "9e5081e6-4c56-57b9-9965-f6dc74559755"}, dbx.HashExp{"uuid": "48cd8c4e-fb70-595c-9dfb-7035f56326d9"}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@@ -19,26 +19,9 @@ package db
|
|||||||
import (
|
import (
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"log/slog"
|
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func FindCollection(app *pocketbase.PocketBase, collectionName string) (*models.Collection, error) {
|
func FindCollection(app *pocketbase.PocketBase, collectionName string) (*models.Collection, error) {
|
||||||
collection, dbError := app.Dao().FindCollectionByNameOrId(collectionName)
|
collection, dbError := app.Dao().FindCollectionByNameOrId(collectionName)
|
||||||
return collection, dbError
|
return collection, dbError
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSafeIdentifier check uuids against sql injection
|
|
||||||
// uuids are generated by the system and are not user input
|
|
||||||
// following the pattern of only containing alphanumeric characters and dashes
|
|
||||||
func IsSafeIdentifier(uuid string) bool {
|
|
||||||
// Define a regular expression that matches only valid UUID characters (alphanumeric and dashes)
|
|
||||||
validUUIDPattern := `^[a-zA-Z0-9-]+$`
|
|
||||||
match, err := regexp.MatchString(validUUIDPattern, uuid)
|
|
||||||
if err != nil {
|
|
||||||
// Handle the error according to your application's needs
|
|
||||||
slog.Warn("Invalid UUID pattern", "uuid", uuid)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
|
@@ -1,50 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestIsSafeIdentifier(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
uuid string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test safe identifier",
|
|
||||||
args: args{
|
|
||||||
uuid: "1234567890-1234567890-1234567890-1234567890",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test safe identifier",
|
|
||||||
args: args{
|
|
||||||
uuid: "1234567890-1234567890-1234567890-1234567890",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test safe identifier",
|
|
||||||
args: args{
|
|
||||||
uuid: "77eddc32-c49d-5d0a-8c36-17b266396641",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test unsafe identifier",
|
|
||||||
args: args{
|
|
||||||
uuid: "77eddc32-c49d-5d0a-8c36-17/1!!b266396641-",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := IsSafeIdentifier(tt.args.uuid); got != tt.want {
|
|
||||||
t.Errorf("IsSafeIdentifier() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -195,6 +195,8 @@ func parseHTML(webpage string) (*html.Node, error) {
|
|||||||
return doc, nil
|
return doc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateUUIDs generates a UUID for each event based on the event name, course and semester
|
||||||
|
// the UUID is used to identify the event in the database
|
||||||
func generateUUIDs(events []model.Event) []model.Event {
|
func generateUUIDs(events []model.Event) []model.Event {
|
||||||
for i, event := range events {
|
for i, event := range events {
|
||||||
// generate a hash value from the event name, course and semester
|
// generate a hash value from the event name, course and semester
|
||||||
|
@@ -1,173 +0,0 @@
|
|||||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
|
||||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
|
||||||
|
|
||||||
//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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ical
|
|
||||||
|
|
||||||
import (
|
|
||||||
"htwkalender/data-manager/model"
|
|
||||||
"htwkalender/data-manager/service/functions"
|
|
||||||
clock "htwkalender/data-manager/service/functions/time"
|
|
||||||
"htwkalender/data-manager/service/names"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jordic/goics"
|
|
||||||
_ "time/tzdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IcalModel local type for EmitICal function
|
|
||||||
type IcalModel struct {
|
|
||||||
Events model.Events
|
|
||||||
Mapping map[string]model.FeedCollection
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitICal implements the interface for goics
|
|
||||||
func (icalModel IcalModel) EmitICal() goics.Componenter {
|
|
||||||
internalClock := clock.RealClock{}
|
|
||||||
c := generateIcalEmit(icalModel, internalClock)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateIcalEmit(icalModel IcalModel, internalClock clock.Clock) *goics.Component {
|
|
||||||
europeTime, _ := time.LoadLocation("Europe/Berlin")
|
|
||||||
c := goics.NewComponent()
|
|
||||||
c.SetType("VCALENDAR")
|
|
||||||
// PRODID is required by the standard
|
|
||||||
c.AddProperty("PRODID", "-//HTWK Kalender//htwkalender.de//DE")
|
|
||||||
|
|
||||||
c.AddProperty("VERSION", "2.0")
|
|
||||||
c.AddProperty("CALSCALE", "GREGORIAN")
|
|
||||||
c.AddProperty("TZID", "EUROPE/BERLIN")
|
|
||||||
c.AddProperty("X-WR-CALNAME", "HTWK Kalender")
|
|
||||||
c.AddProperty("X-WR-TIMEZONE", "EUROPE/BERLIN")
|
|
||||||
//add v time zone
|
|
||||||
icalModel.vtimezone(c)
|
|
||||||
|
|
||||||
for _, event := range icalModel.Events {
|
|
||||||
mapEntry, mappingFound := icalModel.Mapping[event.UUID]
|
|
||||||
|
|
||||||
s := goics.NewComponent()
|
|
||||||
s.SetType("VEVENT")
|
|
||||||
|
|
||||||
s.AddProperty(goics.FormatDateTime("DTSTAMP", internalClock.Now().Local().In(europeTime)))
|
|
||||||
|
|
||||||
// create a unique id for the event by hashing the event start, end, course and name
|
|
||||||
var eventHash = functions.HashString(event.Start.String() + event.End.String() + event.Course + event.Name + event.Rooms)
|
|
||||||
|
|
||||||
s.AddProperty("UID", eventHash+"@htwkalender.de")
|
|
||||||
s.AddProperty(goics.FormatDateTime("DTEND", event.End.Time().Local().In(europeTime)))
|
|
||||||
s.AddProperty(goics.FormatDateTime("DTSTART", event.Start.Time().Local().In(europeTime)))
|
|
||||||
|
|
||||||
if mappingFound {
|
|
||||||
addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry))
|
|
||||||
addAlarmIfSpecified(s, event, mapEntry, internalClock)
|
|
||||||
} else {
|
|
||||||
addPropertyIfNotEmpty(s, "SUMMARY", event.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
addPropertyIfNotEmpty(s, "DESCRIPTION", generateDescription(event))
|
|
||||||
addPropertyIfNotEmpty(s, "LOCATION", event.Rooms)
|
|
||||||
c.AddComponent(s)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (icalModel IcalModel) vtimezone(c *goics.Component) {
|
|
||||||
tz := goics.NewComponent()
|
|
||||||
tz.SetType("VTIMEZONE")
|
|
||||||
tz.AddProperty("TZID", "EUROPE/BERLIN")
|
|
||||||
//add standard time
|
|
||||||
icalModel.standard(tz)
|
|
||||||
//add daylight time
|
|
||||||
icalModel.daylight(tz)
|
|
||||||
|
|
||||||
c.AddComponent(tz)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (icalModel IcalModel) standard(tz *goics.Component) {
|
|
||||||
st := NewHtwkalenderComponent()
|
|
||||||
st.SetType("STANDARD")
|
|
||||||
st.AddProperty("DTSTART", "19701025T030000")
|
|
||||||
st.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU")
|
|
||||||
st.AddProperty("TZOFFSETFROM", "+0200")
|
|
||||||
st.AddProperty("TZOFFSETTO", "+0100")
|
|
||||||
st.AddProperty("TZNAME", "CET")
|
|
||||||
tz.AddComponent(st)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an override for goics component function Write
|
|
||||||
// to add the RRULE property
|
|
||||||
|
|
||||||
func (icalModel IcalModel) daylight(tz *goics.Component) {
|
|
||||||
dt := NewHtwkalenderComponent()
|
|
||||||
dt.SetType("DAYLIGHT")
|
|
||||||
dt.AddProperty("DTSTART", "19700329T020000")
|
|
||||||
dt.AddProperty("TZOFFSETFROM", "+0100")
|
|
||||||
dt.AddProperty("TZOFFSETTO", "+0200")
|
|
||||||
dt.AddProperty("TZNAME", "CEST")
|
|
||||||
dt.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU")
|
|
||||||
tz.AddComponent(dt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if reminder is specified in the configuration for this event, an alarm will be added to the event
|
|
||||||
func addAlarmIfSpecified(s *goics.Component, event model.Event, mapping model.FeedCollection, clock clock.Clock) {
|
|
||||||
// if event.Start > now
|
|
||||||
// then add alarm
|
|
||||||
if event.Start.Time().Local().After(clock.Now().Local()) && mapping.Reminder {
|
|
||||||
a := goics.NewComponent()
|
|
||||||
a.SetType("VALARM")
|
|
||||||
a.AddProperty("TRIGGER", "-P0DT0H15M0S")
|
|
||||||
a.AddProperty("ACTION", "DISPLAY")
|
|
||||||
a.AddProperty("DESCRIPTION", "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms)
|
|
||||||
s.AddComponent(a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaceNameIfUserDefined replaces the name of the event with the user defined name if it is not empty
|
|
||||||
// all contained template strings will be replaced with the corresponding values from the event
|
|
||||||
func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) string {
|
|
||||||
if !functions.OnlyWhitespace(mapping.UserDefinedName) {
|
|
||||||
return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event)
|
|
||||||
}
|
|
||||||
|
|
||||||
return event.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
|
|
||||||
// or contains only whitespaces
|
|
||||||
func addPropertyIfNotEmpty(component *goics.Component, key string, value string) {
|
|
||||||
if !functions.OnlyWhitespace(value) {
|
|
||||||
component.AddProperty(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateDescription(event model.Event) string {
|
|
||||||
var description string
|
|
||||||
|
|
||||||
if !functions.OnlyWhitespace(event.Prof) {
|
|
||||||
description += "Profs: " + event.Prof + "\n"
|
|
||||||
}
|
|
||||||
if !functions.OnlyWhitespace(event.Course) {
|
|
||||||
description += "Gruppen: " + event.Course + "\n"
|
|
||||||
}
|
|
||||||
if !functions.OnlyWhitespace(event.EventType) {
|
|
||||||
description += "Typ: " + event.EventType + event.Compulsory + "\n"
|
|
||||||
}
|
|
||||||
if !functions.OnlyWhitespace(event.Notes) {
|
|
||||||
description += "Notizen: " + event.Notes + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return description
|
|
||||||
}
|
|
@@ -1,258 +0,0 @@
|
|||||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
|
||||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
|
||||||
|
|
||||||
//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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ical
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jordic/goics"
|
|
||||||
"htwkalender/data-manager/model"
|
|
||||||
mockTime "htwkalender/data-manager/service/functions/time"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIcalModel_EmitICal(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Events model.Events
|
|
||||||
Mapping map[string]model.FeedCollection
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want *goics.Component
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test EmitICal",
|
|
||||||
fields: fields{
|
|
||||||
Events: model.Events{
|
|
||||||
{
|
|
||||||
UUID: "123",
|
|
||||||
Name: "Test",
|
|
||||||
EventType: "Test",
|
|
||||||
Notes: "Test",
|
|
||||||
Prof: "Test",
|
|
||||||
Rooms: "Test",
|
|
||||||
BookedAt: "Test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Mapping: map[string]model.FeedCollection{
|
|
||||||
"123": {
|
|
||||||
UUID: "123",
|
|
||||||
Name: "Test",
|
|
||||||
Course: "Test",
|
|
||||||
UserDefinedName: "Test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &goics.Component{
|
|
||||||
Tipo: "VCALENDAR",
|
|
||||||
Elements: []goics.Componenter{
|
|
||||||
&goics.Component{
|
|
||||||
Tipo: "VTIMEZONE",
|
|
||||||
Elements: []goics.Componenter{
|
|
||||||
&HtwkalenderComponent{
|
|
||||||
Component: &goics.Component{
|
|
||||||
Tipo: "STANDARD",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTART": {"19701025T030000"},
|
|
||||||
"RRULE": {"FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU"},
|
|
||||||
"TZOFFSETFROM": {"+0200"},
|
|
||||||
"TZOFFSETTO": {"+0100"},
|
|
||||||
"TZNAME": {"CET"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&HtwkalenderComponent{
|
|
||||||
Component: &goics.Component{
|
|
||||||
Tipo: "DAYLIGHT",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTART": {"19700329T020000"},
|
|
||||||
"TZOFFSETFROM": {"+0100"},
|
|
||||||
"TZOFFSETTO": {"+0200"},
|
|
||||||
"TZNAME": {"CEST"},
|
|
||||||
"RRULE": {"FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"TZID": {"EUROPE/BERLIN"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&goics.Component{
|
|
||||||
Tipo: "VEVENT",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTAMP": {"20231201T000000Z"},
|
|
||||||
"UID": {"a8d627d93f518e9096b6f40e36d27b7660fa26d318ef1adc43da750e49ebe4be@htwkalender.de"},
|
|
||||||
"DTEND": {"00010101T000000Z"},
|
|
||||||
"DTSTART": {"00010101T000000Z"},
|
|
||||||
"SUMMARY": {"Test"},
|
|
||||||
"DESCRIPTION": {"Profs: Test\nTyp: Test\nNotizen: Test\n"},
|
|
||||||
"LOCATION": {"Test"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"PRODID": {"-//HTWK Kalender//htwkalender.de//DE"},
|
|
||||||
"VERSION": {"2.0"},
|
|
||||||
"CALSCALE": {"GREGORIAN"},
|
|
||||||
"TZID": {"EUROPE/BERLIN"},
|
|
||||||
"X-WR-CALNAME": {"HTWK Kalender"},
|
|
||||||
"X-WR-TIMEZONE": {"EUROPE/BERLIN"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Similar Events like Sport Courses",
|
|
||||||
fields: fields{
|
|
||||||
Events: model.Events{
|
|
||||||
{
|
|
||||||
UUID: "123",
|
|
||||||
Name: "Test",
|
|
||||||
Course: "Test",
|
|
||||||
EventType: "Test",
|
|
||||||
Notes: "Test",
|
|
||||||
Prof: "Test",
|
|
||||||
Rooms: "ZU430",
|
|
||||||
BookedAt: "Test",
|
|
||||||
Start: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
|
|
||||||
End: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 1, 0, 0, 0, time.UTC)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
UUID: "123",
|
|
||||||
Name: "Test",
|
|
||||||
Course: "Test",
|
|
||||||
EventType: "Test",
|
|
||||||
Notes: "Test",
|
|
||||||
Prof: "Test",
|
|
||||||
Rooms: "ZU221",
|
|
||||||
BookedAt: "Test",
|
|
||||||
Start: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
|
|
||||||
End: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 1, 0, 0, 0, time.UTC)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Mapping: map[string]model.FeedCollection{
|
|
||||||
"123": {
|
|
||||||
UUID: "123",
|
|
||||||
Name: "Test",
|
|
||||||
Course: "Test",
|
|
||||||
UserDefinedName: "UserDefinedName",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &goics.Component{
|
|
||||||
Tipo: "VCALENDAR",
|
|
||||||
Elements: []goics.Componenter{
|
|
||||||
&goics.Component{
|
|
||||||
Tipo: "VTIMEZONE",
|
|
||||||
Elements: []goics.Componenter{
|
|
||||||
&HtwkalenderComponent{
|
|
||||||
Component: &goics.Component{
|
|
||||||
Tipo: "STANDARD",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTART": {"19701025T030000"},
|
|
||||||
"RRULE": {"FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU"},
|
|
||||||
"TZOFFSETFROM": {"+0200"},
|
|
||||||
"TZOFFSETTO": {"+0100"},
|
|
||||||
"TZNAME": {"CET"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&HtwkalenderComponent{
|
|
||||||
Component: &goics.Component{
|
|
||||||
Tipo: "DAYLIGHT",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTART": {"19700329T020000"},
|
|
||||||
"TZOFFSETFROM": {"+0100"},
|
|
||||||
"TZOFFSETTO": {"+0200"},
|
|
||||||
"TZNAME": {"CEST"},
|
|
||||||
"RRULE": {"FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"TZID": {"EUROPE/BERLIN"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&goics.Component{
|
|
||||||
Tipo: "VEVENT",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTAMP": {"20231201T000000Z"},
|
|
||||||
"UID": {"b52a7a081f46eeba9b402114493278a34a48b572c84e53d7ac4da9dea15cdff2@htwkalender.de"},
|
|
||||||
"DTEND": {"20231201T010000Z"},
|
|
||||||
"DTSTART": {"20231201T000000Z"},
|
|
||||||
"SUMMARY": {"UserDefinedName"},
|
|
||||||
"DESCRIPTION": {"Profs: Test\nGruppen: Test\nTyp: Test\nNotizen: Test\n"},
|
|
||||||
"LOCATION": {"ZU430"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&goics.Component{
|
|
||||||
Tipo: "VEVENT",
|
|
||||||
Elements: []goics.Componenter{},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"DTSTAMP": {"20231201T000000Z"},
|
|
||||||
"UID": {"5e946c0c4474bc6e6337262093e3ef31477e026bbc6bab398d755b002506d9d7@htwkalender.de"},
|
|
||||||
"DTEND": {"20231201T010000Z"},
|
|
||||||
"DTSTART": {"20231201T000000Z"},
|
|
||||||
"SUMMARY": {"UserDefinedName"},
|
|
||||||
"DESCRIPTION": {"Profs: Test\nGruppen: Test\nTyp: Test\nNotizen: Test\n"},
|
|
||||||
"LOCATION": {"ZU221"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Properties: map[string][]string{
|
|
||||||
"PRODID": {"-//HTWK Kalender//htwkalender.de//DE"},
|
|
||||||
"VERSION": {"2.0"},
|
|
||||||
"CALSCALE": {"GREGORIAN"},
|
|
||||||
"TZID": {"EUROPE/BERLIN"},
|
|
||||||
"X-WR-CALNAME": {"HTWK Kalender"},
|
|
||||||
"X-WR-TIMEZONE": {"EUROPE/BERLIN"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
icalModel := IcalModel{
|
|
||||||
Events: tt.fields.Events,
|
|
||||||
Mapping: tt.fields.Mapping,
|
|
||||||
}
|
|
||||||
|
|
||||||
mockClock := mockTime.MockClock{
|
|
||||||
NowTime: time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC),
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := generateIcalEmit(icalModel, mockClock); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("EmitICal() = \n%v, want \n%v", got, tt.want)
|
|
||||||
|
|
||||||
// Print the differences
|
|
||||||
for i, element := range got.Elements {
|
|
||||||
if !reflect.DeepEqual(element, tt.want.Elements[i]) {
|
|
||||||
t.Errorf("Element %d: got \n%v, want \n%v", i, element, tt.want.Elements[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
|
||||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
|
||||||
|
|
||||||
//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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ical
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jordic/goics"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HtwkalenderComponent struct {
|
|
||||||
*goics.Component
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHtwkalenderComponent() *HtwkalenderComponent {
|
|
||||||
return &HtwkalenderComponent{
|
|
||||||
Component: goics.NewComponent(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes the component to the Writer
|
|
||||||
func (c *HtwkalenderComponent) Write(w *goics.ICalEncode) {
|
|
||||||
w.WriteLine("BEGIN:" + c.Tipo + goics.CRLF)
|
|
||||||
|
|
||||||
// Iterate over component properties
|
|
||||||
var keys []string
|
|
||||||
for k := range c.Properties {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
for _, key := range keys {
|
|
||||||
vals := c.Properties[key]
|
|
||||||
for _, val := range vals {
|
|
||||||
w.WriteLine(WriteStringField(key, val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, xc := range c.Elements {
|
|
||||||
xc.Write(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteLine("END:" + c.Tipo + goics.CRLF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteStringField UID:asdfasdfаs@dfasdf.com
|
|
||||||
func WriteStringField(key string, val string) string {
|
|
||||||
return strings.ToUpper(key) + ":" + (val) + goics.CRLF
|
|
||||||
}
|
|
Reference in New Issue
Block a user