mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-16 17:48:49 +02:00
Merge branch 'development' into 'main'
Development See merge request htwk-software/htwkalender!85
This commit is contained in:
266
.gitlab-ci.yml
266
.gitlab-ci.yml
@ -13,250 +13,234 @@
|
||||
|
||||
#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/>.
|
||||
|
||||
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
|
||||
@ -280,3 +264,5 @@ deploy-all:
|
||||
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"
|
||||
include:
|
||||
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||
|
@ -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
|
||||
|
139
services/go.sum
139
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,6 +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.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=
|
||||
@ -106,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=
|
||||
@ -127,17 +70,8 @@ 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.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
@ -146,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=
|
||||
@ -182,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=
|
||||
@ -196,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=
|
||||
@ -226,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=
|
||||
@ -245,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=
|
||||
@ -257,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=
|
||||
@ -269,13 +176,9 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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=
|
||||
@ -293,29 +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/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/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/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=
|
||||
@ -335,8 +228,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
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=
|
||||
@ -345,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=
|
||||
@ -359,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=
|
||||
@ -371,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=
|
||||
@ -458,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=
|
||||
@ -467,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=
|
||||
@ -489,24 +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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
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/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.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=
|
||||
|
@ -72,7 +72,7 @@ func main() {
|
||||
fiberApp.Use(logger.New(
|
||||
logger.Config{
|
||||
Format: "${time} | ${status} | ${latency} | ${method} - ${path} | ${error}\n",
|
||||
TimeFormat: "02-Jan-2006 15:04:05",
|
||||
TimeFormat: "02-01-2006 15:04:05",
|
||||
},
|
||||
))
|
||||
|
||||
|
40
services/ical/service/functions/roomMapping.go
Normal file
40
services/ical/service/functions/roomMapping.go
Normal file
@ -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., "-<letter>")
|
||||
return match[:len(match)-2]
|
||||
})
|
||||
|
||||
return room
|
||||
}
|
73
services/ical/service/functions/roomMapping_test.go
Normal file
73
services/ical/service/functions/roomMapping_test.go
Normal file
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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"
|
||||
@ -32,7 +30,7 @@ const expirationTime = 5 * time.Minute
|
||||
|
||||
var FeedDeletedError = fmt.Errorf("feed deleted")
|
||||
|
||||
func Feed(app model.AppType, token string) (string, string, error) {
|
||||
func Feed(app model.AppType, token string, userAgent string) (string, string, error) {
|
||||
|
||||
var events model.Events
|
||||
modules := map[string]model.FeedCollection{}
|
||||
@ -72,9 +70,8 @@ func Feed(app model.AppType, token string) (string, string, error) {
|
||||
// Generate one Hash for E-TAG from all events and modules
|
||||
etag := functions.HashString(events.String() + fmt.Sprint(modules))
|
||||
|
||||
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, etag, nil
|
||||
}
|
||||
|
139
services/ical/service/ical/icalDescriptionGenerator.go
Normal file
139
services/ical/service/ical/icalDescriptionGenerator.go
Normal file
@ -0,0 +1,139 @@
|
||||
//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 (
|
||||
ics "github.com/arran4/golang-ical"
|
||||
"htwkalender/ical/model"
|
||||
"htwkalender/ical/service/functions"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"strings"
|
||||
_ "time/tzdata"
|
||||
)
|
||||
|
||||
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 userAgentType := identifyUserAgent(userAgent); userAgentType == Thunderbird && altrep != "" {
|
||||
vEvent.AddProperty(ics.ComponentPropertyDescription, description, ics.WithAlternativeRepresentation(buildDataURL(altrep)))
|
||||
} else {
|
||||
vEvent.AddProperty(ics.ComponentPropertyDescription, description)
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a description and ALTREP (alternative representation) based on the user agent.
|
||||
func generateDescription(event model.Event, userAgent string) (string, string) {
|
||||
plainDescription := buildPlainTextDescription(event)
|
||||
|
||||
switch identifyUserAgent(userAgent) {
|
||||
case Thunderbird:
|
||||
htmlDescription := generateHTMLDescriptionForThunderbird(event)
|
||||
altrep := "text/html," + url.PathEscape(htmlDescription)
|
||||
return plainDescription, altrep
|
||||
case GoogleCalendar:
|
||||
plainDescription += generateRoomLinksForGoogleCalendar(event.Rooms)
|
||||
}
|
||||
return plainDescription, ""
|
||||
}
|
||||
|
||||
// Builds a plain text description of the event details.
|
||||
func buildPlainTextDescription(event model.Event) string {
|
||||
var description strings.Builder
|
||||
|
||||
if !functions.OnlyWhitespace(event.Prof) {
|
||||
description.WriteString("Profs: " + event.Prof + "\n")
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Course) {
|
||||
description.WriteString("Gruppen: " + event.Course + "\n")
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.EventType) {
|
||||
description.WriteString("Typ: " + event.EventType + event.Compulsory + "\n")
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Notes) {
|
||||
description.WriteString("Notizen: " + event.Notes + "\n")
|
||||
}
|
||||
|
||||
return description.String()
|
||||
}
|
||||
|
||||
// Generates an HTML description for Thunderbird users.
|
||||
func generateHTMLDescriptionForThunderbird(event model.Event) string {
|
||||
var htmlDescription strings.Builder
|
||||
|
||||
if !functions.OnlyWhitespace(event.Prof) {
|
||||
htmlDescription.WriteString("Profs: " + event.Prof + "<br>")
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Course) {
|
||||
htmlDescription.WriteString("Gruppen: " + event.Course + "<br>")
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.EventType) {
|
||||
htmlDescription.WriteString("Typ: " + event.EventType + event.Compulsory + "<br>")
|
||||
}
|
||||
|
||||
if !functions.OnlyWhitespace(event.Rooms) {
|
||||
htmlDescription.WriteString("Orte: ")
|
||||
for _, room := range functions.SeperateRoomString(event.Rooms) {
|
||||
roomLink := functions.MapRoom(room, true)
|
||||
_, err := htmlDescription.WriteString("<a href=\"https://map.htwk-leipzig.de/room/" + roomLink + "\">" + room + "</a> ")
|
||||
if err != nil {
|
||||
slog.Error("Error while writing to HTML description", "error", err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return htmlDescription.String()
|
||||
}
|
||||
|
||||
// Generates room links formatted for Google Calendar.
|
||||
func generateRoomLinksForGoogleCalendar(rooms string) string {
|
||||
var description strings.Builder
|
||||
roomList := functions.SeperateRoomString(rooms)
|
||||
|
||||
if !functions.OnlyWhitespace(rooms) {
|
||||
description.WriteString("Orte: ")
|
||||
for _, room := range roomList {
|
||||
roomLink := functions.MapRoom(room, true)
|
||||
description.WriteString("<a href=\"https://map.htwk-leipzig.de/room/" + roomLink + "\"> " + room + " </a> ")
|
||||
}
|
||||
}
|
||||
|
||||
return description.String()
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
// Constructs a data URL for the ALTREP representation.
|
||||
func buildDataURL(data string) *url.URL {
|
||||
return &url.URL{Scheme: "data", Opaque: data}
|
||||
}
|
324
services/ical/service/ical/icalDescriptionGenerator_test.go
Normal file
324
services/ical/service/ical/icalDescriptionGenerator_test.go
Normal file
@ -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: <a href=\"https://map.htwk-leipzig.de/room/A123\"> A123 </a> <a href=\"https://map.htwk-leipzig.de/room/ZU423\"> ZU423 </a> <a href=\"https://map.htwk-leipzig.de/room/C789\"> C789 </a> ",
|
||||
},
|
||||
{
|
||||
name: "Test 2",
|
||||
args: args{
|
||||
rooms: "A123",
|
||||
},
|
||||
want: "Orte: <a href=\"https://map.htwk-leipzig.de/room/A123\"> A123 </a> ",
|
||||
},
|
||||
}
|
||||
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<br>Gruppen: Math 101<br>Typ: Lecture (mandatory)<br>Orte: <a href=\"https://map.htwk-leipzig.de/room/ZU423\">ZU423</a> <a href=\"https://map.htwk-leipzig.de/room/FE231\">FE231</a> ",
|
||||
},
|
||||
{
|
||||
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<br>Typ: Seminar<br>Orte: <a href=\"https://map.htwk-leipzig.de/room/TR_L106\">TR_L1.06-B</a> ",
|
||||
},
|
||||
}
|
||||
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: <a href=\"https://map.htwk-leipzig.de/room/ZU423\"> ZU423 </a> <a href=\"https://map.htwk-leipzig.de/room/FE231\"> FE231 </a> ",
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -17,121 +17,157 @@
|
||||
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")
|
||||
setDefaultIcalParams(cal)
|
||||
|
||||
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)
|
||||
addUserAgentSpecificDescription(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)
|
||||
func setDefaultIcalParams(cal *ics.Calendar) {
|
||||
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")
|
||||
|
||||
c.AddComponent(tz)
|
||||
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 (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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,33 +177,5 @@ func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection)
|
||||
if !functions.OnlyWhitespace(mapping.UserDefinedName) {
|
||||
return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event)
|
||||
}
|
||||
|
||||
return event.Name
|
||||
}
|
||||
|
||||
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
|
||||
// or contains only whitespaces
|
||||
func addPropertyIfNotEmpty(component *goics.Component, key string, value string) {
|
||||
if !functions.OnlyWhitespace(value) {
|
||||
component.AddProperty(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func generateDescription(event model.Event) string {
|
||||
var description string
|
||||
|
||||
if !functions.OnlyWhitespace(event.Prof) {
|
||||
description += "Profs: " + event.Prof + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Course) {
|
||||
description += "Gruppen: " + event.Course + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.EventType) {
|
||||
description += "Typ: " + event.EventType + event.Compulsory + "\n"
|
||||
}
|
||||
if !functions.OnlyWhitespace(event.Notes) {
|
||||
description += "Notizen: " + event.Notes + "\n"
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
@ -1,257 +1,77 @@
|
||||
//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/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
|
||||
func Test_replaceNameIfUserDefined(t *testing.T) {
|
||||
type args struct {
|
||||
event *model.Event
|
||||
mapping model.FeedCollection
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *goics.Component
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Test EmitICal",
|
||||
fields: fields{
|
||||
Events: model.Events{
|
||||
{
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
EventType: "Test",
|
||||
Notes: "Test",
|
||||
Prof: "Test",
|
||||
Rooms: "Test",
|
||||
BookedAt: "Test",
|
||||
},
|
||||
name: "Custom Name Test",
|
||||
args: args{
|
||||
event: &model.Event{
|
||||
Name: "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"},
|
||||
mapping: model.FeedCollection{
|
||||
Name: "Test",
|
||||
UserDefinedName: "CustomTest",
|
||||
},
|
||||
},
|
||||
want: "CustomTest",
|
||||
},
|
||||
{
|
||||
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)),
|
||||
},
|
||||
name: "Empty Custom Name Test",
|
||||
args: args{
|
||||
event: &model.Event{
|
||||
Name: "Test",
|
||||
},
|
||||
Mapping: map[string]model.FeedCollection{
|
||||
"123": {
|
||||
UUID: "123",
|
||||
Name: "Test",
|
||||
Course: "Test",
|
||||
UserDefinedName: "UserDefinedName",
|
||||
},
|
||||
mapping: model.FeedCollection{
|
||||
Name: "Test",
|
||||
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"},
|
||||
},
|
||||
},
|
||||
want: "Test",
|
||||
},
|
||||
{
|
||||
name: "Empty Name Test",
|
||||
args: args{
|
||||
event: &model.Event{
|
||||
Name: "",
|
||||
},
|
||||
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"},
|
||||
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) {
|
||||
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])
|
||||
}
|
||||
}
|
||||
if got := replaceNameIfUserDefined(tt.args.event, tt.args.mapping); got != tt.want {
|
||||
t.Errorf("replaceNameIfUserDefined() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -35,7 +35,11 @@ func AddFeedRoutes(app model.AppType) {
|
||||
|
||||
token := c.Query("token")
|
||||
ifNoneMatch := c.Get("If-None-Match")
|
||||
results, eTag, 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, eTag, err := ical.Feed(app, token, userAgent)
|
||||
|
||||
if errors.Is(err, ical.FeedDeletedError) {
|
||||
return c.SendStatus(fiber.StatusGone)
|
||||
|
Reference in New Issue
Block a user