mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-08-11 14:13:49 +02:00
feat:#7 added new folder structure and updated api in ical
This commit is contained in:
79
services/ical/service/connector/feedConnector.go
Normal file
79
services/ical/service/connector/feedConnector.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package connector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"htwkalender-ical/model"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
func GetFeedByToken(token string) (model.FeedRecord, error) {
|
||||
var feed model.FeedRecord
|
||||
|
||||
// /api/collections/feeds/records/{id}
|
||||
|
||||
response, err := RequestApi("/api/collections/feeds/records/" + token)
|
||||
if err != nil {
|
||||
return model.FeedRecord{}, err
|
||||
}
|
||||
|
||||
// parse the response body json to FeedRecord struct
|
||||
feed, err = parseResponse(response.Body())
|
||||
if err != nil {
|
||||
slog.Error("Failed to read response body", "error", err)
|
||||
return model.FeedRecord{}, err
|
||||
}
|
||||
|
||||
return feed, nil
|
||||
|
||||
}
|
||||
|
||||
func parseResponse(response []byte) (model.FeedRecord, error) {
|
||||
var feedRecord model.FeedRecord
|
||||
|
||||
err := json.Unmarshal(response, &feedRecord)
|
||||
if err != nil {
|
||||
return model.FeedRecord{}, err
|
||||
}
|
||||
|
||||
return feedRecord, nil
|
||||
}
|
||||
|
||||
func DeleteFeedRecord(token string) error {
|
||||
err := DeleteRequestApi("/api/feed?token=" + token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetModuleWithEvents(module model.FeedModule) (model.Module, error) {
|
||||
var modules model.Module
|
||||
|
||||
// /api/module?uuid=
|
||||
|
||||
response, err := RequestApi("/api/module?uuid=" + module.UUID)
|
||||
if err != nil {
|
||||
return model.Module{}, err
|
||||
}
|
||||
|
||||
// parse the response body json to Events struct
|
||||
modules, err = parseModuleResponse(response.Body())
|
||||
if err != nil {
|
||||
slog.Error("Failed to read response body", "error", err)
|
||||
return model.Module{}, err
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func parseModuleResponse(body []byte) (model.Module, error) {
|
||||
var module model.Module
|
||||
|
||||
err := json.Unmarshal(body, &module)
|
||||
if err != nil {
|
||||
return model.Module{}, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
38
services/ical/service/connector/restHandler.go
Normal file
38
services/ical/service/connector/restHandler.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package connector
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v3/client"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RequestApi(path string) (*client.Response, error) {
|
||||
|
||||
var host = "http://htwkalender-backend:8090"
|
||||
|
||||
cc := client.New()
|
||||
cc.SetTimeout(5 * time.Second)
|
||||
|
||||
// set retry to 0
|
||||
response, err := cc.Get(host + path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func DeleteRequestApi(path string) error {
|
||||
|
||||
var host = "http://htwkalender-backend:8090"
|
||||
|
||||
cc := client.New()
|
||||
cc.SetTimeout(5 * time.Second)
|
||||
|
||||
// set retry to 0
|
||||
_, err := cc.Delete(host + path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
47
services/ical/service/functions/semester.go
Normal file
47
services/ical/service/functions/semester.go
Normal file
@@ -0,0 +1,47 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package functions
|
||||
|
||||
import (
|
||||
localTime "htwkalender-ical/service/functions/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetCurrentSemesterString returns the current semester as string
|
||||
// if current month is between 10 and 03 -> winter semester "ws"
|
||||
func GetCurrentSemesterString(localeTime localTime.Clock) string {
|
||||
if localeTime.Now().Month() >= 10 || localeTime.Now().Month() <= 3 {
|
||||
return "ws"
|
||||
} else {
|
||||
return "ss"
|
||||
}
|
||||
}
|
||||
|
||||
func CalculateSemesterList(clock localTime.Clock) []string {
|
||||
summerSemester := clock.Now().Month() >= time.March && clock.Now().Month() <= time.September
|
||||
winterSemester := clock.Now().Month() <= time.March || clock.Now().Month() >= time.September
|
||||
|
||||
if summerSemester && !winterSemester {
|
||||
return []string{"ss"}
|
||||
}
|
||||
|
||||
if !summerSemester && winterSemester {
|
||||
return []string{"ws"}
|
||||
}
|
||||
|
||||
return []string{"ss", "ws"}
|
||||
}
|
91
services/ical/service/functions/semester_test.go
Normal file
91
services/ical/service/functions/semester_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package functions
|
||||
|
||||
import (
|
||||
mockTime "htwkalender/service/functions/time"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_calculateSemesterList(t *testing.T) {
|
||||
type args struct {
|
||||
clock mockTime.Clock
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "is summer semester",
|
||||
args: args{
|
||||
clock: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: []string{"ss"},
|
||||
},
|
||||
{
|
||||
name: "is winter semester",
|
||||
args: args{
|
||||
clock: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: []string{"ws"},
|
||||
},
|
||||
{
|
||||
name: "is in both",
|
||||
args: args{
|
||||
clock: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 3, 22, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: []string{"ss", "ws"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := CalculateSemesterList(tt.args.clock); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("calculateSemesterList() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCurrentSemesterString(t *testing.T) {
|
||||
type args struct {
|
||||
localeTime mockTime.Clock
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "is winter semester",
|
||||
args: args{
|
||||
localeTime: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: "ws",
|
||||
},
|
||||
{
|
||||
name: "is summer semester",
|
||||
args: args{
|
||||
localeTime: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: "ss",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetCurrentSemesterString(tt.args.localeTime); got != tt.want {
|
||||
t.Errorf("GetCurrentSemesterString() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
62
services/ical/service/functions/string.go
Normal file
62
services/ical/service/functions/string.go
Normal file
@@ -0,0 +1,62 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package functions
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// check if string is empty or contains only whitespaces
|
||||
func OnlyWhitespace(word string) bool {
|
||||
return len(strings.TrimSpace(word)) == 0
|
||||
}
|
||||
|
||||
// return function to check if rune is a separator
|
||||
func IsSeparator(separator []rune) func(rune) bool {
|
||||
return func(character rune) bool {
|
||||
for _, sep := range separator {
|
||||
if sep == character {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HashString(s string) string {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(s))
|
||||
hashInBytes := hash.Sum(nil)
|
||||
return hex.EncodeToString(hashInBytes)
|
||||
}
|
||||
|
||||
func SeperateRoomString(rooms string) []string {
|
||||
return strings.FieldsFunc(rooms, IsSeparator(
|
||||
[]rune{',', '\t', '\n', '\r', ';', ' ', '\u00A0'}),
|
||||
)
|
||||
}
|
145
services/ical/service/functions/string_test.go
Normal file
145
services/ical/service/functions/string_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package functions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOnlyWhitespace(t *testing.T) {
|
||||
type args struct {
|
||||
word string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{"empty string", args{""}, true},
|
||||
{"whitespace", args{" "}, true},
|
||||
{"whitespaces", args{" "}, true},
|
||||
{"whitespaces and tabs", args{" \t"}, true},
|
||||
{"whitespaces and tabs and newlines", args{" \t\n"}, true},
|
||||
{"whitespaces and tabs and newlines and non-breaking spaces", args{" \t\n\u00A0"}, true},
|
||||
{"non-whitespace", args{"a"}, false},
|
||||
{"non-whitespaces", args{"abc"}, false},
|
||||
{"non-whitespaces and tabs", args{"abc\t"}, false},
|
||||
{"non-whitespaces and tabs and newlines", args{"abc\t\n"}, false},
|
||||
{"non-whitespaces and tabs and newlines and non-breaking spaces", args{"abc\t\n\u00A0"}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := OnlyWhitespace(tt.args.word); got != tt.want {
|
||||
t.Errorf("OnlyWhitespace() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashString(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"empty string", args{""}, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
|
||||
{"non-empty string", args{"abc"}, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := HashString(tt.args.s); got != tt.want {
|
||||
t.Errorf("HashString() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSeparator(t *testing.T) {
|
||||
type args struct {
|
||||
separator []rune
|
||||
character rune
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{"empty separator", args{[]rune{}, 'a'}, false},
|
||||
{"separator with one rune equal", args{[]rune{'a'}, 'a'}, true},
|
||||
{"separator with one rune different", args{[]rune{'a'}, 'b'}, false},
|
||||
{"separator with two runes equal", args{[]rune{'a', 'b'}, 'a'}, true},
|
||||
{"separator with two runes equal second", args{[]rune{'a', 'b'}, 'b'}, true},
|
||||
{"separator with two runes different", args{[]rune{'a', 'b'}, 'c'}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsSeparator(tt.args.separator)(tt.args.character); got != tt.want {
|
||||
t.Errorf("IsSeparator()() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
type args struct {
|
||||
s []string
|
||||
e string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{"empty slice", args{[]string{}, "a"}, false},
|
||||
{"slice with one element equal", args{[]string{"a"}, "a"}, true},
|
||||
{"slice with one element different", args{[]string{"a"}, "b"}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := Contains(tt.args.s, tt.args.e); got != tt.want {
|
||||
t.Errorf("Contains() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeperateRoomString(t *testing.T) {
|
||||
type args struct {
|
||||
rooms string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{"empty string", args{""}, []string{}},
|
||||
{"one room", args{"a"}, []string{"a"}},
|
||||
{"two rooms", args{"a,b"}, []string{"a", "b"}},
|
||||
{"two rooms with whitespace", args{"a, b"}, []string{"a", "b"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := SeperateRoomString(tt.args.rooms); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("SeperateRoomString() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
26
services/ical/service/functions/time/mockClock.go
Normal file
26
services/ical/service/functions/time/mockClock.go
Normal file
@@ -0,0 +1,26 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type MockClock struct {
|
||||
NowTime time.Time
|
||||
}
|
||||
|
||||
func (m MockClock) Now() time.Time { return m.NowTime }
|
||||
func (MockClock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
36
services/ical/service/functions/time/parse.go
Normal file
36
services/ical/service/functions/time/parse.go
Normal file
@@ -0,0 +1,36 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package time
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ParseTime(timeString string) (time.Time, error) {
|
||||
return time.Parse("2006-01-02T15:04:05Z", timeString)
|
||||
}
|
||||
|
||||
func ParseAsTypesDatetime(time time.Time) types.DateTime {
|
||||
dateTime, err := types.ParseDateTime(time)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse time as types.DateTime", "error", err)
|
||||
return types.DateTime{}
|
||||
}
|
||||
return dateTime
|
||||
}
|
24
services/ical/service/functions/time/realClock.go
Normal file
24
services/ical/service/functions/time/realClock.go
Normal file
@@ -0,0 +1,24 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type RealClock struct{}
|
||||
|
||||
func (RealClock) Now() time.Time { return time.Now() }
|
||||
func (RealClock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
24
services/ical/service/functions/time/time.go
Normal file
24
services/ical/service/functions/time/time.go
Normal file
@@ -0,0 +1,24 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
After(d time.Duration) <-chan time.Time
|
||||
}
|
63
services/ical/service/ical/ical.go
Normal file
63
services/ical/service/ical/ical.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/jordic/goics"
|
||||
"htwkalender-ical/model"
|
||||
"htwkalender-ical/service/connector"
|
||||
"time"
|
||||
)
|
||||
|
||||
const expirationTime = 5 * time.Minute
|
||||
|
||||
func Feed(token string) (string, error) {
|
||||
// get feed by token
|
||||
feed, err := connector.GetFeedByToken(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var events model.Events
|
||||
|
||||
for _, module := range feed.Modules {
|
||||
moduleWithEvents, err := connector.GetModuleWithEvents(module)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
events = append(events, moduleWithEvents.Events...)
|
||||
}
|
||||
|
||||
// Sorte events by start date
|
||||
events.Sort()
|
||||
|
||||
modules := make(map[string]model.FeedCollection)
|
||||
for _, module := range feed.Modules {
|
||||
modules[module.UUID] = model.FeedCollection(module)
|
||||
}
|
||||
|
||||
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))}
|
||||
|
||||
return icalFeed.Content, nil
|
||||
}
|
||||
|
||||
func FeedRecord(token string) (model.FeedRecord, error) {
|
||||
|
||||
feedRecord, err := connector.GetFeedByToken(token)
|
||||
if err != nil {
|
||||
return model.FeedRecord{}, err
|
||||
}
|
||||
|
||||
return feedRecord, nil
|
||||
|
||||
}
|
||||
|
||||
func DeleteFeedRecord(token string) error {
|
||||
err := connector.DeleteFeedRecord(token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
173
services/ical/service/ical/icalFileGeneration.go
Normal file
173
services/ical/service/ical/icalFileGeneration.go
Normal file
@@ -0,0 +1,173 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package ical
|
||||
|
||||
import (
|
||||
"htwkalender-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
|
||||
}
|
||||
|
||||
// EmitICal implements the interface for goics
|
||||
func (icalModel IcalModel) EmitICal() goics.Componenter {
|
||||
internalClock := clock.RealClock{}
|
||||
c := generateIcalEmit(icalModel, internalClock)
|
||||
return c
|
||||
}
|
||||
|
||||
func generateIcalEmit(icalModel IcalModel, internalClock clock.Clock) *goics.Component {
|
||||
europeTime, _ := time.LoadLocation("Europe/Berlin")
|
||||
c := goics.NewComponent()
|
||||
c.SetType("VCALENDAR")
|
||||
// PRODID is required by the standard
|
||||
c.AddProperty("PRODID", "-//HTWK Kalender//htwkalender.de//DE")
|
||||
|
||||
c.AddProperty("VERSION", "2.0")
|
||||
c.AddProperty("CALSCALE", "GREGORIAN")
|
||||
c.AddProperty("TZID", "EUROPE/BERLIN")
|
||||
c.AddProperty("X-WR-CALNAME", "HTWK Kalender")
|
||||
c.AddProperty("X-WR-TIMEZONE", "EUROPE/BERLIN")
|
||||
//add v time zone
|
||||
icalModel.vtimezone(c)
|
||||
|
||||
for _, event := range icalModel.Events {
|
||||
mapEntry, mappingFound := icalModel.Mapping[event.UUID]
|
||||
|
||||
s := goics.NewComponent()
|
||||
s.SetType("VEVENT")
|
||||
|
||||
s.AddProperty(goics.FormatDateTime("DTSTAMP", internalClock.Now().Local().In(europeTime)))
|
||||
|
||||
// create a unique id for the event by hashing the event start, end, course and name
|
||||
var eventHash = functions.HashString(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)))
|
||||
|
||||
if mappingFound {
|
||||
addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry))
|
||||
addAlarmIfSpecified(s, event, mapEntry, internalClock)
|
||||
} else {
|
||||
addPropertyIfNotEmpty(s, "SUMMARY", event.Name)
|
||||
}
|
||||
|
||||
addPropertyIfNotEmpty(s, "DESCRIPTION", generateDescription(event))
|
||||
addPropertyIfNotEmpty(s, "LOCATION", event.Rooms)
|
||||
c.AddComponent(s)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (icalModel IcalModel) vtimezone(c *goics.Component) {
|
||||
tz := goics.NewComponent()
|
||||
tz.SetType("VTIMEZONE")
|
||||
tz.AddProperty("TZID", "EUROPE/BERLIN")
|
||||
//add standard time
|
||||
icalModel.standard(tz)
|
||||
//add daylight time
|
||||
icalModel.daylight(tz)
|
||||
|
||||
c.AddComponent(tz)
|
||||
}
|
||||
|
||||
func (icalModel IcalModel) standard(tz *goics.Component) {
|
||||
st := NewHtwkalenderComponent()
|
||||
st.SetType("STANDARD")
|
||||
st.AddProperty("DTSTART", "19701025T030000")
|
||||
st.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU")
|
||||
st.AddProperty("TZOFFSETFROM", "+0200")
|
||||
st.AddProperty("TZOFFSETTO", "+0100")
|
||||
st.AddProperty("TZNAME", "CET")
|
||||
tz.AddComponent(st)
|
||||
}
|
||||
|
||||
// create an override for goics component function Write
|
||||
// to add the RRULE property
|
||||
|
||||
func (icalModel IcalModel) daylight(tz *goics.Component) {
|
||||
dt := NewHtwkalenderComponent()
|
||||
dt.SetType("DAYLIGHT")
|
||||
dt.AddProperty("DTSTART", "19700329T020000")
|
||||
dt.AddProperty("TZOFFSETFROM", "+0100")
|
||||
dt.AddProperty("TZOFFSETTO", "+0200")
|
||||
dt.AddProperty("TZNAME", "CEST")
|
||||
dt.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU")
|
||||
tz.AddComponent(dt)
|
||||
}
|
||||
|
||||
// if reminder is specified in the configuration for this event, an alarm will be added to the event
|
||||
func addAlarmIfSpecified(s *goics.Component, event model.Event, mapping model.FeedCollection, clock clock.Clock) {
|
||||
// if event.Start > now
|
||||
// then add alarm
|
||||
if 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)
|
||||
}
|
||||
}
|
||||
|
||||
// replaceNameIfUserDefined replaces the name of the event with the user defined name if it is not empty
|
||||
// all contained template strings will be replaced with the corresponding values from the event
|
||||
func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) string {
|
||||
if !functions.OnlyWhitespace(mapping.UserDefinedName) {
|
||||
return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event)
|
||||
}
|
||||
|
||||
return event.Name
|
||||
}
|
||||
|
||||
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
|
||||
// or contains only whitespaces
|
||||
func addPropertyIfNotEmpty(component *goics.Component, key string, value string) {
|
||||
if !functions.OnlyWhitespace(value) {
|
||||
component.AddProperty(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func generateDescription(event model.Event) string {
|
||||
var description string
|
||||
|
||||
if !functions.OnlyWhitespace(event.Prof) {
|
||||
description += "Profs: " + event.Prof + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Course) {
|
||||
description += "Gruppen: " + event.Course + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.EventType) {
|
||||
description += "Typ: " + event.EventType + event.Compulsory + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Notes) {
|
||||
description += "Notizen: " + event.Notes + "\n"
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
258
services/ical/service/ical/icalFileGeneration_test.go
Normal file
258
services/ical/service/ical/icalFileGeneration_test.go
Normal file
@@ -0,0 +1,258 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package ical
|
||||
|
||||
import (
|
||||
"github.com/jordic/goics"
|
||||
"htwkalender/model"
|
||||
mockTime "htwkalender/service/functions/time"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIcalModel_EmitICal(t *testing.T) {
|
||||
type fields struct {
|
||||
Events model.Events
|
||||
Mapping map[string]model.FeedCollection
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *goics.Component
|
||||
}{
|
||||
{
|
||||
name: "Test EmitICal",
|
||||
fields: fields{
|
||||
Events: model.Events{
|
||||
{
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
EventType: "Test",
|
||||
Notes: "Test",
|
||||
Prof: "Test",
|
||||
Rooms: "Test",
|
||||
BookedAt: "Test",
|
||||
},
|
||||
},
|
||||
Mapping: map[string]model.FeedCollection{
|
||||
"123": {
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
Course: "Test",
|
||||
UserDefinedName: "Test",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &goics.Component{
|
||||
Tipo: "VCALENDAR",
|
||||
Elements: []goics.Componenter{
|
||||
&goics.Component{
|
||||
Tipo: "VTIMEZONE",
|
||||
Elements: []goics.Componenter{
|
||||
&HtwkalenderComponent{
|
||||
Component: &goics.Component{
|
||||
Tipo: "STANDARD",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTART": {"19701025T030000"},
|
||||
"RRULE": {"FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU"},
|
||||
"TZOFFSETFROM": {"+0200"},
|
||||
"TZOFFSETTO": {"+0100"},
|
||||
"TZNAME": {"CET"},
|
||||
},
|
||||
},
|
||||
},
|
||||
&HtwkalenderComponent{
|
||||
Component: &goics.Component{
|
||||
Tipo: "DAYLIGHT",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTART": {"19700329T020000"},
|
||||
"TZOFFSETFROM": {"+0100"},
|
||||
"TZOFFSETTO": {"+0200"},
|
||||
"TZNAME": {"CEST"},
|
||||
"RRULE": {"FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Properties: map[string][]string{
|
||||
"TZID": {"EUROPE/BERLIN"},
|
||||
},
|
||||
},
|
||||
&goics.Component{
|
||||
Tipo: "VEVENT",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTAMP": {"20231201T000000Z"},
|
||||
"UID": {"a8d627d93f518e9096b6f40e36d27b7660fa26d318ef1adc43da750e49ebe4be@htwkalender.de"},
|
||||
"DTEND": {"00010101T000000Z"},
|
||||
"DTSTART": {"00010101T000000Z"},
|
||||
"SUMMARY": {"Test"},
|
||||
"DESCRIPTION": {"Profs: Test\nTyp: Test\nNotizen: Test\n"},
|
||||
"LOCATION": {"Test"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Properties: map[string][]string{
|
||||
"PRODID": {"-//HTWK Kalender//htwkalender.de//DE"},
|
||||
"VERSION": {"2.0"},
|
||||
"CALSCALE": {"GREGORIAN"},
|
||||
"TZID": {"EUROPE/BERLIN"},
|
||||
"X-WR-CALNAME": {"HTWK Kalender"},
|
||||
"X-WR-TIMEZONE": {"EUROPE/BERLIN"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test Similar Events like Sport Courses",
|
||||
fields: fields{
|
||||
Events: model.Events{
|
||||
{
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
Course: "Test",
|
||||
EventType: "Test",
|
||||
Notes: "Test",
|
||||
Prof: "Test",
|
||||
Rooms: "ZU430",
|
||||
BookedAt: "Test",
|
||||
Start: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
|
||||
End: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 1, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
{
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
Course: "Test",
|
||||
EventType: "Test",
|
||||
Notes: "Test",
|
||||
Prof: "Test",
|
||||
Rooms: "ZU221",
|
||||
BookedAt: "Test",
|
||||
Start: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC)),
|
||||
End: mockTime.ParseAsTypesDatetime(time.Date(2023, 12, 1, 1, 0, 0, 0, time.UTC)),
|
||||
},
|
||||
},
|
||||
Mapping: map[string]model.FeedCollection{
|
||||
"123": {
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
Course: "Test",
|
||||
UserDefinedName: "UserDefinedName",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &goics.Component{
|
||||
Tipo: "VCALENDAR",
|
||||
Elements: []goics.Componenter{
|
||||
&goics.Component{
|
||||
Tipo: "VTIMEZONE",
|
||||
Elements: []goics.Componenter{
|
||||
&HtwkalenderComponent{
|
||||
Component: &goics.Component{
|
||||
Tipo: "STANDARD",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTART": {"19701025T030000"},
|
||||
"RRULE": {"FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU"},
|
||||
"TZOFFSETFROM": {"+0200"},
|
||||
"TZOFFSETTO": {"+0100"},
|
||||
"TZNAME": {"CET"},
|
||||
},
|
||||
},
|
||||
},
|
||||
&HtwkalenderComponent{
|
||||
Component: &goics.Component{
|
||||
Tipo: "DAYLIGHT",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTART": {"19700329T020000"},
|
||||
"TZOFFSETFROM": {"+0100"},
|
||||
"TZOFFSETTO": {"+0200"},
|
||||
"TZNAME": {"CEST"},
|
||||
"RRULE": {"FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Properties: map[string][]string{
|
||||
"TZID": {"EUROPE/BERLIN"},
|
||||
},
|
||||
},
|
||||
&goics.Component{
|
||||
Tipo: "VEVENT",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTAMP": {"20231201T000000Z"},
|
||||
"UID": {"b52a7a081f46eeba9b402114493278a34a48b572c84e53d7ac4da9dea15cdff2@htwkalender.de"},
|
||||
"DTEND": {"20231201T010000Z"},
|
||||
"DTSTART": {"20231201T000000Z"},
|
||||
"SUMMARY": {"UserDefinedName"},
|
||||
"DESCRIPTION": {"Profs: Test\nGruppen: Test\nTyp: Test\nNotizen: Test\n"},
|
||||
"LOCATION": {"ZU430"},
|
||||
},
|
||||
},
|
||||
&goics.Component{
|
||||
Tipo: "VEVENT",
|
||||
Elements: []goics.Componenter{},
|
||||
Properties: map[string][]string{
|
||||
"DTSTAMP": {"20231201T000000Z"},
|
||||
"UID": {"5e946c0c4474bc6e6337262093e3ef31477e026bbc6bab398d755b002506d9d7@htwkalender.de"},
|
||||
"DTEND": {"20231201T010000Z"},
|
||||
"DTSTART": {"20231201T000000Z"},
|
||||
"SUMMARY": {"UserDefinedName"},
|
||||
"DESCRIPTION": {"Profs: Test\nGruppen: Test\nTyp: Test\nNotizen: Test\n"},
|
||||
"LOCATION": {"ZU221"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Properties: map[string][]string{
|
||||
"PRODID": {"-//HTWK Kalender//htwkalender.de//DE"},
|
||||
"VERSION": {"2.0"},
|
||||
"CALSCALE": {"GREGORIAN"},
|
||||
"TZID": {"EUROPE/BERLIN"},
|
||||
"X-WR-CALNAME": {"HTWK Kalender"},
|
||||
"X-WR-TIMEZONE": {"EUROPE/BERLIN"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
icalModel := IcalModel{
|
||||
Events: tt.fields.Events,
|
||||
Mapping: tt.fields.Mapping,
|
||||
}
|
||||
|
||||
mockClock := mockTime.MockClock{
|
||||
NowTime: time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||
}
|
||||
|
||||
if got := generateIcalEmit(icalModel, mockClock); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("EmitICal() = \n%v, want \n%v", got, tt.want)
|
||||
|
||||
// Print the differences
|
||||
for i, element := range got.Elements {
|
||||
if !reflect.DeepEqual(element, tt.want.Elements[i]) {
|
||||
t.Errorf("Element %d: got \n%v, want \n%v", i, element, tt.want.Elements[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
62
services/ical/service/ical/icsComponenter.go
Normal file
62
services/ical/service/ical/icsComponenter.go
Normal file
@@ -0,0 +1,62 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package ical
|
||||
|
||||
import (
|
||||
"github.com/jordic/goics"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HtwkalenderComponent struct {
|
||||
*goics.Component
|
||||
}
|
||||
|
||||
func NewHtwkalenderComponent() *HtwkalenderComponent {
|
||||
return &HtwkalenderComponent{
|
||||
Component: goics.NewComponent(),
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the component to the Writer
|
||||
func (c *HtwkalenderComponent) Write(w *goics.ICalEncode) {
|
||||
w.WriteLine("BEGIN:" + c.Tipo + goics.CRLF)
|
||||
|
||||
// Iterate over component properties
|
||||
var keys []string
|
||||
for k := range c.Properties {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
vals := c.Properties[key]
|
||||
for _, val := range vals {
|
||||
w.WriteLine(WriteStringField(key, val))
|
||||
}
|
||||
}
|
||||
|
||||
for _, xc := range c.Elements {
|
||||
xc.Write(w)
|
||||
}
|
||||
|
||||
w.WriteLine("END:" + c.Tipo + goics.CRLF)
|
||||
}
|
||||
|
||||
// WriteStringField UID:asdfasdfаs@dfasdf.com
|
||||
func WriteStringField(key string, val string) string {
|
||||
return strings.ToUpper(key) + ":" + (val) + goics.CRLF
|
||||
}
|
39
services/ical/service/names/userDefinedNameTemplates.go
Normal file
39
services/ical/service/names/userDefinedNameTemplates.go
Normal file
@@ -0,0 +1,39 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package names
|
||||
|
||||
import (
|
||||
"htwkalender-ical/model"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func ReplaceTemplateSubStrings(rawString string, event model.Event) string {
|
||||
re := regexp.MustCompile(`\%(.)`)
|
||||
|
||||
return re.ReplaceAllStringFunc(rawString, func(match string) string {
|
||||
switch match {
|
||||
case "%%":
|
||||
return "%"
|
||||
case "%t":
|
||||
return event.EventType
|
||||
case "%p":
|
||||
return event.Compulsory
|
||||
default:
|
||||
return match
|
||||
}
|
||||
})
|
||||
}
|
94
services/ical/service/names/userDefinedNameTemplates_test.go
Normal file
94
services/ical/service/names/userDefinedNameTemplates_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero General Public License as published by
|
||||
//the Free Software Foundation, either version 3 of the License, or
|
||||
//(at your option) any later version.
|
||||
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package names
|
||||
|
||||
import (
|
||||
"htwkalender/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReplaceTemplateSubStrings(t *testing.T) {
|
||||
type args struct {
|
||||
rawString string
|
||||
event model.Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Test 1",
|
||||
args: args{
|
||||
rawString: "%t",
|
||||
event: model.Event{
|
||||
EventType: "Test",
|
||||
},
|
||||
},
|
||||
want: "Test",
|
||||
},
|
||||
{
|
||||
name: "Test 2",
|
||||
args: args{
|
||||
rawString: "%p",
|
||||
event: model.Event{
|
||||
Compulsory: "Test",
|
||||
},
|
||||
},
|
||||
want: "Test",
|
||||
},
|
||||
{
|
||||
name: "Test 3",
|
||||
args: args{
|
||||
rawString: "%%",
|
||||
event: model.Event{
|
||||
EventType: "Test",
|
||||
},
|
||||
},
|
||||
want: "%",
|
||||
},
|
||||
{
|
||||
name: "Test 4",
|
||||
args: args{
|
||||
rawString: "%t %p",
|
||||
event: model.Event{
|
||||
EventType: "Test",
|
||||
Compulsory: "Test",
|
||||
},
|
||||
},
|
||||
want: "Test Test",
|
||||
},
|
||||
{
|
||||
name: "Test 5",
|
||||
args: args{
|
||||
rawString: "%t %p %%",
|
||||
event: model.Event{
|
||||
EventType: "Test",
|
||||
Compulsory: "Test",
|
||||
},
|
||||
},
|
||||
want: "Test Test %",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ReplaceTemplateSubStrings(tt.args.rawString, tt.args.event); got != tt.want {
|
||||
t.Errorf("ReplaceTemplateSubStrings() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
84
services/ical/service/routes.go
Normal file
84
services/ical/service/routes.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"htwkalender-ical/service/ical"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// add routes to the app instance for the backend ical service
|
||||
// with golang fiber
|
||||
func AddFeedRoutes(app *fiber.App) {
|
||||
|
||||
// Define a route for the GET method on the root path '/'
|
||||
app.Get("/api/feed", func(c fiber.Ctx) error {
|
||||
|
||||
token := c.Query("token")
|
||||
results, err := ical.Feed(token)
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Failed to get feed", "error", err)
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
c.Response().Header.Set("Content-type", "text/calendar")
|
||||
c.Response().Header.Set("charset", "utf-8")
|
||||
c.Response().Header.Set("Content-Disposition", "inline")
|
||||
c.Response().Header.Set("filename", "calendar.ics")
|
||||
|
||||
return c.SendString(results)
|
||||
})
|
||||
|
||||
// Define a route for the GET method on the root path '/'
|
||||
app.Get("/api/collections/feeds/records/:token", func(c fiber.Ctx) error {
|
||||
|
||||
token := c.Params("token")
|
||||
results, err := ical.FeedRecord(token)
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Failed to get feed", "error", err)
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
c.Response().Header.Set("Content-type", "application/json; charset=UTF-8")
|
||||
|
||||
return c.JSON(results)
|
||||
})
|
||||
|
||||
app.Delete("/api/feed", func(c fiber.Ctx) error {
|
||||
|
||||
token := c.Query("token")
|
||||
err := ical.DeleteFeedRecord(token)
|
||||
if err != nil {
|
||||
slog.Error("Feed could not be deleted", "error", err)
|
||||
return c.JSON(http.StatusNotFound, "Feed could not be deleted")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, "Feed deleted")
|
||||
}
|
||||
})
|
||||
|
||||
app.Put("/api/feed", func(c fiber.Ctx) error {
|
||||
token := c.Query("token")
|
||||
return c.JSON(http.StatusOK, "token: "+token)
|
||||
})
|
||||
|
||||
app.Head("/api/feed", func(c fiber.Ctx) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
})
|
||||
|
||||
app.Add([]string{"PROPFIND"}, "/api/feed", func(c fiber.Ctx) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
})
|
||||
|
||||
// Route for Thunderbird to get calendar server information
|
||||
// Response is a 200 OK without additional content
|
||||
app.Add([]string{"PROPFIND"}, "/.well-known/caldav", func(c fiber.Ctx) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
})
|
||||
|
||||
// Route for Thunderbird to get calendar server information
|
||||
// Response is a 200 OK without additional content
|
||||
app.Add([]string{"PROPFIND"}, "/", func(c fiber.Ctx) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
})
|
||||
|
||||
}
|
Reference in New Issue
Block a user