From c00990dd8ee690c1777f3aeabe848047d4f40f68 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Tue, 24 Sep 2024 16:39:23 +0200 Subject: [PATCH 1/9] feat:#49 added new ical lib for description formatting --- services/data-manager/Dockerfile | 2 +- services/go.mod | 1 + services/go.sum | 10 + services/ical/Dockerfile | 2 +- services/ical/main.go | 7 +- services/ical/service/ical/ical.go | 9 +- .../service/ical/icalDescriptionGenerator.go | 153 ++++++++++++ .../ical/service/ical/icalFileGeneration.go | 229 +++++++++--------- services/ical/service/routes.go | 8 +- 9 files changed, 299 insertions(+), 122 deletions(-) create mode 100644 services/ical/service/ical/icalDescriptionGenerator.go diff --git a/services/data-manager/Dockerfile b/services/data-manager/Dockerfile index 9e52f5d..0799b2d 100644 --- a/services/data-manager/Dockerfile +++ b/services/data-manager/Dockerfile @@ -50,7 +50,7 @@ EXPOSE 8090 ENTRYPOINT ["./main", "serve"] -FROM golang:1.21.6 AS dev +FROM golang:1.23 AS dev # Set the Current Working Directory inside the container WORKDIR /htwkalender-data-manager diff --git a/services/go.mod b/services/go.mod index 0b5f256..94733a1 100644 --- a/services/go.mod +++ b/services/go.mod @@ -24,6 +24,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/arran4/golang-ical v0.3.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect diff --git a/services/go.sum b/services/go.sum index 66c6218..6e16631 100644 --- a/services/go.sum +++ b/services/go.sum @@ -73,6 +73,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/arran4/golang-ical v0.3.1 h1:v13B3eQZ9VDHTAvT6M11vVzxYgcYmjyPBE2eAZl3VZk= +github.com/arran4/golang-ical v0.3.1/go.mod h1:LZWxF8ZIu/sjBVUCV0udiVPrQAgq3V0aa0RfbO99Qkk= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -139,6 +141,7 @@ github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnTh github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -267,6 +270,8 @@ github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2 github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -293,6 +298,7 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -330,6 +336,7 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -491,9 +498,12 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/services/ical/Dockerfile b/services/ical/Dockerfile index 19d231c..446d796 100644 --- a/services/ical/Dockerfile +++ b/services/ical/Dockerfile @@ -49,7 +49,7 @@ EXPOSE 8091 ENTRYPOINT ["./main"] -FROM golang:1.21.6 AS dev +FROM golang:1.23 AS dev # Set the Current Working Directory inside the container WORKDIR /htwkalender-ical diff --git a/services/ical/main.go b/services/ical/main.go index 1769786..0b8b19f 100644 --- a/services/ical/main.go +++ b/services/ical/main.go @@ -62,7 +62,12 @@ func main() { DataManagerURL: "http://" + host + ":8090", } - fiberApp.Use(logger.New()) + fiberApp.Use(logger.New( + logger.Config{ + Format: "[${time}] ${status} - ${latency} ${method} ${path} ${error}\n", + TimeFormat: "02-01-2006 15:04:05", + }, + )) // Add routes to the app instance for the data-manager ical service service.AddFeedRoutes(app) diff --git a/services/ical/service/ical/ical.go b/services/ical/service/ical/ical.go index ba8e7f2..6a331d5 100644 --- a/services/ical/service/ical/ical.go +++ b/services/ical/service/ical/ical.go @@ -17,9 +17,7 @@ package ical import ( - "bytes" "fmt" - "github.com/jordic/goics" "htwkalender/ical/model" "htwkalender/ical/service/connector" htwkalenderGrpc "htwkalender/ical/service/connector/grpc" @@ -31,7 +29,7 @@ const expirationTime = 5 * time.Minute var FeedDeletedError = fmt.Errorf("feed deleted") -func Feed(app model.AppType, token string) (string, error) { +func Feed(app model.AppType, token string, userAgent string) (string, error) { var events model.Events modules := map[string]model.FeedCollection{} @@ -68,9 +66,8 @@ func Feed(app model.AppType, token string) (string, error) { } } - b := bytes.Buffer{} - goics.NewICalEncode(&b).Encode(IcalModel{Events: events, Mapping: modules}) - icalFeed := &model.FeedModel{Content: b.String(), ExpiresAt: model.JSONTime(time.Now().Add(expirationTime))} + cal := GenerateIcalFeed(events, modules, userAgent) + icalFeed := &model.FeedModel{Content: cal.Serialize(), ExpiresAt: model.JSONTime(time.Now().Add(expirationTime))} return icalFeed.Content, nil } diff --git a/services/ical/service/ical/icalDescriptionGenerator.go b/services/ical/service/ical/icalDescriptionGenerator.go new file mode 100644 index 0000000..d88559c --- /dev/null +++ b/services/ical/service/ical/icalDescriptionGenerator.go @@ -0,0 +1,153 @@ +//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 . + +package ical + +import ( + ics "github.com/arran4/golang-ical" + "htwkalender/ical/model" + "htwkalender/ical/service/functions" + "net/url" + "strings" + _ "time/tzdata" +) + +// Generates user-agent specific description and adds it to the calendar event. +func generateUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, userAgent string) { + // Generate description and ALTREP (alternative representation) based on user agent. + description, altrep := generateDescription(event, userAgent) + + if isThunderbird(userAgent) && altrep != "" { + // Thunderbird-specific handling: Add DESCRIPTION with ALTREP attribute. + // create property altrep + altrepParam := WithAltRep(altrep) + + vEvent.AddProperty(ics.ComponentPropertyDescription, description, altrepParam) + AddProperty(ics.ComponentPropertyDescription, description, altrepParam, vEvent) + } else { + // Default handling: Add plain DESCRIPTION property for other user agents. + vEvent.AddProperty("DESCRIPTION", description) + } + +} + +func AddProperty(property ics.ComponentProperty, value string, prop ics.PropertyParameter, cb *ics.VEvent) { + baseProperty := &ics.BaseProperty{ + IANAToken: string(property), + Value: value, + ICalParameters: map[string][]string{}, + } + + customProperty := NewCustomProperty(*baseProperty) + + r := ics.IANAProperty{ + BaseProperty: customProperty, + } + k, v := prop.KeyValue() + r.ICalParameters[k] = v + cb.Properties = append(cb.Properties, r) +} + +// Generates description based on the event details and user agent. +func generateDescription(event model.Event, userAgent string) (string, string) { + plainDescription := buildPlainTextDescription(event) + + if isThunderbird(userAgent) { + // Thunderbird-specific: Generate HTML and ALTREP format for Thunderbird users. + htmlDescription := generateThunderbirdHTMLDescription(event) + altrep := `"data:text/html,` + url.PathEscape(htmlDescription) + `"` + return plainDescription, altrep + } + + // Google Calendar-specific handling. + if isGoogleCalendar(userAgent) { + plainDescription += generateGoogleCalendarDescription(event.Rooms) + } + + // Default: Return plain text description without ALTREP for non-Thunderbird cases. + return plainDescription, "" +} + +// Builds the plain text description from the event details. +func buildPlainTextDescription(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 +} + +// Helper function to generate HTML description for Thunderbird's ALTREP. +func generateThunderbirdHTMLDescription(event model.Event) string { + var htmlDescription strings.Builder + + // HTML-encoded description similar to plain text. + if !functions.OnlyWhitespace(event.Prof) { + htmlDescription.WriteString("Profs: " + event.Prof + "
") + } + if !functions.OnlyWhitespace(event.Course) { + htmlDescription.WriteString("Gruppen: " + event.Course + "
") + } + if !functions.OnlyWhitespace(event.EventType) { + htmlDescription.WriteString("Typ: " + event.EventType + event.Compulsory + "
") + } + + // Add the HTML link to the room map. + htmlDescription.WriteString(`Link: HTWK-Karte`) + + return htmlDescription.String() +} + +// Generates a room description with links for Google Calendar. +func generateGoogleCalendarDescription(rooms string) string { + var description strings.Builder + roomList := strings.Split(rooms, " ") + description.WriteString("Orte: \n ") + + for _, room := range roomList { + description.WriteString("HTWK-Karte\n") + } + + return description.String() +} + +// Checks if the user agent is Thunderbird. +func isThunderbird(userAgent string) bool { + return strings.Contains(userAgent, "Thunderbird") +} + +// Checks if the user agent is Google Calendar. +func isGoogleCalendar(userAgent string) bool { + return strings.Contains(userAgent, "Google-Calendar-Importer") +} + +func WithAltRep(altRepUrl string) ics.PropertyParameter { + return &ics.KeyValues{ + Key: string(ics.ParameterAltrep), + Value: []string{altRepUrl}, + } +} diff --git a/services/ical/service/ical/icalFileGeneration.go b/services/ical/service/ical/icalFileGeneration.go index 9af0c60..fed15df 100644 --- a/services/ical/service/ical/icalFileGeneration.go +++ b/services/ical/service/ical/icalFileGeneration.go @@ -17,121 +17,153 @@ package ical import ( + ics "github.com/arran4/golang-ical" "htwkalender/ical/model" "htwkalender/ical/service/functions" clock "htwkalender/ical/service/functions/time" "htwkalender/ical/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 -} +func GenerateIcalFeed(events model.Events, mapping map[string]model.FeedCollection, userAgent string) *ics.Calendar { -// EmitICal implements the interface for goics -func (icalModel IcalModel) EmitICal() goics.Componenter { - internalClock := clock.RealClock{} - c := generateIcalEmit(icalModel, internalClock) - return c -} + cal := ics.NewCalendarFor("HTWK Kalender") + cal.SetMethod(ics.MethodPublish) + cal.SetProductId("-//HTWK Kalender//htwkalender.de//DE") + cal.SetTzid("Europe/Berlin") + cal.SetXWRCalName("HTWK Kalender") + cal.SetXWRTimezone("Europe/Berlin") + cal.SetVersion("2.0") + cal.SetCalscale("GREGORIAN") + + vTimeZone := ics.NewTimezone("Europe/Berlin") + + vTimeZone.Components = []ics.Component{ + &ics.Daylight{ + ComponentBase: ics.ComponentBase{ + Properties: []ics.IANAProperty{ + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyDtstart), + Value: "19700329T020000", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyRrule), + Value: "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzname), + Value: "CEST", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzoffsetfrom), + Value: "+0100", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzoffsetto), + Value: "+0200", + }, + }, + }, + }, + }, + &ics.Standard{ + ComponentBase: ics.ComponentBase{ + Properties: []ics.IANAProperty{ + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyDtstart), + Value: "19701025T030000", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyRrule), + Value: "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzname), + Value: "CET", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzoffsetfrom), + Value: "+0200", + }, + }, + { + BaseProperty: ics.BaseProperty{ + IANAToken: string(ics.PropertyTzoffsetto), + Value: "+0100", + }, + }, + }, + }, + }, + } + + cal.AddVTimezone(vTimeZone) -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") + internalClock := clock.RealClock{} - 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 events { + mapEntry, mappingFound := mapping[event.UUID] - 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(time.Time(event.Start).String() + time.Time(event.End).String() + event.Course + event.Name + event.Rooms) - s.AddProperty("UID", eventHash+"@htwkalender.de") - s.AddProperty(goics.FormatDateTime("DTEND", time.Time(event.End).Local().In(europeTime))) - s.AddProperty(goics.FormatDateTime("DTSTART", time.Time(event.Start).Local().In(europeTime))) + icalEvent := ics.NewEvent(eventHash + "@htwkalender.de") + + icalEvent.SetDtStampTime(internalClock.Now().Local().In(europeTime)) + icalEvent.SetStartAt(time.Time(event.Start).Local().In(europeTime)) + icalEvent.SetEndAt(time.Time(event.End).Local().In(europeTime)) if mappingFound { - addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry)) - addAlarmIfSpecified(s, event, mapEntry, internalClock) + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, replaceNameIfUserDefined(&event, mapEntry)) + addAlarmIfSpecified(icalEvent, event, mapEntry, internalClock) } else { - addPropertyIfNotEmpty(s, "SUMMARY", event.Name) + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, event.Name) } - addPropertyIfNotEmpty(s, "DESCRIPTION", generateDescription(event)) - addPropertyIfNotEmpty(s, "LOCATION", event.Rooms) - c.AddComponent(s) + generateUserAgentSpecificDescription(icalEvent, event, userAgent) + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertyLocation, event.Rooms) + + cal.AddVEvent(icalEvent) + } - return c + return cal } -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) +// AddPropertyIfNotEmpty adds a property to the component if the value is not empty +// or contains only whitespaces +func addPropertyIfNotEmpty(component *ics.VEvent, key ics.ComponentProperty, value string) { + if !functions.OnlyWhitespace(value) { + component.AddProperty(key, value) + } } // 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) { +func addAlarmIfSpecified(icalEvent *ics.VEvent, event model.Event, mapping model.FeedCollection, clock clock.Clock) { // if event.Start > now // then add alarm if time.Time(event.Start).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) + alarm := icalEvent.AddAlarm() + alarm.AddProperty(ics.ComponentPropertyTrigger, "-P0DT0H15M0S") + alarm.AddProperty(ics.ComponentPropertyAction, "DISPLAY") + alarm.AddProperty(ics.ComponentPropertyDescription, "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms) } } @@ -144,30 +176,3 @@ func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) 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 -} diff --git a/services/ical/service/routes.go b/services/ical/service/routes.go index c92a0f7..09ffc9e 100644 --- a/services/ical/service/routes.go +++ b/services/ical/service/routes.go @@ -34,7 +34,13 @@ func AddFeedRoutes(app model.AppType) { app.Fiber.Get("/api/feed", func(c fiber.Ctx) error { token := c.Query("token") - results, err := ical.Feed(app, token) + + // get request userAgent and check if it is Thunderbird + userAgent := c.Get("User-Agent") + + slog.Info("User-Agent", "userAgent", userAgent) + + results, err := ical.Feed(app, token, userAgent) if errors.Is(err, ical.FeedDeletedError) { return c.SendStatus(fiber.StatusGone) From b8dd4338b30e1ca31d7cc5b899bb720735e3306b Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Tue, 15 Oct 2024 14:59:33 +0200 Subject: [PATCH 2/9] feat:#49 switch to new branch for ical altrep --- services/go.mod | 2 +- services/go.sum | 143 +----------------- .../service/ical/icalDescriptionGenerator.go | 29 +--- 3 files changed, 12 insertions(+), 162 deletions(-) diff --git a/services/go.mod b/services/go.mod index 94733a1..6e08859 100644 --- a/services/go.mod +++ b/services/go.mod @@ -6,6 +6,7 @@ toolchain go1.23.1 require ( github.com/PuerkitoBio/goquery v1.10.0 + github.com/arran4/golang-ical v0.3.2-0.20241015053101-5bb438cf85f5 github.com/goccy/go-json v0.10.3 github.com/gofiber/fiber/v3 v3.0.0-beta.3 github.com/google/uuid v1.6.0 @@ -24,7 +25,6 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/arran4/golang-ical v0.3.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect diff --git a/services/go.sum b/services/go.sum index 6e16631..35e5d33 100644 --- a/services/go.sum +++ b/services/go.sum @@ -1,5 +1,3 @@ -cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= -cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= @@ -9,62 +7,15 @@ cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= -cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= -cloud.google.com/go/kms v1.18.5 h1:75LSlVs60hyHK3ubs2OHd4sE63OAMcM2BdSJc2bkuM4= -cloud.google.com/go/kms v1.18.5/go.mod h1:yXunGUGzabH8rjUPImp2ndHiGolHeWJJ0LODLedicIY= -cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= -cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= -cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MHNeC0E= -cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= -cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= -cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= -cloud.google.com/go/secretmanager v1.13.6 h1:0ZEl/LuoB4xQsjVfQt3Gi/dZfOv36n4JmdPrMargzYs= -cloud.google.com/go/secretmanager v1.13.6/go.mod h1:x2ySyOrqv3WGFRFn2Xk10iHmNmvmcEVSSqc30eb1bhw= cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= -cloud.google.com/go/trace v1.10.12 h1:GoGZv1iAXEa73HgSGNjRl2vKqp5/f2AeKqErRFXA2kg= -cloud.google.com/go/trace v1.10.12/go.mod h1:tYkAIta/gxgbBZ/PIzFxSH5blajgX4D00RpQqCG/GZs= -contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec h1:CSNP8nIEQt4sZEo2sGUiWSmVJ9c5QdyIQvwzZAsn+8Y= -contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= -contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4= -contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc= -contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60= -contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= -github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2htVQTBY8nOZpyajYztF0vUvSZTuM= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= -github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= -github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 h1:o/Ws6bEqMeKZUfj1RRm3mQ51O8JGU5w+Qdg2AhHib6A= -github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1/go.mod h1:6QAMYBAbQeeKX+REFJMZ1nFWu9XLw/PPcjYpuc9RDFs= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E= -github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= -github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbOrtjYTXPtWNSyOFlcxkBU= -github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= @@ -73,8 +24,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/arran4/golang-ical v0.3.1 h1:v13B3eQZ9VDHTAvT6M11vVzxYgcYmjyPBE2eAZl3VZk= -github.com/arran4/golang-ical v0.3.1/go.mod h1:LZWxF8ZIu/sjBVUCV0udiVPrQAgq3V0aa0RfbO99Qkk= +github.com/arran4/golang-ical v0.3.2-0.20241015053101-5bb438cf85f5 h1:aIT5x2Cjcg/bioaV8mc1jpDOiousU0alvWTNIcX+F5E= +github.com/arran4/golang-ical v0.3.2-0.20241015053101-5bb438cf85f5/go.mod h1:xblDGxxIUMWwFZk9dlECUlc1iXNV65LJZOTHLVwu8bo= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -108,18 +59,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsd github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 h1:u+EfGmksnJc/x5tq3A+OD7LrMbSSR/5TrKLvkdy/fhY= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17/go.mod h1:VaMx6302JHax2vHJWgRo+5n9zvbacs3bLU/23DNQrTY= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8= github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 h1:Kp6PWAlXwP1UvIflkIP6MFZYBNDCa4mFCGtxrpICVOg= github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2/go.mod h1:5FmD/Dqq57gP+XwaUnd5WFPipAuzrf0HmupX27Gvjvc= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4 h1:NgRFYyFpiMD62y4VPXh4DosPFbZd4vdMVBWKk0VmWXc= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4/go.mod h1:TKKN7IQoM7uTnyuFm9bm9cw5P//ZYTl4m3htBWQ1G/c= -github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 h1:eSTEdxkfle2G98FE+Xl3db/XAXXVTJPNQo9K/Ar8oAI= -github.com/aws/aws-sdk-go-v2/service/sns v1.31.3/go.mod h1:1dn0delSO3J69THuty5iwP0US2Glt0mx2qBBlI13pvw= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz7y/WXbN3RgOoVrc= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI= -github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7M5lu8PqdvOmbRHw= -github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE= github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 h1:pIaGg+08llrP7Q5aiz9ICWbY8cqhTkyy+0SHvfzQpTc= github.com/aws/aws-sdk-go-v2/service/sso v1.22.7/go.mod h1:eEygMHnTKH/3kNp9Jr1n3PdejuSNcgwLe1dWgQtO0VQ= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1KvQzkzPyiKUdlWJqy+J4= @@ -129,19 +70,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlh github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -149,32 +80,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= -github.com/dop251/goja v0.0.0-20240822155948-fa6d1ed5e4b6 h1:0x8Sh2rKCTVUQnRTJFIwtRWAp91VMsnATQEsMAg14kM= -github.com/dop251/goja v0.0.0-20240822155948-fa6d1ed5e4b6/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= -github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc h1:MKYt39yZJi0Z9xEeRmDX2L4ocE0ETKcHKw6MVL3R+co= -github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc/go.mod h1:VULptt4Q/fNzQUJlqY/GP3qHyU7ZH46mFkBZe0ZTokU= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc= -github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k= @@ -185,8 +104,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -199,15 +116,10 @@ github.com/gofiber/utils/v2 v2.0.0-beta.6 h1:ED62bOmpRXdgviPlfTmf0Q+AXzhaTUAFtdW github.com/gofiber/utils/v2 v2.0.0-beta.6/go.mod h1:3Kz8Px3jInKFvqxDzDeoSygwEOO+3uyubTmUa6PqY+0= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -229,12 +141,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= -github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= -github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= -github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= -github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= -github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= @@ -248,7 +154,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2 github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= @@ -260,7 +165,6 @@ github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInw github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/jordic/goics v0.0.0-20210404174824-5a0337b716a0 h1:p+k2RozdR141dIkAbOuZafkZjrcjT/YvwYYH7qCSG+c= github.com/jordic/goics v0.0.0-20210404174824-5a0337b716a0/go.mod h1:YHaw6sOIeFRob8Y9q/blEAMfVcLpeE9+vdhrwyEMxoI= @@ -270,17 +174,11 @@ github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2 github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4= github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -298,30 +196,19 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA= github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= github.com/pocketbase/pocketbase v0.22.20 h1:yUkhO5bTPWlzD4ZK6EQlS4R3AcHKDlBD+DxxU2BR83I= github.com/pocketbase/pocketbase v0.22.20/go.mod h1:Cw5E4uoGhKItBIE2lJL3NfmiUr9Syk2xaNJ2G7Dssow= -github.com/pocketbase/tygoja v0.0.0-20240113091827-17918475d342 h1:OcAwewen3hs/zY8i0syt8CcMTGBJhQwQRVDLcoQVXVk= -github.com/pocketbase/tygoja v0.0.0-20240113091827-17918475d342/go.mod h1:dOJ+pCyqm/jRn5kO/TX598J0e5xGDcJAZerK5atCrKI= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/prometheus v0.54.0 h1:6+VmEkohHcofl3W5LyRlhw1Lfm575w/aX6ZFyVAmzM0= -github.com/prometheus/prometheus v0.54.0/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -336,14 +223,11 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= @@ -352,7 +236,6 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= @@ -366,10 +249,6 @@ go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2 go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -378,14 +257,11 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -465,7 +341,6 @@ google.golang.org/api v0.196.0 h1:k/RafYqebaIJBO3+SMnfEGtFVlvp5vSgqTUF54UN/zg= google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -474,8 +349,6 @@ google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eY google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988 h1:+/tmTy5zAieooKIXfzDm9KiA3Bv6JBwriRN9LY+yayk= google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988/go.mod h1:4+X6GvPs+25wZKbQq9qyAXrwIRExv7w0Ea6MgZLZiDM= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240903143218-8af14fe29dc1 h1:W0PHii1rtgc5UgBtJif8xGePValKeZRomnuC5hatKME= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:q0eWNnCW04EJlyrmLT+ZHsjuoUiZ36/eAEdCCezZoco= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -496,27 +369,17 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= -modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v3 v3.17.0 h1:o3OmOqx4/OFnl4Vm3G8Bgmqxnvxnh0nbxeT5p/dWChA= -modernc.org/ccgo/v3 v3.17.0/go.mod h1:Sg3fwVpmLvCUTaqEUjiBDAvshIaKDB0RXaf+zgqFu8I= modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4= modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= diff --git a/services/ical/service/ical/icalDescriptionGenerator.go b/services/ical/service/ical/icalDescriptionGenerator.go index d88559c..3717168 100644 --- a/services/ical/service/ical/icalDescriptionGenerator.go +++ b/services/ical/service/ical/icalDescriptionGenerator.go @@ -33,10 +33,14 @@ func generateUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, if isThunderbird(userAgent) && altrep != "" { // Thunderbird-specific handling: Add DESCRIPTION with ALTREP attribute. // create property altrep - altrepParam := WithAltRep(altrep) + //altrepParam := WithAltRep(altrep) - vEvent.AddProperty(ics.ComponentPropertyDescription, description, altrepParam) - AddProperty(ics.ComponentPropertyDescription, description, altrepParam, vEvent) + uri := &url.URL{ + Scheme: "data", + Opaque: altrep, + } + + vEvent.AddProperty(ics.ComponentPropertyDescription, description, ics.WithAlternativeRepresentation(uri)) } else { // Default handling: Add plain DESCRIPTION property for other user agents. vEvent.AddProperty("DESCRIPTION", description) @@ -44,23 +48,6 @@ func generateUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, } -func AddProperty(property ics.ComponentProperty, value string, prop ics.PropertyParameter, cb *ics.VEvent) { - baseProperty := &ics.BaseProperty{ - IANAToken: string(property), - Value: value, - ICalParameters: map[string][]string{}, - } - - customProperty := NewCustomProperty(*baseProperty) - - r := ics.IANAProperty{ - BaseProperty: customProperty, - } - k, v := prop.KeyValue() - r.ICalParameters[k] = v - cb.Properties = append(cb.Properties, r) -} - // Generates description based on the event details and user agent. func generateDescription(event model.Event, userAgent string) (string, string) { plainDescription := buildPlainTextDescription(event) @@ -68,7 +55,7 @@ func generateDescription(event model.Event, userAgent string) (string, string) { if isThunderbird(userAgent) { // Thunderbird-specific: Generate HTML and ALTREP format for Thunderbird users. htmlDescription := generateThunderbirdHTMLDescription(event) - altrep := `"data:text/html,` + url.PathEscape(htmlDescription) + `"` + altrep := `text/html,` + url.PathEscape(htmlDescription) return plainDescription, altrep } From f971a97378f50736e739889f067a1c6e40b7b4ff Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Thu, 17 Oct 2024 10:39:17 +0200 Subject: [PATCH 3/9] feat:#49 added multiple room linkout and mapping for map --- .../ical/service/functions/roomMapping.go | 40 ++++++++++ .../service/functions/roomMapping_test.go | 73 +++++++++++++++++++ .../service/ical/icalDescriptionGenerator.go | 30 ++++---- 3 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 services/ical/service/functions/roomMapping.go create mode 100644 services/ical/service/functions/roomMapping_test.go diff --git a/services/ical/service/functions/roomMapping.go b/services/ical/service/functions/roomMapping.go new file mode 100644 index 0000000..8163c9b --- /dev/null +++ b/services/ical/service/functions/roomMapping.go @@ -0,0 +1,40 @@ +package functions + +import ( + "regexp" + "strings" +) + +func MapRoom(room string, output bool) string { + // remove dots from room string + + if output { + //replace second point in TR_A1.23.1 -> TR_A1.23-1 with a minus + re := regexp.MustCompile(`\b[TR_]+[A-ZÄÖÜ]?[0-9]{1,4}[.][0-9]{1,4}[.]\b`) + room = re.ReplaceAllStringFunc(room, func(match string) string { + return match[:len(match)-1] + "-" + match[len(match)-1:] + }) + // If the output flag is set, remove all dots from the room string + room = strings.ReplaceAll(room, ".", "") + } else { + // If the output flag is false add a dot for all rooms with regexp TR_A123 -> TR_A1.23 + re := regexp.MustCompile(`\bTR_+[A-ZÄÖÜ]?[0-9]{1,4}[a-zäöü]?\b`) + room = re.ReplaceAllStringFunc(room, func(match string) string { + return match[:len(match)-2] + "." + match[len(match)-2:] + }) + room = strings.ReplaceAll(room, "-", ".") + } + + // Regular expression pattern to match room identifiers + // The pattern looks for strings that start with two uppercase letters, optionally followed by an underscore, + // followed by 1 to 3 digits, and ending with "-[A-Z]" + re := regexp.MustCompile(`\b[A-ZÄÖÜ]{2}([_]+[A-ZÄÖÜ])?[0-9]{1,4}[a-zäöü]?[-]?[0-9]?-[A-ZÄÖÜ]\b`) + + // Use the ReplaceAllStringFunc to process each match + room = re.ReplaceAllStringFunc(room, func(match string) string { + // Remove the last two characters (i.e., "-") + return match[:len(match)-2] + }) + + return room +} diff --git a/services/ical/service/functions/roomMapping_test.go b/services/ical/service/functions/roomMapping_test.go new file mode 100644 index 0000000..b4e36ea --- /dev/null +++ b/services/ical/service/functions/roomMapping_test.go @@ -0,0 +1,73 @@ +package functions + +import "testing" + +func TestMapRoom(t *testing.T) { + type args struct { + room string + output bool + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Test 1 MapRoom", + args: args{ + room: "H.1.1", + output: true, + }, + want: "H11", + }, + { + name: "Test Treftsbau MapRoom", + args: args{ + room: "TR_L3.03-S,TR_L3.02-S,TR_L2.13-L,TR_L2.05-S,TR_L1.14-H,TR_L1.07-H,TR_L1.06-B,TR_L0.14-S,TR_Innenhof_FF,TR_C1.62-L,TR_B1.50-S,TR_B1.49-S,TR_B1.48-S,TR_B1.46-S,TR_B1.45-S,TR_B0.71-L,TR_B0.70-L,TR_B0.67-L,TR_A_Cafeteria,TR_A2.28-L,TR_A1.40-F,TR_A1.37-S,TR_A1.34-S,TR_A1.29-H,TR_A1.28-S,TR_A1.27-S,TR_A1.26-S,TR_A1.25-S,TR_A1.24-H,TR_A0.34-L,TR_A0.33-L,TR_A0.32.2-L,TR_A0.32.1-L,TR_A0.31.1-L,NI_FoyerK_F,NI_Foyer1_F,NI104-L,NI103-L,NI102-L,NI070-L,GU319-L,GU318-L,GU317b-L,GU317a-L,GU313-L,GU309-L,GU301-L,GU225-L,GU224-L,GU219-L,GU203-L,GU202-L,GU201-L,GU014-L,GU013-L,GU012-L,GU011-L,GU010-L,GU009-L,GU001-L,FÖ306-S,FÖ305-S,FÖ304-S", + output: true, + }, + want: "TR_L303,TR_L302,TR_L213,TR_L205,TR_L114,TR_L107,TR_L106,TR_L014,TR_Innenhof_FF,TR_C162,TR_B150,TR_B149,TR_B148,TR_B146,TR_B145,TR_B071,TR_B070,TR_B067,TR_A_Cafeteria,TR_A228,TR_A140,TR_A137,TR_A134,TR_A129,TR_A128,TR_A127,TR_A126,TR_A125,TR_A124,TR_A034,TR_A033,TR_A032-2,TR_A032-1,TR_A031-1,NI_FoyerK_F,NI_Foyer1_F,NI104,NI103,NI102,NI070,GU319,GU318,GU317b,GU317a,GU313,GU309,GU301,GU225,GU224,GU219,GU203,GU202,GU201,GU014,GU013,GU012,GU011,GU010,GU009,GU001,FÖ306,FÖ305,FÖ304", + }, + { + name: "Test Trefstbau MapRoom Input", + args: args{ + room: "TR_L321", + output: false, + }, + want: "TR_L3.21", + }, + { + name: "Test Trefstbau MapRoom Input", + args: args{ + room: "TR_A032-2", + output: false, + }, + want: "TR_A0.32.2", + }, + { + name: "Test Trefstbau MapRoom Input double point", + args: args{ + //TR_A1.23.1 -> TR_A1.23-1 + room: "TR_A1.23.1", + output: true, + }, + want: "TR_A123-1", + }, + { + name: "Test Trefstbau MapRoom Input double point with -S", + args: args{ + //TR_A1.23.1 -> TR_A1.23-1 + room: "TR_A1.23.1-S", + output: true, + }, + want: "TR_A123-1", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MapRoom(tt.args.room, tt.args.output); got != tt.want { + t.Errorf("MapRoom() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/services/ical/service/ical/icalDescriptionGenerator.go b/services/ical/service/ical/icalDescriptionGenerator.go index 3717168..c967a19 100644 --- a/services/ical/service/ical/icalDescriptionGenerator.go +++ b/services/ical/service/ical/icalDescriptionGenerator.go @@ -20,6 +20,7 @@ import ( ics "github.com/arran4/golang-ical" "htwkalender/ical/model" "htwkalender/ical/service/functions" + "log/slog" "net/url" "strings" _ "time/tzdata" @@ -31,10 +32,6 @@ func generateUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, description, altrep := generateDescription(event, userAgent) if isThunderbird(userAgent) && altrep != "" { - // Thunderbird-specific handling: Add DESCRIPTION with ALTREP attribute. - // create property altrep - //altrepParam := WithAltRep(altrep) - uri := &url.URL{ Scheme: "data", Opaque: altrep, @@ -103,20 +100,30 @@ func generateThunderbirdHTMLDescription(event model.Event) string { htmlDescription.WriteString("Typ: " + event.EventType + event.Compulsory + "
") } - // Add the HTML link to the room map. - htmlDescription.WriteString(`Link: HTWK-Karte`) + roomList := functions.SeperateRoomString(event.Rooms) + htmlDescription.WriteString("Orte:
") + for _, room := range roomList { + mapRoomName := functions.MapRoom(room, true) + _, bufferErr := htmlDescription.WriteString("" + room + "
") + + if bufferErr != nil { + slog.Error("Error while writing to buffer", "error", bufferErr) + return "" + } + } return htmlDescription.String() } // Generates a room description with links for Google Calendar. func generateGoogleCalendarDescription(rooms string) string { var description strings.Builder - roomList := strings.Split(rooms, " ") + roomList := functions.SeperateRoomString(rooms) description.WriteString("Orte: \n ") for _, room := range roomList { - description.WriteString("HTWK-Karte\n") + mapRoomName := functions.MapRoom(room, true) + description.WriteString(" " + mapRoomName + " \n") } return description.String() @@ -131,10 +138,3 @@ func isThunderbird(userAgent string) bool { func isGoogleCalendar(userAgent string) bool { return strings.Contains(userAgent, "Google-Calendar-Importer") } - -func WithAltRep(altRepUrl string) ics.PropertyParameter { - return &ics.KeyValues{ - Key: string(ics.ParameterAltrep), - Value: []string{altRepUrl}, - } -} From adcd5deda1f4e9237c9164b10104b868528f5386 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Thu, 17 Oct 2024 10:40:25 +0200 Subject: [PATCH 4/9] fix:#49 removed old tests for old lib --- .../service/ical/icalFileGeneration_test.go | 258 ------------------ 1 file changed, 258 deletions(-) delete mode 100644 services/ical/service/ical/icalFileGeneration_test.go diff --git a/services/ical/service/ical/icalFileGeneration_test.go b/services/ical/service/ical/icalFileGeneration_test.go deleted file mode 100644 index f85702f..0000000 --- a/services/ical/service/ical/icalFileGeneration_test.go +++ /dev/null @@ -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 . - -package ical - -import ( - "github.com/jordic/goics" - "htwkalender/ical/model" - mockTime "htwkalender/ical/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": {"5166fc0abd9d7750077261f1e26a26168d32c88af77198fe83af63e1ba6310dc@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": {"2463aac347bca19130d8e579b4b6d89a32c88f7c7e7f858e56477d94b71543a7@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": {"ea42fc31835128735636b235be552af559fae5329fe7e501f529130e11a7f3a1@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]) - } - } - } - }) - } -} From 20020e1e491b6dbaa8c4e3f6011849b6d70c40fa Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 19 Oct 2024 15:46:49 +0200 Subject: [PATCH 5/9] feat:#49 refactoring and tests --- .../service/ical/icalDescriptionGenerator.go | 109 +++--- .../ical/icalDescriptionGenerator_test.go | 324 ++++++++++++++++++ .../ical/service/ical/icalFileGeneration.go | 63 ++-- .../service/ical/icalFileGeneration_test.go | 78 +++++ 4 files changed, 488 insertions(+), 86 deletions(-) create mode 100644 services/ical/service/ical/icalDescriptionGenerator_test.go create mode 100644 services/ical/service/ical/icalFileGeneration_test.go diff --git a/services/ical/service/ical/icalDescriptionGenerator.go b/services/ical/service/ical/icalDescriptionGenerator.go index c967a19..4c2fc35 100644 --- a/services/ical/service/ical/icalDescriptionGenerator.go +++ b/services/ical/service/ical/icalDescriptionGenerator.go @@ -26,70 +26,61 @@ import ( _ "time/tzdata" ) -// Generates user-agent specific description and adds it to the calendar event. -func generateUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, userAgent string) { - // Generate description and ALTREP (alternative representation) based on user agent. +const ( + Thunderbird = "Thunderbird" + GoogleCalendar = "Google-Calendar-Importer" +) + +// Adds a user-agent specific description and ALTREP to the calendar event. +func addUserAgentSpecificDescription(vEvent *ics.VEvent, event model.Event, userAgent string) { description, altrep := generateDescription(event, userAgent) - if isThunderbird(userAgent) && altrep != "" { - uri := &url.URL{ - Scheme: "data", - Opaque: altrep, - } - - vEvent.AddProperty(ics.ComponentPropertyDescription, description, ics.WithAlternativeRepresentation(uri)) + if userAgentType := identifyUserAgent(userAgent); userAgentType == Thunderbird && altrep != "" { + vEvent.AddProperty(ics.ComponentPropertyDescription, description, ics.WithAlternativeRepresentation(buildDataURL(altrep))) } else { - // Default handling: Add plain DESCRIPTION property for other user agents. - vEvent.AddProperty("DESCRIPTION", description) + vEvent.AddProperty(ics.ComponentPropertyDescription, description) } - } -// Generates description based on the event details and user agent. +// Generates a description and ALTREP (alternative representation) based on the user agent. func generateDescription(event model.Event, userAgent string) (string, string) { plainDescription := buildPlainTextDescription(event) - if isThunderbird(userAgent) { - // Thunderbird-specific: Generate HTML and ALTREP format for Thunderbird users. - htmlDescription := generateThunderbirdHTMLDescription(event) - altrep := `text/html,` + url.PathEscape(htmlDescription) + switch identifyUserAgent(userAgent) { + case Thunderbird: + htmlDescription := generateHTMLDescriptionForThunderbird(event) + altrep := "text/html," + url.PathEscape(htmlDescription) return plainDescription, altrep + case GoogleCalendar: + plainDescription += generateRoomLinksForGoogleCalendar(event.Rooms) } - - // Google Calendar-specific handling. - if isGoogleCalendar(userAgent) { - plainDescription += generateGoogleCalendarDescription(event.Rooms) - } - - // Default: Return plain text description without ALTREP for non-Thunderbird cases. return plainDescription, "" } -// Builds the plain text description from the event details. +// Builds a plain text description of the event details. func buildPlainTextDescription(event model.Event) string { - var description string + var description strings.Builder if !functions.OnlyWhitespace(event.Prof) { - description += "Profs: " + event.Prof + "\n" + description.WriteString("Profs: " + event.Prof + "\n") } if !functions.OnlyWhitespace(event.Course) { - description += "Gruppen: " + event.Course + "\n" + description.WriteString("Gruppen: " + event.Course + "\n") } if !functions.OnlyWhitespace(event.EventType) { - description += "Typ: " + event.EventType + event.Compulsory + "\n" + description.WriteString("Typ: " + event.EventType + event.Compulsory + "\n") } if !functions.OnlyWhitespace(event.Notes) { - description += "Notizen: " + event.Notes + "\n" + description.WriteString("Notizen: " + event.Notes + "\n") } - return description + return description.String() } -// Helper function to generate HTML description for Thunderbird's ALTREP. -func generateThunderbirdHTMLDescription(event model.Event) string { +// Generates an HTML description for Thunderbird users. +func generateHTMLDescriptionForThunderbird(event model.Event) string { var htmlDescription strings.Builder - // HTML-encoded description similar to plain text. if !functions.OnlyWhitespace(event.Prof) { htmlDescription.WriteString("Profs: " + event.Prof + "
") } @@ -100,41 +91,47 @@ func generateThunderbirdHTMLDescription(event model.Event) string { htmlDescription.WriteString("Typ: " + event.EventType + event.Compulsory + "
") } - roomList := functions.SeperateRoomString(event.Rooms) - htmlDescription.WriteString("Orte:
") - - for _, room := range roomList { - mapRoomName := functions.MapRoom(room, true) - _, bufferErr := htmlDescription.WriteString("" + room + "
") - - if bufferErr != nil { - slog.Error("Error while writing to buffer", "error", bufferErr) - return "" + if !functions.OnlyWhitespace(event.Rooms) { + htmlDescription.WriteString("Orte: ") + for _, room := range functions.SeperateRoomString(event.Rooms) { + roomLink := functions.MapRoom(room, true) + _, err := htmlDescription.WriteString("" + room + " ") + if err != nil { + slog.Error("Error while writing to HTML description", "error", err) + return "" + } } } + return htmlDescription.String() } -// Generates a room description with links for Google Calendar. -func generateGoogleCalendarDescription(rooms string) string { +// Generates room links formatted for Google Calendar. +func generateRoomLinksForGoogleCalendar(rooms string) string { var description strings.Builder roomList := functions.SeperateRoomString(rooms) - description.WriteString("Orte: \n ") + description.WriteString("Orte: \n") for _, room := range roomList { - mapRoomName := functions.MapRoom(room, true) - description.WriteString(" " + mapRoomName + " \n") + roomLink := functions.MapRoom(room, true) + description.WriteString(" " + room + " \n") } return description.String() } -// Checks if the user agent is Thunderbird. -func isThunderbird(userAgent string) bool { - return strings.Contains(userAgent, "Thunderbird") +// Identifies the user agent type (e.g., Thunderbird or Google Calendar). +func identifyUserAgent(userAgent string) string { + if strings.Contains(userAgent, Thunderbird) { + return Thunderbird + } + if strings.Contains(userAgent, GoogleCalendar) { + return GoogleCalendar + } + return "" } -// Checks if the user agent is Google Calendar. -func isGoogleCalendar(userAgent string) bool { - return strings.Contains(userAgent, "Google-Calendar-Importer") +// Constructs a data URL for the ALTREP representation. +func buildDataURL(data string) *url.URL { + return &url.URL{Scheme: "data", Opaque: data} } diff --git a/services/ical/service/ical/icalDescriptionGenerator_test.go b/services/ical/service/ical/icalDescriptionGenerator_test.go new file mode 100644 index 0000000..d5d14b9 --- /dev/null +++ b/services/ical/service/ical/icalDescriptionGenerator_test.go @@ -0,0 +1,324 @@ +package ical + +import ( + "htwkalender/ical/model" + "net/url" + "reflect" + "testing" +) + +func Test_buildDataURL(t *testing.T) { + type args struct { + data string + } + tests := []struct { + name string + args args + want *url.URL + }{ + { + name: "Test 1", + args: args{ + data: "text/calendar;charset=utf-8;base64,ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg", + }, + want: &url.URL{ + Scheme: "data", + Opaque: "text/calendar;charset=utf-8;base64,ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg", + }, + }, + { + name: "Test 2", + args: args{ + data: "text/calendar;charset=utf-8;base64,ÄÜ'*A`+#Add\"$%&/()=?", + }, + want: &url.URL{ + Scheme: "data", + Opaque: "text/calendar;charset=utf-8;base64,ÄÜ'*A`+#Add\"$%&/()=?", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := buildDataURL(tt.args.data); !reflect.DeepEqual(got, tt.want) { + t.Errorf("buildDataURL() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_identifyUserAgent(t *testing.T) { + type args struct { + userAgent string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Test 1", + args: args{ + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Thunderbird/78.10.0", + }, + want: "Thunderbird", + }, + { + name: "Test 2", + args: args{ + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 GoogleCalendar/78.10.0", + }, + want: "", + }, + { + name: "Test 3", + args: args{ + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101", + }, + want: "", + }, + { + name: "Test 4", + args: args{ + userAgent: "Google-Calendar-Importer 1.0", + }, + want: "Google-Calendar-Importer", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := identifyUserAgent(tt.args.userAgent); got != tt.want { + t.Errorf("identifyUserAgent() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_buildPlainTextDescription(t *testing.T) { + type args struct { + event model.Event + } + tests := []struct { + name string + args args + want string + }{ + { + name: "All fields have valid values", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "Math 101", + EventType: "S", + Compulsory: "w", + Notes: "Bring textbook", + }, + }, + want: "Profs: Dr. Smith\nGruppen: Math 101\nTyp: Sw\nNotizen: Bring textbook\n", + }, + { + name: "All fields are empty", + args: args{ + event: model.Event{ + Prof: "", + Course: "", + EventType: "", + Compulsory: "", + Notes: "", + }, + }, + want: "", + }, + { + name: "Some fields are empty", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "", + EventType: "Seminar", + Compulsory: "", + Notes: "", + }, + }, + want: "Profs: Dr. Smith\nTyp: Seminar\n", + }, + { + name: "Fields contain only whitespace", + args: args{ + event: model.Event{ + Prof: " ", + Course: " ", + EventType: " ", + Compulsory: "", + Notes: " ", + }, + }, + want: "", + }, + { + name: "Mix of valid values, empty values, and whitespace", + args: args{ + event: model.Event{ + Prof: "Dr. Brown", + Course: " ", + EventType: "V", + Compulsory: "p", + Notes: "", + }, + }, + want: "Profs: Dr. Brown\nTyp: Vp\n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := buildPlainTextDescription(tt.args.event); got != tt.want { + t.Errorf("buildPlainTextDescription() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_generateRoomLinksForGoogleCalendar(t *testing.T) { + type args struct { + rooms string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Test 1", + args: args{ + rooms: "A123, ZU423, C789", + }, + want: "Orte: \n A123 \n ZU423 \n C789 \n", + }, + { + name: "Test 2", + args: args{ + rooms: "A123", + }, + want: "Orte: \n A123 \n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := generateRoomLinksForGoogleCalendar(tt.args.rooms); got != tt.want { + t.Errorf("generateRoomLinksForGoogleCalendar() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_generateHTMLDescriptionForThunderbird(t *testing.T) { + type args struct { + event model.Event + } + tests := []struct { + name string + args args + want string + }{ + { + name: "All fields have valid values including rooms", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "Math 101", + EventType: "Lecture", + Compulsory: " (mandatory)", + Rooms: "ZU423, FE231", + }, + }, + want: "Profs: Dr. Smith
Gruppen: Math 101
Typ: Lecture (mandatory)
Orte: ZU423 FE231 ", + }, + { + name: "All fields are empty", + args: args{ + event: model.Event{ + Prof: "", + Course: "", + EventType: "", + Compulsory: "", + Rooms: "", + }, + }, + want: "", + }, + { + name: "Some fields are empty, rooms have valid values", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "", + EventType: "Seminar", + Compulsory: "", + Rooms: "TR_L1.06-B", + }, + }, + want: "Profs: Dr. Smith
Typ: Seminar
Orte: TR_L1.06-B ", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := generateHTMLDescriptionForThunderbird(tt.args.event); got != tt.want { + t.Errorf("generateHTMLDescriptionForThunderbird() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_generateDescription(t *testing.T) { + type args struct { + event model.Event + userAgent string + } + tests := []struct { + name string + args args + description string + altrep string + }{ + { + name: "Test 1", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "Math 101", + EventType: "Lecture", + Compulsory: " (mandatory)", + Rooms: "ZU423, FE231", + }, + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Thunderbird/78.10.0", + }, + description: "Profs: Dr. Smith\nGruppen: Math 101\nTyp: Lecture (mandatory)\n", + altrep: "text/html,Profs:%20Dr.%20Smith%3Cbr%3EGruppen:%20Math%20101%3Cbr%3ETyp:%20Lecture%20%28mandatory%29%3Cbr%3EOrte:%20%3Ca%20href=%22https:%2F%2Fmap.htwk-leipzig.de%2Froom%2FZU423%22%3EZU423%3C%2Fa%3E%20%3Ca%20href=%22https:%2F%2Fmap.htwk-leipzig.de%2Froom%2FFE231%22%3EFE231%3C%2Fa%3E%20", + }, + { + name: "Test 2", + args: args{ + event: model.Event{ + Prof: "Dr. Smith", + Course: "Math 101", + EventType: "Lecture", + Compulsory: " (mandatory)", + Rooms: "ZU423, FE231", + }, + userAgent: "Google-Calendar-Importer", + }, + description: "Profs: Dr. Smith\nGruppen: Math 101\nTyp: Lecture (mandatory)\nOrte: \n ZU423 \n FE231 \n", + altrep: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := generateDescription(tt.args.event, tt.args.userAgent) + if got != tt.description { + t.Errorf("generateDescription() got = %v, want %v", got, tt.description) + } + if got1 != tt.altrep { + t.Errorf("generateDescription() got1 = %v, want %v", got1, tt.altrep) + } + }) + } +} diff --git a/services/ical/service/ical/icalFileGeneration.go b/services/ical/service/ical/icalFileGeneration.go index fed15df..9674c06 100644 --- a/services/ical/service/ical/icalFileGeneration.go +++ b/services/ical/service/ical/icalFileGeneration.go @@ -30,6 +30,39 @@ import ( func GenerateIcalFeed(events model.Events, mapping map[string]model.FeedCollection, userAgent string) *ics.Calendar { cal := ics.NewCalendarFor("HTWK Kalender") + setDefaultIcalParams(cal) + + europeTime, _ := time.LoadLocation("Europe/Berlin") + internalClock := clock.RealClock{} + + for _, event := range events { + mapEntry, mappingFound := mapping[event.UUID] + + var eventHash = functions.HashString(time.Time(event.Start).String() + time.Time(event.End).String() + event.Course + event.Name + event.Rooms) + + icalEvent := ics.NewEvent(eventHash + "@htwkalender.de") + + icalEvent.SetDtStampTime(internalClock.Now().Local().In(europeTime)) + icalEvent.SetStartAt(time.Time(event.Start).Local().In(europeTime)) + icalEvent.SetEndAt(time.Time(event.End).Local().In(europeTime)) + + if mappingFound { + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, replaceNameIfUserDefined(&event, mapEntry)) + addAlarmIfSpecified(icalEvent, event, mapEntry, internalClock) + } else { + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, event.Name) + } + + addUserAgentSpecificDescription(icalEvent, event, userAgent) + addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertyLocation, event.Rooms) + + cal.AddVEvent(icalEvent) + + } + return cal +} + +func setDefaultIcalParams(cal *ics.Calendar) { cal.SetMethod(ics.MethodPublish) cal.SetProductId("-//HTWK Kalender//htwkalender.de//DE") cal.SetTzid("Europe/Berlin") @@ -116,35 +149,6 @@ func GenerateIcalFeed(events model.Events, mapping map[string]model.FeedCollecti } cal.AddVTimezone(vTimeZone) - - europeTime, _ := time.LoadLocation("Europe/Berlin") - internalClock := clock.RealClock{} - - for _, event := range events { - mapEntry, mappingFound := mapping[event.UUID] - - var eventHash = functions.HashString(time.Time(event.Start).String() + time.Time(event.End).String() + event.Course + event.Name + event.Rooms) - - icalEvent := ics.NewEvent(eventHash + "@htwkalender.de") - - icalEvent.SetDtStampTime(internalClock.Now().Local().In(europeTime)) - icalEvent.SetStartAt(time.Time(event.Start).Local().In(europeTime)) - icalEvent.SetEndAt(time.Time(event.End).Local().In(europeTime)) - - if mappingFound { - addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, replaceNameIfUserDefined(&event, mapEntry)) - addAlarmIfSpecified(icalEvent, event, mapEntry, internalClock) - } else { - addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, event.Name) - } - - generateUserAgentSpecificDescription(icalEvent, event, userAgent) - addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertyLocation, event.Rooms) - - cal.AddVEvent(icalEvent) - - } - return cal } // AddPropertyIfNotEmpty adds a property to the component if the value is not empty @@ -173,6 +177,5 @@ func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) if !functions.OnlyWhitespace(mapping.UserDefinedName) { return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event) } - return event.Name } diff --git a/services/ical/service/ical/icalFileGeneration_test.go b/services/ical/service/ical/icalFileGeneration_test.go new file mode 100644 index 0000000..71dfe96 --- /dev/null +++ b/services/ical/service/ical/icalFileGeneration_test.go @@ -0,0 +1,78 @@ +package ical + +import ( + "htwkalender/ical/model" + "testing" +) + +func Test_replaceNameIfUserDefined(t *testing.T) { + type args struct { + event *model.Event + mapping model.FeedCollection + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Custom Name Test", + args: args{ + event: &model.Event{ + Name: "Test", + }, + mapping: model.FeedCollection{ + Name: "Test", + UserDefinedName: "CustomTest", + }, + }, + want: "CustomTest", + }, + { + name: "Empty Custom Name Test", + args: args{ + event: &model.Event{ + Name: "Test", + }, + mapping: model.FeedCollection{ + Name: "Test", + UserDefinedName: "", + }, + }, + want: "Test", + }, + { + name: "Empty Name Test", + args: args{ + event: &model.Event{ + Name: "", + }, + mapping: model.FeedCollection{ + Name: "", + UserDefinedName: "CustomTest", + }, + }, + want: "CustomTest", + }, + { + name: "Names dont match but check is done before", + args: args{ + event: &model.Event{ + Name: "Test", + }, + mapping: model.FeedCollection{ + Name: "Test2", + UserDefinedName: "CustomTest", + }, + }, + want: "CustomTest", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := replaceNameIfUserDefined(tt.args.event, tt.args.mapping); got != tt.want { + t.Errorf("replaceNameIfUserDefined() = %v, want %v", got, tt.want) + } + }) + } +} From da17b24ec52bb68af68f114a3732429fa68545d6 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 19 Oct 2024 13:56:31 +0000 Subject: [PATCH 6/9] Configure Dependency Scanning in `.gitlab-ci.yml`, creating this file if it does not already exist --- .gitlab-ci.yml | 323 +++++++++++++++++++++++-------------------------- 1 file changed, 151 insertions(+), 172 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 49ca5e0..6d5f90a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,282 +1,261 @@ -#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 . - +# You can override the included template(s) by including variable overrides +# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings +# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/pipeline/#customization +# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings +# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings +# Note that environment variables can be set in several places +# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: - - lint - - build - - test - - sonarqube-check - - oci-build - - deploy - - deploy-dev # New stage for development deployment - +- lint +- build +- test +- sonarqube-check +- oci-build +- deploy +- deploy-dev lint-frontend: image: node:lts stage: lint rules: - - changes: - - frontend/**/* + - changes: + - frontend/**/* script: - - cd frontend - - npm i - - npm run lint-no-fix - + - cd frontend + - npm i + - npm run lint-no-fix lint-data-manager: stage: lint image: golangci/golangci-lint:latest rules: - - changes: - - services/data-manager/**/* + - changes: + - services/data-manager/**/* script: - - cd services/data-manager - - go mod download - - golangci-lint --version - - golangci-lint run -v --skip-dirs=migrations --timeout=5m - + - cd services/data-manager + - go mod download + - golangci-lint --version + - golangci-lint run -v --skip-dirs=migrations --timeout=5m lint-ical: stage: lint image: golangci/golangci-lint:latest rules: - - changes: - - services/ical/**/* + - changes: + - services/ical/**/* script: - - cd services/ical - - go mod download - - golangci-lint --version - - golangci-lint run -v --skip-dirs=migrations --timeout=5m - - + - cd services/ical + - go mod download + - golangci-lint --version + - golangci-lint run -v --skip-dirs=migrations --timeout=5m build-data-manager: image: golang:alpine stage: build rules: - - changes: - - services/data-manager/**/* + - changes: + - services/data-manager/**/* script: - - cd services/data-manager - - go build -o htwkalender + - cd services/data-manager + - go build -o htwkalender artifacts: paths: - - data-manager/htwkalender - - data-manager/go.sum - - data-manager/go.mod - + - data-manager/htwkalender + - data-manager/go.sum + - data-manager/go.mod build-ical: image: golang:alpine stage: build rules: - - changes: - - services/ical/**/* + - changes: + - services/ical/**/* script: - - cd services/ical - - go build -o htwkalender-ical + - cd services/ical + - go build -o htwkalender-ical artifacts: paths: - - data-manager/htwkalender-ical - - data-manager/go.sum - - data-manager/go.mod - + - data-manager/htwkalender-ical + - data-manager/go.sum + - data-manager/go.mod sonarqube-data-manager: stage: sonarqube-check image: name: sonarsource/sonar-scanner-cli:5.0 - entrypoint: [""] + entrypoint: + - '' variables: - SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache - GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task + SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" + GIT_DEPTH: '0' cache: key: "${CI_JOB_NAME}" paths: - - .sonar/cache + - ".sonar/cache" script: - - cd services/data-manager - - sonar-scanner + - cd services/data-manager + - sonar-scanner allow_failure: true only: - - merge_requests - - master - - main - - develop - + - merge_requests + - master + - main + - develop build-frontend: - image: node:lts - stage: build - rules: - - changes: - - frontend/**/* - script: - - cd frontend - - npm i - - npm run build - artifacts: - paths: - - frontend/build - + image: node:lts + stage: build + rules: + - changes: + - frontend/**/* + script: + - cd frontend + - npm i + - npm run build + artifacts: + paths: + - frontend/build test-data-manager: image: golang:alpine stage: test rules: - - changes: - - services/data-manager/**/* + - changes: + - services/data-manager/**/* script: - - cd services/data-manager - - go test -v ./... + - cd services/data-manager + - go test -v ./... dependencies: - - build-data-manager - + - build-data-manager test-ical: image: golang:alpine stage: test rules: - - changes: - - services/ical/**/* + - changes: + - services/ical/**/* script: - - cd services/ical - - go test -v ./... + - cd services/ical + - go test -v ./... dependencies: - - build-ical - + - build-ical test-frontend: image: node:lts stage: test rules: - - changes: - - frontend/**/* + - changes: + - frontend/**/* script: - - cd frontend - - npm i - - npm run test + - cd frontend + - npm i + - npm run test dependencies: - - lint-frontend - + - lint-frontend build-data-manager-image: stage: oci-build image: docker:latest services: - - docker:dind + - docker:dind tags: - - image + - image variables: - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-data-manager + IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-data-manager" DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" DOCKER_TLS_VERIFY: 1 DOCKER_CERT_PATH: "/certs/client" before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - - docker build --pull -t $IMAGE_TAG -f ./services/data-manager/Dockerfile --target prod ./services - - docker push $IMAGE_TAG + - docker build --pull -t $IMAGE_TAG -f ./services/data-manager/Dockerfile --target + prod ./services + - docker push $IMAGE_TAG rules: - - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" - changes: - - services/data-manager/**/* - + - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" + changes: + - services/data-manager/**/* build-ical-image: stage: oci-build image: docker:latest services: - - docker:dind + - docker:dind tags: - - image + - image variables: - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-ical + IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-ical" DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" DOCKER_TLS_VERIFY: 1 DOCKER_CERT_PATH: "/certs/client" before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - - docker build --pull -t $IMAGE_TAG -f ./services/ical/Dockerfile --target prod ./services - - docker push $IMAGE_TAG + - docker build --pull -t $IMAGE_TAG -f ./services/ical/Dockerfile --target prod + ./services + - docker push $IMAGE_TAG rules: - - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" - changes: - - services/ical/**/* - + - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" + changes: + - services/ical/**/* build-frontend-image: stage: oci-build image: docker:latest services: - - docker:dind + - docker:dind tags: - - image + - image variables: - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-frontend + IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-frontend" DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" DOCKER_TLS_VERIFY: 1 DOCKER_CERT_PATH: "/certs/client" before_script: - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - - cd ./frontend + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - cd ./frontend script: - - docker build --pull -t $IMAGE_TAG -f ./Dockerfile --target prod . - - docker push $IMAGE_TAG + - docker build --pull -t $IMAGE_TAG -f ./Dockerfile --target prod . + - docker push $IMAGE_TAG rules: - - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" - changes: - - frontend/**/* - -# Development deployment job + - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development" + changes: + - frontend/**/* deploy-dev: - stage: deploy-dev # New stage for development deployment + stage: deploy-dev image: alpine:latest before_script: - - apk add --no-cache openssh-client sed # install dependencies - - eval $(ssh-agent -s) # set some ssh variables - - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') + - apk add --no-cache openssh-client sed + - eval $(ssh-agent -s) + - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') script: - # replace some placeholders - - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.dev.yml # Assuming you have a separate docker-compose file for development - # upload necessary files to the dev server - - > - scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.dev.yml ./reverseproxy.dev.conf - $CI_SSH_USER@$CI_SSH_DEV_HOST:/home/$CI_SSH_USER/docker/htwkalender/ - # ssh to the dev server and start the service - - > - ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_DEV_HOST - "cd /home/$CI_SSH_USER/docker/htwkalender/ && - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && - docker compose -f ./docker-compose.dev.yml down && docker compose -f ./docker-compose.dev.yml up -d --remove-orphans && docker logout" + - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.dev.yml + - 'scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.dev.yml + ./reverseproxy.dev.conf $CI_SSH_USER@$CI_SSH_DEV_HOST:/home/$CI_SSH_USER/docker/htwkalender/ + + ' + - 'ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_DEV_HOST + "cd /home/$CI_SSH_USER/docker/htwkalender/ && docker login -u $CI_REGISTRY_USER + -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker compose -f ./docker-compose.dev.yml + down && docker compose -f ./docker-compose.dev.yml up -d --remove-orphans && docker + logout" + + ' rules: - - if: $CI_COMMIT_BRANCH == "development" # Only execute for the development branch - - + - if: $CI_COMMIT_BRANCH == "development" deploy-all: stage: deploy image: alpine:latest before_script: - - apk add --no-cache openssh-client sed # install dependencies - - eval $(ssh-agent -s) # set some ssh variables - - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') + - apk add --no-cache openssh-client sed + - eval $(ssh-agent -s) + - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') script: - # replace some placeholders - - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.prod.yml - # upload necessary files to the server - - > - scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.prod.yml ./reverseproxy.conf - $CI_SSH_USER@$CI_SSH_HOST:/home/$CI_SSH_USER/docker/htwkalender/ - # ssh to the server and start the service - - > - ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_HOST - "cd /home/$CI_SSH_USER/docker/htwkalender/ && - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && - docker compose -f ./docker-compose.prod.yml down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && docker logout && - docker exec --user root htwkalender-htwkalender-frontend-1 /bin/sh -c \"echo 'google-site-verification: $GOOGLE_VERIFICATION.html' > ./$GOOGLE_VERIFICATION.html\" " + - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.prod.yml + - 'scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.prod.yml + ./reverseproxy.conf $CI_SSH_USER@$CI_SSH_HOST:/home/$CI_SSH_USER/docker/htwkalender/ + + ' + - 'ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_HOST + "cd /home/$CI_SSH_USER/docker/htwkalender/ && docker login -u $CI_REGISTRY_USER + -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker compose -f ./docker-compose.prod.yml + down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && + docker logout && docker exec --user root htwkalender-htwkalender-frontend-1 /bin/sh + -c \"echo ''google-site-verification: $GOOGLE_VERIFICATION.html'' > ./$GOOGLE_VERIFICATION.html\" + " + + ' rules: - - if: $CI_COMMIT_BRANCH == "main" + - if: $CI_COMMIT_BRANCH == "main" +include: +- template: Security/Dependency-Scanning.gitlab-ci.yml From 0cd867072460bf95bd1526b837194e201acf80fe Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 19 Oct 2024 16:02:32 +0200 Subject: [PATCH 7/9] feat:#00 edited ci file after auto Dependency security added --- .gitlab-ci.yml | 57 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d5f90a..c5edfb3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,18 @@ -# You can override the included template(s) by including variable overrides -# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings -# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/pipeline/#customization -# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings -# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings -# Note that environment variables can be set in several places -# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence +#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 . stages: - lint - build @@ -237,25 +245,24 @@ deploy-all: stage: deploy image: alpine:latest before_script: - - apk add --no-cache openssh-client sed - - eval $(ssh-agent -s) - - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') + - apk add --no-cache openssh-client sed # install dependencies + - eval $(ssh-agent -s) # set some ssh variables + - ssh-add <(echo "$CI_SSH_KEY" | tr -d '\r') script: - - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.prod.yml - - 'scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.prod.yml - ./reverseproxy.conf $CI_SSH_USER@$CI_SSH_HOST:/home/$CI_SSH_USER/docker/htwkalender/ - - ' - - 'ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_HOST - "cd /home/$CI_SSH_USER/docker/htwkalender/ && docker login -u $CI_REGISTRY_USER - -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && docker compose -f ./docker-compose.prod.yml - down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && - docker logout && docker exec --user root htwkalender-htwkalender-frontend-1 /bin/sh - -c \"echo ''google-site-verification: $GOOGLE_VERIFICATION.html'' > ./$GOOGLE_VERIFICATION.html\" - " - - ' + # replace some placeholders + - sed -i -e "s|DOCKER_REGISTRY_REPO|$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG|" docker-compose.prod.yml + # upload necessary files to the server + - > + scp -P $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR ./docker-compose.prod.yml ./reverseproxy.conf + $CI_SSH_USER@$CI_SSH_HOST:/home/$CI_SSH_USER/docker/htwkalender/ + # ssh to the server and start the service + - > + ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_HOST + "cd /home/$CI_SSH_USER/docker/htwkalender/ && + docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY && + docker compose -f ./docker-compose.prod.yml down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && docker logout && + docker exec --user root htwkalender-htwkalender-frontend-1 /bin/sh -c \"echo 'google-site-verification: $GOOGLE_VERIFICATION.html' > ./$GOOGLE_VERIFICATION.html\" " rules: - - if: $CI_COMMIT_BRANCH == "main" + - if: $CI_COMMIT_BRANCH == "main" include: - template: Security/Dependency-Scanning.gitlab-ci.yml From 84bc504734d650477345c16b0228c01000ab276e Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 19 Oct 2024 16:36:43 +0200 Subject: [PATCH 8/9] fix:#49 new line fix google calendar description --- services/ical/service/ical/icalDescriptionGenerator.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/services/ical/service/ical/icalDescriptionGenerator.go b/services/ical/service/ical/icalDescriptionGenerator.go index 4c2fc35..8b5fbfc 100644 --- a/services/ical/service/ical/icalDescriptionGenerator.go +++ b/services/ical/service/ical/icalDescriptionGenerator.go @@ -111,10 +111,12 @@ func generateRoomLinksForGoogleCalendar(rooms string) string { var description strings.Builder roomList := functions.SeperateRoomString(rooms) - description.WriteString("Orte: \n") - for _, room := range roomList { - roomLink := functions.MapRoom(room, true) - description.WriteString(" " + room + " \n") + if !functions.OnlyWhitespace(rooms) { + description.WriteString("Orte: ") + for _, room := range roomList { + roomLink := functions.MapRoom(room, true) + description.WriteString(" " + room + " ") + } } return description.String() From b9bd7f52c06828985f8c8650323913e016bf1241 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 19 Oct 2024 16:41:25 +0200 Subject: [PATCH 9/9] fix:#49 test rewrite --- services/ical/service/ical/icalDescriptionGenerator_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/ical/service/ical/icalDescriptionGenerator_test.go b/services/ical/service/ical/icalDescriptionGenerator_test.go index d5d14b9..1fd992f 100644 --- a/services/ical/service/ical/icalDescriptionGenerator_test.go +++ b/services/ical/service/ical/icalDescriptionGenerator_test.go @@ -191,14 +191,14 @@ func Test_generateRoomLinksForGoogleCalendar(t *testing.T) { args: args{ rooms: "A123, ZU423, C789", }, - want: "Orte: \n A123 \n ZU423 \n C789 \n", + want: "Orte: A123 ZU423 C789 ", }, { name: "Test 2", args: args{ rooms: "A123", }, - want: "Orte: \n A123 \n", + want: "Orte: A123 ", }, } for _, tt := range tests { @@ -306,7 +306,7 @@ func Test_generateDescription(t *testing.T) { }, userAgent: "Google-Calendar-Importer", }, - description: "Profs: Dr. Smith\nGruppen: Math 101\nTyp: Lecture (mandatory)\nOrte: \n ZU423 \n FE231 \n", + description: "Profs: Dr. Smith\nGruppen: Math 101\nTyp: Lecture (mandatory)\nOrte: ZU423 FE231 ", altrep: "", }, }