Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08fc8eeb4 | ||
|
|
06f9de1c64 | ||
|
|
e4b1d6e01b | ||
|
|
c05f51b923 | ||
|
|
48a173a563 | ||
|
|
9a6588fcd7 | ||
|
|
9b1455723b | ||
|
|
2d594d2079 | ||
|
|
410c54134a | ||
|
|
ac35b2ee83 | ||
|
|
55831c9bf1 | ||
|
|
9d77b4156d | ||
|
|
6229e1caf6 | ||
|
|
58e4462ca5 | ||
|
|
51075f71e6 | ||
|
|
d66f5e77d4 | ||
|
|
57bd5aca80 | ||
|
|
74e40ccfb4 | ||
|
|
6ef1bc3944 | ||
|
|
cca42b9188 | ||
|
|
fd0b2d103d | ||
|
|
0ce110fa52 | ||
|
|
8a3f16fcfb | ||
|
|
d35c30dd0c | ||
|
|
66b44da85b | ||
|
|
4615b4dbfb | ||
|
|
c6c465c273 | ||
|
|
f26f1326ea | ||
|
|
bbc8da1e80 | ||
|
|
df1f95871a | ||
|
|
5da1075f88 | ||
|
|
1ed65d306d | ||
|
|
b9a54b019d | ||
|
|
61bce2b349 | ||
|
|
ab2eda4bd5 | ||
|
|
225d65268d | ||
|
|
6169c2e12e | ||
|
|
91146f139b | ||
|
|
46683a2516 | ||
|
|
707bb6f89e | ||
|
|
8acc42cb0b | ||
|
|
b1b1efde88 | ||
|
|
c4d18b13a3 | ||
|
|
7848a93293 | ||
|
|
2105128b9c | ||
|
|
c27ecf18da | ||
|
|
eb96c6f2df | ||
|
|
de20c5a972 | ||
|
|
b2866ca3da | ||
|
|
4d3ba495a7 | ||
|
|
a2925cf55b | ||
|
|
27984157c1 | ||
|
|
66afe52afb | ||
|
|
f5e5b22641 | ||
|
|
ae77ee068b | ||
|
|
f1a2028f59 | ||
|
|
70cec74239 | ||
|
|
db0153a721 | ||
|
|
6a7aec2e9d | ||
|
|
1c416ae73e | ||
|
|
a375223872 | ||
|
|
a1ea77f751 | ||
|
|
4625377752 | ||
|
|
aad6bc08f6 | ||
|
|
916e75da09 | ||
|
|
8a4856ad87 | ||
|
|
10c9913e12 | ||
|
|
66cf7ab50a | ||
|
|
b2b4b5423f | ||
|
|
1eefe265c5 | ||
|
|
edab83b7c5 | ||
|
|
78812e47b2 | ||
|
|
4d9baa38d0 | ||
|
|
55d345e236 | ||
|
|
61cd08fa13 | ||
|
|
72366a5b27 | ||
|
|
b4e3d8ee47 | ||
|
|
7a74e491da | ||
|
|
2c84cec044 | ||
|
|
35e8183f6a | ||
|
|
fc0029eed7 | ||
|
|
177f367a8c | ||
|
|
1b82f26d3e | ||
|
|
ec4aa606e2 | ||
|
|
c3947e1016 | ||
|
|
831aa4a014 | ||
|
|
b38360c9a5 | ||
|
|
34a92b759e | ||
|
|
8cc775ac4c | ||
|
|
a0d8b28813 | ||
|
|
a3a323cbf1 | ||
|
|
4253d14367 | ||
|
|
96ccf6b923 | ||
|
|
eb1b9247ad | ||
|
|
57de44694c | ||
|
|
8d7a492936 | ||
|
|
7d1c5c50c5 | ||
|
|
7f3c300240 | ||
|
|
c5de41f183 | ||
|
|
e5b8d8bd2d | ||
|
|
ad7d485eb5 | ||
|
|
a1d0541a7a | ||
|
|
e1525fca6e | ||
|
|
872acd329a | ||
|
|
baa907f738 | ||
|
|
21d0676399 | ||
|
|
9a29b29a04 | ||
|
|
58497f29e6 | ||
|
|
9cdccd7005 | ||
|
|
3097336054 | ||
|
|
0449aaba0b | ||
|
|
775b98b729 | ||
|
|
4ac8012117 | ||
|
|
83025fb527 | ||
|
|
1019948216 | ||
|
|
f5883db889 | ||
|
|
d30965554c | ||
|
|
0769d59054 | ||
|
|
332a7403ed | ||
|
|
eef4f0afa2 | ||
|
|
5b7bb9f983 | ||
|
|
7eb59f577c | ||
|
|
250043dd75 | ||
|
|
ea476e738c | ||
|
|
7ce18860a6 | ||
|
|
75085302c9 | ||
|
|
4c8712c70d | ||
|
|
56625b0b90 | ||
|
|
c9aec495d5 | ||
|
|
f2b4702893 | ||
|
|
b3a932e903 | ||
|
|
ac91a8a3a8 | ||
|
|
33af96265b | ||
|
|
30e8dc53e5 | ||
|
|
6e41567f9e | ||
|
|
7c008b1693 | ||
|
|
367a35912b | ||
|
|
4f53a608a7 | ||
|
|
137f3bc151 | ||
|
|
0abf686f66 | ||
|
|
83f02b1ebc | ||
|
|
f5ac3abb2a | ||
|
|
ad04d302af | ||
|
|
c217233e08 | ||
|
|
98102e59f2 | ||
|
|
8f4ee3a089 | ||
|
|
70d1903dca | ||
|
|
feacbbff74 | ||
|
|
f065dcf4ad | ||
|
|
addcbdd8ca | ||
|
|
054f21821c | ||
|
|
38a3a5c6e8 | ||
|
|
1ee243f2bd | ||
|
|
191c154150 | ||
|
|
378759e06d | ||
|
|
c5c74e9537 | ||
|
|
e34f503674 | ||
|
|
bfcefa0217 | ||
|
|
c6bdb5752a | ||
|
|
68d4dcd7e6 | ||
|
|
b2f3a23cb3 | ||
|
|
93795d2f29 | ||
|
|
adf4b95ed3 | ||
|
|
ce3a06f03b | ||
|
|
2c0c3ea24e | ||
|
|
2d9cf672b8 | ||
|
|
ae766f52c7 | ||
|
|
107b0b791f | ||
|
|
985233ac38 | ||
|
|
424bf7647b | ||
|
|
06bc92556e | ||
|
|
7c4b2c9b39 | ||
|
|
b8e73f4fa5 | ||
|
|
bfb01898c2 | ||
|
|
d6db498853 | ||
|
|
80b40bb2c0 | ||
|
|
c28d1af877 | ||
|
|
f38535b2f4 | ||
|
|
093b5b99a0 | ||
|
|
490d374cfd | ||
|
|
21a1f02ea3 | ||
|
|
1a492722dd | ||
|
|
8a15c91a4f | ||
|
|
b99b323c4c | ||
|
|
0e32d478d1 | ||
|
|
60bd5c8a79 | ||
|
|
bbbb45d224 | ||
|
|
97b5cd306f | ||
|
|
b3a847e581 | ||
|
|
11810c9b3e | ||
|
|
60cd1250a0 | ||
|
|
edbfc06a41 | ||
|
|
769b4f8d66 | ||
|
|
eac4e455fd | ||
|
|
a4d946b4a9 | ||
|
|
974e1878f8 | ||
|
|
14c5a8ca5b | ||
|
|
e3c5a93f4f | ||
|
|
8f3d18a809 | ||
|
|
e295d75e6e | ||
|
|
d9bfcdab8e | ||
|
|
e3dac16398 | ||
|
|
e4c71123ef | ||
|
|
5c6c6cd9f0 | ||
|
|
677bd5cfc9 | ||
|
|
fb2a1c59db | ||
|
|
c88cbaa973 | ||
|
|
e6b25bd57b | ||
|
|
3faf48706a | ||
|
|
1ca93a678e | ||
|
|
e518fb1191 | ||
|
|
5811d2a13b | ||
|
|
5392ca788c | ||
|
|
6f825fa413 | ||
|
|
02184663e5 | ||
|
|
29317b980e | ||
|
|
c1ccbe8186 | ||
|
|
4f2796ac58 | ||
|
|
d0e3062bee | ||
|
|
748651447a | ||
|
|
2ca193e63b | ||
|
|
09cfe41e4f | ||
|
|
1a82d6da44 | ||
|
|
010b4ce783 | ||
|
|
dee46d527a | ||
|
|
693a77ae51 | ||
|
|
49d8713388 | ||
|
|
1b9c4204a8 | ||
|
|
5e8084c194 | ||
|
|
acb03c430e | ||
|
|
40037f25f2 | ||
|
|
665c046717 | ||
|
|
2081852004 | ||
|
|
69451d14a6 | ||
|
|
3523325663 | ||
|
|
f07adb5edd | ||
|
|
1647282f0b | ||
|
|
e42278f93f | ||
|
|
4c04a7bd5f | ||
|
|
d913edbcb6 | ||
|
|
adbc1d2997 |
+66
-33
@@ -39,7 +39,7 @@ volumes:
|
||||
|
||||
services:
|
||||
- name: test-mysql-unit
|
||||
image: mariadb:10
|
||||
image: mariadb:11
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
- name: tmp-mysql-unit
|
||||
path: /var/lib/mysql
|
||||
- name: test-mysql-integration
|
||||
image: mariadb:10
|
||||
image: mariadb:11
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
- name: tmp-mysql-integration
|
||||
path: /var/lib/mysql
|
||||
- name: test-mysql-migration
|
||||
image: mariadb:10
|
||||
image: mariadb:11
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
@@ -63,7 +63,7 @@ services:
|
||||
- name: tmp-mysql-migration
|
||||
path: /var/lib/mysql
|
||||
- name: test-postgres-unit
|
||||
image: postgres:14
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
@@ -73,7 +73,7 @@ services:
|
||||
commands:
|
||||
- docker-entrypoint.sh -c fsync=off -c full_page_writes=off # turns of wal
|
||||
- name: test-postgres-integration
|
||||
image: postgres:14
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
@@ -83,7 +83,7 @@ services:
|
||||
commands:
|
||||
- docker-entrypoint.sh -c fsync=off -c full_page_writes=off # turns of wal
|
||||
- name: test-postgres-migration
|
||||
image: postgres:14
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
@@ -121,38 +121,25 @@ steps:
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: prepare-build
|
||||
- name: build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ mage ]
|
||||
commands:
|
||||
- ./mage-static do-the-swag
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ prepare-build ]
|
||||
commands:
|
||||
- ./mage-static build:build
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: lint
|
||||
image: golang:1.19-alpine
|
||||
image: golangci/golangci-lint:v1.55.2
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ build ]
|
||||
commands:
|
||||
- export "GOROOT=$(go env GOROOT)"
|
||||
- apk --no-cache add build-base git
|
||||
- wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.2
|
||||
- ./mage-static check:golangci
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
@@ -230,7 +217,7 @@ steps:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -247,7 +234,7 @@ steps:
|
||||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -264,7 +251,7 @@ steps:
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -282,7 +269,7 @@ steps:
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -293,7 +280,7 @@ steps:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -310,7 +297,7 @@ steps:
|
||||
path: /db
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -327,7 +314,7 @@ steps:
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
@@ -345,10 +332,54 @@ steps:
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, prepare-build ]
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: generate-swagger-docs
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
include:
|
||||
- main
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: generate-swagger-docs
|
||||
image: vikunja/golang-build:latest
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- mage do-the-swag
|
||||
|
||||
- name: push
|
||||
pull: always
|
||||
image: appleboy/drone-git-push
|
||||
depends_on:
|
||||
- generate-swagger-docs
|
||||
settings:
|
||||
author_email: "frederik@vikunja.io"
|
||||
author_name: Frederick [Bot]
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated swagger docs"
|
||||
remote: "ssh://git@kolaente.dev:9022/vikunja/api.git"
|
||||
ssh_key:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
---
|
||||
########
|
||||
# Build a release when tagging
|
||||
@@ -396,7 +427,6 @@ steps:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:dirs
|
||||
- ./mage-static do-the-swag
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
|
||||
- name: static-build-windows
|
||||
@@ -406,6 +436,7 @@ steps:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
@@ -419,6 +450,7 @@ steps:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
@@ -432,6 +464,7 @@ steps:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
GOPROXY: https://goproxy.kolaente.de
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
@@ -518,7 +551,7 @@ steps:
|
||||
|
||||
# Build os packages and push it to our bucket
|
||||
- name: build-os-packages-unstable
|
||||
image: goreleaser/nfpm:v2.30.1
|
||||
image: goreleaser/nfpm:v2.34.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
@@ -534,7 +567,7 @@ steps:
|
||||
depends_on: [ after-build-compress ]
|
||||
|
||||
- name: build-os-packages-version
|
||||
image: goreleaser/nfpm:v2.30.1
|
||||
image: goreleaser/nfpm:v2.34.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
@@ -743,6 +776,6 @@ steps:
|
||||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: d47bd1cf6f3e9be2ff3eed2039e65c8b6de2b16c1e636699f66382f941277411
|
||||
hmac: e189b9133e2c96f81c06b90dd7793f0649c15aa809d4ee13c27f67d318f2edf3
|
||||
|
||||
...
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: 'Repo Lockdown'
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: opened
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/repo-lockdown@v3
|
||||
with:
|
||||
pr-comment: 'Hi! Thank you for your contribution.
|
||||
|
||||
This repo is only a mirror and unfortunately we can''t accept PRs made here. Please re-submit your changes to [our Gitea instance](https://kolaente.dev/vikunja/api/pulls).
|
||||
|
||||
Also check out the [contribution guidelines](https://vikunja.io/docs/development/#pull-requests).
|
||||
|
||||
Thank you for your understanding.'
|
||||
+1
-1
@@ -27,4 +27,4 @@ vikunja-dump*
|
||||
vendor/
|
||||
os-packages/
|
||||
mage_output_file.go
|
||||
pkg/swagger/*
|
||||
mage-static
|
||||
|
||||
+4
-2
@@ -6,7 +6,6 @@ linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- goerr113
|
||||
@@ -18,6 +17,7 @@ linters:
|
||||
disable:
|
||||
- scopelint # Obsolete, using exportloopref instead
|
||||
- durationcheck
|
||||
- goconst
|
||||
presets:
|
||||
- bugs
|
||||
- unused
|
||||
@@ -54,7 +54,6 @@ issues:
|
||||
- path: pkg/migration/*
|
||||
linters:
|
||||
- exhaustive
|
||||
- goconst
|
||||
- goerr113
|
||||
- path: pkg/models/task_collection_filter\.go
|
||||
linters:
|
||||
@@ -97,3 +96,6 @@ issues:
|
||||
- musttag
|
||||
- path: pkg/models/task_collection.go
|
||||
text: 'append result not assigned to the same slice'
|
||||
- path: pkg/modules/migration/ticktick/ticktick_test.go
|
||||
linters:
|
||||
- testifylint
|
||||
|
||||
+258
-1
@@ -7,6 +7,263 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
|
||||
## [0.22.0] - 2023-12-19
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(api tokens)* Make sure read one routes show up in routes endpoint
|
||||
* *(api tokens)* Test
|
||||
* *(api tokens)* Lint
|
||||
* *(api tokens)* Make sure task create routes are available to use with the api
|
||||
- **BREAKING**: The api route to create a new task is now /projects/:project/tasks instead of /projects/:project
|
||||
* *(build)* Don't run go mod commands when generating swagger docs
|
||||
* *(build)* Don't generate swagger files when building
|
||||
* *(build)* Don't require swagger to build
|
||||
* *(build)* Don't remove swagger files when running build:clean step
|
||||
* *(caldav)* Check for related tasks synced back from a caldav client
|
||||
* *(caldav)* Do not update dates of tasks when repositioning them (#1605)
|
||||
* *(ci)* Don't generate swagger docs in ci
|
||||
* *(ci)* Use the same go image for everything
|
||||
* *(ci)* Don't try to install when linting
|
||||
* *(cmd)* Do not initialize asnyc operations when running certain cli commands
|
||||
* *(comments)* Make sure comment sort order is stable
|
||||
* *(docs)* Add empty swagger file so that the package exists
|
||||
* *(docs)* Remove duplicate paths (params) in swagger docs
|
||||
* *(files)* Keyvalue init in tests
|
||||
* *(filter)* Assignee search by partial username test
|
||||
* *(filters)* Make "in" filter comparator work with Typesense
|
||||
* *(import)* Don't fail when importing from dev exports
|
||||
* *(import)* Ignore duplicate project identifier
|
||||
* *(import)* Resolve task relations by old task ids
|
||||
* *(import)* Correctly set child project relations
|
||||
* *(import)* Create related tasks without an id
|
||||
* *(import)* Make sure importing works if parent / child projects are created in a different order
|
||||
* *(kanban)* Don't prevent setting a different bucket as done bucket
|
||||
* *(kanban)* Create missing kanban buckets (#1601)
|
||||
* *(kanban)* Filter for tasks in buckets by assignee should not modify the filter directly
|
||||
* *(labels)* Make sure labels of shared sub projects are usable
|
||||
* *(migration)* Use string for todoist project note id
|
||||
* *(migration)* Make sub project hierarchy work when importing from other services
|
||||
* *(openid)* Make sure usernames with spaces work
|
||||
* *(project)* Duplicating a project should not create two backlog buckets
|
||||
* *(project background)* Add more checks for whether a background file exists when duplicating or deleting a project
|
||||
* *(projects)* Save done and default bucket when updating project
|
||||
* *(projects)* Don't limit results to top-level projects when searching
|
||||
* *(projects)* Don't return child projects multiple times
|
||||
* *(projects)* Correctly set project's archived state if their parent was archived
|
||||
* *(projects)* Delete child projects when deleting a project
|
||||
* *(reminders)* Make sure reminders are only sent once per user
|
||||
* *(swagger)* Add generated swagger docs to repo
|
||||
* *(task)* Remove task relation in the other direction as well
|
||||
* *(test)* Don't check for error
|
||||
* *(tests)* Use string IDs in Todoist test
|
||||
* *(tests)* Remove duplicate projects from assertions
|
||||
* *(tests)* Pass the map
|
||||
* *(typesense)* Upsert one document at a time
|
||||
* *(typesense)* Add more error logging
|
||||
* *(typesense)* Add more error logging
|
||||
* *(typesense)* Pass the correct user when fetching task comments
|
||||
* *(typesense)* Upsert all documents at once
|
||||
* *(typesense)* Explicitely create typesense sync table
|
||||
* *(typesense)* Don't try to index tasks if there are none
|
||||
* *(typesense)* Add typesense sync to initial structs
|
||||
* *(typesense)* Make sure searching works when no task has a comment at index time
|
||||
* *(typesense)* Getting all data from typesense
|
||||
* *(typesense)* Correctly convert date values for typesense
|
||||
* *(user)* Don't crash when attempting to change a user's password
|
||||
* *(user)* Allow deleting a user if they have a default project
|
||||
* *(user)* Don't prevent deleting a user if their default project was shared
|
||||
* *(user)* Allow openid users to request their deletion
|
||||
* *(webhooks)* Routes should use the common schema used for other routes already
|
||||
* *(webhooks)* Don't send the proxy auth header to the webhook target
|
||||
* *(webhooks)* Lint
|
||||
* *(webhooks)* Lint
|
||||
* *(webhooks)* Add created by user object when creating a webhook
|
||||
* *(webhooks)* Send application/json header* Typo ([49d8713](49d87133885b4fa660c300fc38768bd91f56340e))
|
||||
* Lint ([29317b9](29317b980e68b7e10b127e7e93afff1dd56ace3e))
|
||||
* Order by clause in task comments ([5811d2a](5811d2a13b5a1017cdd0b393599ffe01db95e836))
|
||||
* Lint ([e4c7112](e4c71123ef91480d41284288bee38939cd17ae39))
|
||||
* Validate usernames on registration ([11810c9](11810c9b3e1a4bb4c5fc1f4a3ac44e8552f6a937))
|
||||
* Lint ([d6db498](d6db49885383ed3e4f98acf649dc302ed1411ccd))
|
||||
* Lint ([b8e73f4](b8e73f4fa5821ce07b42667cf84c1ff9b87e0888))
|
||||
* Lint ([424bf76](424bf7647baa34e0fa594c2c36eec542ebea531b))
|
||||
* Lint ([e34f503](e34f503674c2aab06c7215cba9e2133037e96b6a))
|
||||
* Lint ([56625b0](56625b0b90d659bd49fc95749691d0100e964dcd))
|
||||
* Properly tag bucket-related operations ([a375223](a3752238729d50b38a5cf0b811e050c3d9f8985f))
|
||||
* Lint ([6ef1bc3](6ef1bc3944980588238fb44295b520695a4ed19a))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.4.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 617d3b6
|
||||
* *(deps)* Update module github.com/iancoleman/strcase to v0.3.0
|
||||
* *(deps)* Update module github.com/labstack/echo/v4 to v4.11.0
|
||||
* *(deps)* Update module github.com/labstack/echo/v4 to v4.11.1
|
||||
* *(deps)* Update module xorm.io/builder to v0.3.13
|
||||
* *(deps)* Update module golang.org/x/image to v0.11.0
|
||||
* *(deps)* Update module github.com/getsentry/sentry-go to v0.23.0
|
||||
* *(deps)* Update module github.com/arran4/golang-ical to v0.1.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 1510ee0
|
||||
* *(deps)* Update module github.com/yuin/goldmark to v1.5.6
|
||||
* *(deps)* Update module xorm.io/xorm to v1.3.3
|
||||
* *(deps)* Update module github.com/jinzhu/copier to v0.4.0
|
||||
* *(deps)* Update module github.com/redis/go-redis/v9 to v9.2.1
|
||||
* *(deps)* Update module github.com/threedotslabs/watermill to v1.3.5
|
||||
* *(deps)* Update module golang.org/x/oauth2 to v0.13.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update github.com/dustinkirkland/golang-petname digest to 6a283f1
|
||||
* *(deps)* Update module github.com/prometheus/client_golang to v1.17.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 6fc6b16
|
||||
* *(deps)* Update module github.com/getsentry/sentry-go to v0.25.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update module github.com/spf13/viper to v1.17.0
|
||||
* *(deps)* Update module github.com/spf13/afero to v1.10.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update module github.com/swaggo/swag to v1.16.2
|
||||
* *(deps)* Update module golang.org/x/image to v0.13.0
|
||||
* *(deps)* Update module golang.org/x/sync to v0.4.0
|
||||
* *(deps)* Update module github.com/labstack/echo/v4 to v4.11.2
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update postgres docker tag to v16 (#1618)
|
||||
* *(deps)* Update goreleaser/nfpm docker tag to v2.33.1 (#1560)
|
||||
* *(deps)* Update mariadb docker tag to v11 (#1544)
|
||||
* *(deps)* Update xgo to go 1.21
|
||||
* *(deps)* Update module github.com/gabriel-vasile/mimetype to v1.4.3
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update module github.com/coreos/go-oidc/v3 to v3.7.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to ecfba3d
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update module src.techknowlogick.com/xormigrate to v1.6.0 (#1627)
|
||||
* *(deps)* Update module github.com/google/uuid to v1.4.0
|
||||
* *(deps)* Update module src.techknowlogick.com/xormigrate to v1.7.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update module xorm.io/xorm to v1.3.4 (#1630)
|
||||
* *(deps)* Update module github.com/redis/go-redis/v9 to v9.3.0
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.18
|
||||
* *(deps)* Update module golang.org/x/sync to v0.5.0
|
||||
* *(deps)* Update module golang.org/x/sys to v0.14.0
|
||||
* *(deps)* Update module github.com/spf13/cobra to v1.8.0
|
||||
* *(deps)* Update module src.techknowlogick.com/xormigrate to v1.7.1
|
||||
* *(deps)* Update module github.com/yuin/goldmark to v1.6.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update github.com/go-jose/go-jose/v3
|
||||
* *(deps)* Update module golang.org/x/text to v0.14.0
|
||||
* *(deps)* Update module golang.org/x/oauth2 to v0.15.0
|
||||
* *(deps)* Update module golang.org/x/crypto to v0.17.0
|
||||
* *(deps)* Update module golang.org/x/term to v0.15.0
|
||||
* *(deps)* Update module golang.org/x/image to v0.14.0
|
||||
* *(deps)* Update module github.com/golang-jwt/jwt/v5 to v5.2.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to c7ed783
|
||||
* *(deps)* Update module github.com/labstack/echo/v4 to v4.11.3
|
||||
* *(deps)* Update golangci/golangci-lint docker tag to v1.55.2
|
||||
* *(deps)* Update goreleaser/nfpm docker tag to v2.34.0
|
||||
* *(deps)* Update lockfile
|
||||
* *(deps)* Update golangci-lint rules
|
||||
* *(deps)* Update sqlite bindings
|
||||
* *(deps)* Update deps
|
||||
|
||||
### Documentation
|
||||
|
||||
* *(webhooks)* Add general docs about webhooks
|
||||
* *(webhooks)* Add swagger docs for all webhook endpoints* Add Caddyfile to reverse proxies setup (#1580) ([665c046](665c04671739fd08e5b24e59749707ce5de83daa))
|
||||
* *(webhooks)* Add webhook config to sample config
|
||||
* Add Authentik example config (#1660) ([4615b4d](4615b4dbfbbf8514d9c41176e6e68a8ba3a453ce))
|
||||
* Add config guide for NGINX Proxy Manager ([a1d0541](a1d0541a7a6926127ba0bac4df03ce62b74f0c84))
|
||||
* Add n8n docs ([6a7aec2](6a7aec2e9ded619b074ef27f360c96c313e4449c))
|
||||
* Add typesense setup ([70d1903](70d1903dcac67e33bdfdf54d0ba561af76dbf927))
|
||||
* Clarify minimum required go version ([a2925cf](a2925cf55bee4c71ac5be1bad66cb3ec2230056d))
|
||||
* Clarify required language code ([e1525fc](e1525fca6eb5af17afa332d2c76a37b288673c5b))
|
||||
* Fix typo ([db0153a](db0153a7213a9b0bbafb43bc2762e2060f1ec9d1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* *(api tokens)* Add api token struct and migration
|
||||
* *(api tokens)* Add crud routes to manage api tokens
|
||||
* *(api tokens)* Add tests
|
||||
* *(api tokens)* Better error message for invalid tokens
|
||||
* *(api tokens)* Check for expiry date
|
||||
* *(api tokens)* Check for scopes
|
||||
* *(api tokens)* Check if a provided token matched a hashed on in the database
|
||||
* *(api tokens)* Check permissions when saving
|
||||
* *(api tokens)* Move token validation middleware to new function
|
||||
* *(api tokens)* Properly hash tokens
|
||||
* *(api)* Enable notifications for api token routes
|
||||
* *(caldav)* Add support for subtasks (i.e. `RELATED-TO` property) in CalDAV (#1634)
|
||||
* *(cli)* Added --confirm/-c argument when deleting users to bypass prompt (#86)
|
||||
* *(docs)* Update sample config and docs about Typesense config
|
||||
* *(metrics)* Add active link share logins
|
||||
* *(metrics)* Add total number of attachments metric
|
||||
* *(metrics)* Add total number of files metric
|
||||
* *(migration)* Migration from other services now happens in the background
|
||||
* *(notifications)* Add endpoint to mark all notifications as read
|
||||
* *(notify)* Don't notify disabled users
|
||||
* *(reminders)* Include project in reminder notification
|
||||
* *(tasks)* Add periodic resync of updated tasks to Typesense
|
||||
* *(tasks)* Add searching via typesense
|
||||
* *(tasks)* Add typesense indexing
|
||||
* *(tasks)* Allow filtering for reminders, assignees and labels with Typesense
|
||||
* *(tasks)* Find tasks by their identifier when searching with Typesense
|
||||
* *(tasks)* Make sorting and filtering work with Typesense
|
||||
* *(tasks)* Remove deleted tasks from Typesense
|
||||
* *(typesense)* Add new tasks to typesense directly when they are created
|
||||
* *(webhooks)* Add basic crud actions for webhooks
|
||||
* *(webhooks)* Add basic sending of webhooks
|
||||
* *(webhooks)* Add created by user object when returning all webhooks
|
||||
* *(webhooks)* Add event listener to send webhook payload
|
||||
* *(webhooks)* Add filter based on project id
|
||||
* *(webhooks)* Add hmac signing
|
||||
* *(webhooks)* Add index on project id
|
||||
* *(webhooks)* Add route to get all available webhook events
|
||||
* *(webhooks)* Add routes
|
||||
* *(webhooks)* Add setting to enable webhooks
|
||||
* *(webhooks)* Add support for webhook proxy
|
||||
* *(webhooks)* Add timeout config option
|
||||
* *(webhooks)* Expose whether webhooks are enabled
|
||||
* *(webhooks)* Prevent link shares from managing webhooks
|
||||
* *(webhooks)* Register task and project events as webhook
|
||||
* *(webhooks)* Set user agent header to Vikunja
|
||||
* *(webhooks)* Validate events and target url* Search improvemens (#1598) ([6f825fa](6f825fa4133a3200dab8a46faa2932cf5633263c))
|
||||
* Accept hex values which start with a # ([a1ea77f](a1ea77f7519efe7696bce018814071cbabaaa62c))
|
||||
* Add demo mode flag ([97b5cd3](97b5cd306f44a23d5f8923b1cf750533c1ca3e10))
|
||||
* Add setting for default bucket ([b99b323](b99b323c4c5a003c5b34e0196da566816469c608))
|
||||
* Add very basic bruno collection ([7eb59f5](7eb59f577c32791af77770e5c4ca2e1d7c01ee04))
|
||||
* Api tokens ([60cd125](60cd1250a0431f33748f83da3256f19ee8144dde))
|
||||
* Convert all markdown content to html (#1625) ([8a4856a](8a4856ad8747dd590f61e80212f77fb6e41cfb4b))
|
||||
* Endpoint to get all token routes ([1ca93a6](1ca93a678e6d931aa3afb3aaa654763ee8304d3b))
|
||||
* Make default bucket configurable ([60bd5c8](60bd5c8a79af18b09cb87c650436d0eff771d670))
|
||||
* Make unauthenticated user routes rate limit configurable ([c6c465c](c6c465c273037fd2c1f02360e647366834ab0cde))
|
||||
* Move done bucket setting to project ([bbbb45d](bbbb45d22461ed88d744cc1d66f74a743a51b843))
|
||||
* Webhooks (#1624) ([4d9baa3](4d9baa38d0861c082aa21713744927d520750fd6))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* *(api tokens)* Add swagger docs about api token auth
|
||||
* *(api tokens)* Remove updated date from tokens as it can't be updated anyway
|
||||
* *(build)* Use our own goproxy to prevent issues with packages not found
|
||||
* *(caldav)* Improve trimming .ics file ending
|
||||
* *(ci)* Sign drone config
|
||||
* *(ci)* Use golangci-lint docker image for lint step
|
||||
* *(tasks)* Better error messages when indexing tasks into Typesense
|
||||
* *(test)* Add task deleted assertion to project deletion test
|
||||
* *(webhooks)* Remove WebhookEvent interface
|
||||
* *(webhooks)* Reuse webhook client
|
||||
* *(webhooks)* Simplify registering webhook events* Remove year from copyright headers ([e518fb1](e518fb1191c0a21180f91bf2defcef80e26f02a7))
|
||||
* Add pr lockdown ([0abf686](0abf686f6630e052c43537cfcaf7b90eebcaa910))
|
||||
* Assume username instead of id when parsing fails for user commands (#87) ([137f3bc](137f3bc151d6417ba3cc8362afec1e7457915ef5))
|
||||
* Go mod tidy ([7c4b2c9](7c4b2c9b3911214d42ab9ab9a01605828013da55))
|
||||
* Reverse the coupling of module log and config (#1606) ([ad04d30](ad04d302af94fe3cf8e5a70ebb87af9002da5610))
|
||||
* Update contributing guidelines ([83f02b1](83f02b1ebc4ceda8226fb6d9c004241c0c47ae8d))
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* *(other)* [skip ci] Updated swagger docs
|
||||
|
||||
|
||||
## [0.21.0] - 2023-07-07
|
||||
|
||||
### Bug Fixes
|
||||
@@ -2174,4 +2431,4 @@ Misc bugfixes and improvements to the build process
|
||||
|
||||
## [0.2] - 2018-10-17
|
||||
|
||||
## [0.1] - 2018-09-20
|
||||
## [0.1] - 2018-09-20
|
||||
@@ -0,0 +1,3 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
Please check out the guidelines on https://vikunja.io/docs/development/
|
||||
+2
-1
@@ -3,7 +3,7 @@
|
||||
# │─││ │││ │ │
|
||||
# ┘─┘┘─┘┘┘─┘┘─┘
|
||||
|
||||
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.20.x AS builder
|
||||
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.21.x AS builder
|
||||
|
||||
RUN go install github.com/magefile/mage@latest && \
|
||||
mv /go/bin/mage /usr/local/go/bin
|
||||
@@ -13,6 +13,7 @@ COPY . ./
|
||||
|
||||
ARG TARGETOS TARGETARCH TARGETVARIANT
|
||||
|
||||
ENV GOPROXY https://goproxy.kolaente.de
|
||||
RUN export PATH=$PATH:$GOPATH/bin && \
|
||||
mage build:clean && \
|
||||
mage release:xgo "${TARGETOS}/${TARGETARCH}/${TARGETVARIANT}"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://drone.kolaente.de/vikunja/api)
|
||||
[](LICENSE)
|
||||
[](https://dl.vikunja.io)
|
||||
[](https://dl.vikunja.io)
|
||||
[](https://hub.docker.com/r/vikunja/api/)
|
||||
[](https://try.vikunja.io/api/v1/docs)
|
||||
[](https://goreportcard.com/report/kolaente.dev/vikunja/api)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -X POST http://localhost:3456/api/v1/register -H 'Content-Type: application/json' -d '{"username":"demo","password":"demo","email":"demo@vikunja.io"}'
|
||||
BEARER=`curl -X POST -H 'Content-Type: application/json' -d '{"username": "demo", "password":"demo"}' localhost:3456/api/v1/login | jq -r '.token'`
|
||||
|
||||
echo "Bearer: $BEARER"
|
||||
|
||||
curl -X POST localhost:3456/api/v1/tokenTest -H "Authorization: Bearer $BEARER"
|
||||
|
||||
curl -X PUT localhost:3456/api/v1/namespaces/1/lists -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"title":"lorem"}'
|
||||
curl -X PUT localhost:3456/api/v1/lists/1 -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"text":"lorem"}'
|
||||
curl -X PUT -H "Authorization: Bearer $BEARER" localhost:3456/api/v1/tasks/1/attachments -F 'files=@/home/konrad/Pictures/Wallpaper/greg-rakozy-_Q4mepyyjMw-unsplash.jpg'
|
||||
@@ -1,29 +0,0 @@
|
||||
### Authorization by token, part 1. Retrieve and save token.
|
||||
POST http://localhost:8080/api/v1/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "user3",
|
||||
"password": "1234"
|
||||
}
|
||||
|
||||
> {% client.global.set("auth_token", response.body.token); %}
|
||||
|
||||
### Register
|
||||
|
||||
POST http://localhost:8080/api/v1/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "user",
|
||||
"password": "1234",
|
||||
"email": "5@knt.li"
|
||||
}
|
||||
|
||||
###
|
||||
# Token test
|
||||
POST http://localhost:8080/api/v1/tokenTest
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
@@ -1,70 +0,0 @@
|
||||
# Get all labels
|
||||
GET http://localhost:8080/api/v1/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label
|
||||
PUT http://localhost:8080/api/v1/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "test5"
|
||||
}
|
||||
|
||||
###
|
||||
# Delete a label
|
||||
DELETE http://localhost:8080/api/v1/labels/6
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Update a label
|
||||
POST http://localhost:8080/api/v1/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "testschinkenbrot",
|
||||
"description": "käsebrot"
|
||||
}
|
||||
|
||||
###
|
||||
# Get one label
|
||||
GET http://localhost:8080/api/v1/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Get all labels on a task
|
||||
GET http://localhost:8080/api/v1/tasks/3565/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label to a task
|
||||
PUT http://localhost:8080/api/v1/tasks/35236365/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"label_id": 1
|
||||
}
|
||||
|
||||
###
|
||||
# Delete a label from a task
|
||||
DELETE http://localhost:8080/api/v1/tasks/3565/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label to a task
|
||||
POST http://localhost:8080/api/v1/tasks/3565/labels/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"labels": [
|
||||
{"id": 1},
|
||||
{"id": 2},
|
||||
{"id": 3}
|
||||
]
|
||||
}
|
||||
|
||||
###
|
||||
@@ -1,177 +0,0 @@
|
||||
# Get all lists
|
||||
GET http://localhost:8080/api/v1/namespaces/35/lists
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get one list
|
||||
GET http://localhost:8080/api/v1/lists/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Add a new list
|
||||
PUT http://localhost:8080/api/v1/namespaces/35/lists
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "test"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Add a new item
|
||||
PUT http://localhost:8080/api/v1/lists/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"text": "Task",
|
||||
"description": "Schinken"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Delete a task from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/14
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all teams who have access to that list
|
||||
GET http://localhost:8080/api/v1/lists/28/teams
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Give a team access to that list
|
||||
PUT http://localhost:8080/api/v1/lists/1/teams
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"team_id":2, "right": 1}
|
||||
|
||||
###
|
||||
|
||||
# Update a teams access to that list
|
||||
POST http://localhost:8080/api/v1/lists/1/teams/2
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"right": 0}
|
||||
|
||||
###
|
||||
|
||||
# Delete a team from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/10235/teams/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Delete a team from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/10235/teams/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all users who have access to that list
|
||||
GET http://localhost:8080/api/v1/lists/28/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Give a user access to that list
|
||||
PUT http://localhost:8080/api/v1/lists/3/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"userID":"user4", "right":1}
|
||||
|
||||
###
|
||||
|
||||
# Update a users access to that list
|
||||
POST http://localhost:8080/api/v1/lists/30/users/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"right":2}
|
||||
|
||||
###
|
||||
|
||||
# Delete a user from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/28/users/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks
|
||||
GET http://localhost:8080/api/v1/tasks/all
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks with priorities
|
||||
GET http://localhost:8080/api/v1/tasks/all?sort=priorityasc
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks in a range
|
||||
GET http://localhost:8080/api/v1/tasks/all/dueadateasc/1546784000/1548784000
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks in caldav
|
||||
GET http://localhost:8080/api/v1/tasks/caldav
|
||||
#Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Update a task
|
||||
POST http://localhost:8080/api/v1/tasks/3565
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"priority": 0
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Bulk update multiple tasks at once
|
||||
POST http://localhost:8080/api/v1/tasks/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"task_ids": [3518,3519,3521],
|
||||
"text":"bulkupdated"
|
||||
}
|
||||
|
||||
###
|
||||
# Get all assignees
|
||||
GET http://localhost:8080/api/v1/tasks/3565/assignees
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Add a bunch of assignees
|
||||
PUT http://localhost:8080/api/v1/tasks/3565/assignees/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"assignees": [
|
||||
{"id": 17}
|
||||
]
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Get all users who have access to a list
|
||||
GET http://localhost:8080/api/v1/lists/3/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
@@ -1,71 +0,0 @@
|
||||
# Get all namespaces
|
||||
GET http://localhost:8080/api/v1/namespaces
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get one namespaces
|
||||
GET http://localhost:8080/api/v1/namespaces/-1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all users who have access to that namespace
|
||||
GET http://localhost:8080/api/v1/namespaces/12/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Give a user access to that namespace
|
||||
PUT http://localhost:8080/api/v1/namespaces/1/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"user_id":3, "right": 0}
|
||||
|
||||
###
|
||||
|
||||
# Update a users access to that namespace
|
||||
POST http://localhost:8080/api/v1/namespaces/1/users/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"right": 2}
|
||||
|
||||
###
|
||||
|
||||
# Delete a user from a namespace
|
||||
DELETE http://localhost:8080/api/v1/namespaces/1/users/2
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all teams who have access to that namespace
|
||||
GET http://localhost:8080/api/v1/namespaces/1/teams
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Give a team access to that namespace
|
||||
PUT http://localhost:8080/api/v1/namespaces/1/teams
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"team_id":3, "right": 0}
|
||||
|
||||
###
|
||||
|
||||
# Update a teams access to that namespace
|
||||
POST http://localhost:8080/api/v1/namespaces/1/teams/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"right": 0}
|
||||
|
||||
###
|
||||
|
||||
# Delete a team from a namespace
|
||||
DELETE http://localhost:8080/api/v1/namespaces/1/teams/2
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
@@ -1,29 +0,0 @@
|
||||
# Get all teams
|
||||
GET http://localhost:8080/api/v1/teams
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get one team
|
||||
GET http://localhost:8080/api/v1/teams/28
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Add a new member to that team
|
||||
PUT http://localhost:8080/api/v1/teams/28/members
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"user_id": 2
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Delete a member from a team
|
||||
DELETE http://localhost:8080/api/v1/teams/28/members/2
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
# Get all users
|
||||
GET http://localhost:8080/api/v1/user
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
######
|
||||
# Search for a user
|
||||
GET http://localhost:8080/api/v1/users?s=3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
## Update password
|
||||
|
||||
POST http://localhost:8080/api/v1/user/password
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"old_password": "1234",
|
||||
"new_password": "1234"
|
||||
}
|
||||
|
||||
### Request a password to reset a password
|
||||
POST http://localhost:8080/api/v1/user/password/token
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"email": "k@knt.li"
|
||||
}
|
||||
|
||||
### Request a token to reset a password
|
||||
POST http://localhost:8080/api/v1/user/password/reset
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"token": "eAsZzakgqARnjzXHqsHqZtSUKuiOhoJjHANhgTxUIDBSalhbtdpAdLeywGXzVDBuRQGNpHdMxoHXhLVSlzpJsFvuoJgMdkhRhkNhaQXfufuZCdtUlerZHSJQLgYMUryHIxIREcmZLtWoZVrYyARkCvkyFhcGtoCwQOEjAOEZMQQuxTVoGYfAqcfNggQnerUcXCiRIgRtkusXSnltomhaeyRwAbrckXFeXxUjslgplSGqSTOqJTYuhrSzAVTwNvuYyvuXLaZoNnJEyeVDWlRydnxfgUQjQZOKwCBRWVQPKpZhlslLUyUAMsRQkHITkruQCjDnOGCCRsSNplbNCEuDmMfpWYHSQAcQIDZtbQWkxzpfmHDMQvvKPPrxEnrTErlvTfKDKICFYPQxXNpNE",
|
||||
"new_password": "1234"
|
||||
}
|
||||
|
||||
### Confirm a users email address
|
||||
|
||||
POST http://localhost:8080/api/v1/user/confirm
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"token": ""
|
||||
}
|
||||
|
||||
###
|
||||
@@ -1,5 +1,5 @@
|
||||
Vikunja is a to-do list application to facilitate your life.
|
||||
Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+30
-1
@@ -59,6 +59,9 @@ service:
|
||||
# The maximum size clients will be able to request for user avatars.
|
||||
# If clients request a size bigger than this, it will be changed on the fly.
|
||||
maxavatarsize: 1024
|
||||
# If set to true, the frontend will show a big red warning not to use this instance for real data as it will be cleared out.
|
||||
# You probably don't need to set this value, it was created specifically for usage on [try](https://try.vikunja.io).
|
||||
demomode: false
|
||||
|
||||
database:
|
||||
# Database type to use. Supported types are mysql, postgres and sqlite.
|
||||
@@ -91,6 +94,18 @@ database:
|
||||
# Enable SSL/TLS for mysql connections. Options: false, true, skip-verify, preferred
|
||||
tls: false
|
||||
|
||||
typesense:
|
||||
# Whether to enable the Typesense integration. If true, all tasks will be synced to the configured Typesense
|
||||
# instance and all search and filtering will run through Typesense instead of only through the database.
|
||||
# Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
# what you'd get with a database-only search.
|
||||
enabled: false
|
||||
# The url to the Typesense instance you want to use. Can be hosted locally or in Typesense Cloud as long
|
||||
# as Vikunja is able to reach it.
|
||||
url: ''
|
||||
# The Typesense API key you want to use.
|
||||
apikey: ''
|
||||
|
||||
redis:
|
||||
# Whether to enable redis or not
|
||||
enabled: false
|
||||
@@ -176,6 +191,10 @@ ratelimit:
|
||||
# Possible values are "keyvalue", "memory" or "redis".
|
||||
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
store: keyvalue
|
||||
# The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
# password confirmation, email verification, password reset request) per minute. This limit cannot be disabled.
|
||||
# You should only change this if you know what you're doing.
|
||||
noauthlimit: 10
|
||||
|
||||
files:
|
||||
# The path where files are stored
|
||||
@@ -327,7 +346,17 @@ defaultsettings:
|
||||
default_project_id: 0
|
||||
# Start of the week for the user. `0` is sunday, `1` is monday and so on.
|
||||
week_start: 0
|
||||
# The language of the user interface. Must be an ISO 639-1 language code. Will default to the browser language the user uses when signing up.
|
||||
# The language of the user interface. Must be an ISO 639-1 language code followed by an ISO 3166-1 alpha-2 country code. Check https://kolaente.dev/vikunja/frontend/src/branch/main/src/i18n/lang for a list of possible languages. Will default to the browser language the user uses when signing up.
|
||||
language: <unset>
|
||||
# The time zone of each individual user. This will affect when users get reminders and overdue task emails.
|
||||
timezone: <time zone set at service.timezone>
|
||||
|
||||
webhooks:
|
||||
# Whether to enable support for webhooks
|
||||
enabled: true
|
||||
# The timout in seconds until a webhook request fails when no response has been received.
|
||||
timoutseconds: 30
|
||||
# The URL of [a mole instance](https://github.com/frain-dev/mole) to use to proxy outgoing webhook requests. You should use this and configure appropriately if you're not the only one using your Vikunja instance. More info about why: https://webhooks.fyi/best-practices/webhook-providers#implement-security-on-egress-communication. Must be used in combination with `webhooks.password` (see below).
|
||||
proxyurl:
|
||||
# The proxy password to use when authenticating against the proxy.
|
||||
proxypassword:
|
||||
|
||||
@@ -17,7 +17,9 @@ menu:
|
||||
## General
|
||||
|
||||
To contribute to Vikunja, fork the project and work on the main branch.
|
||||
Once you feel like your changes are ready, open a PR in the respective repo.
|
||||
Once you feel like your changes are ready, open a PR in the respective repo [on our Gitea instance](https://kolaente.dev/vikunja).
|
||||
We cannot accept PRs on mirror sites.
|
||||
|
||||
A maintainer will take a look and give you feedback. Once everyone is happy, the PR gets merged and released.
|
||||
|
||||
If you plan to do a bigger change, it is better to open an issue for discussion first.
|
||||
@@ -26,7 +28,7 @@ If you plan to do a bigger change, it is better to open an issue for discussion
|
||||
|
||||
The code for the api is located at [code.vikunja.io/api](https://code.vikunja.io/api).
|
||||
|
||||
We use go modules to manage third-party libraries for Vikunja, so you'll need at least go `1.17` to use these.
|
||||
You'll need at least Go 1.21 to build Vikunja's api.
|
||||
|
||||
A lot of developing tasks are automated using a Magefile, so make sure to [take a look at it]({{< ref "mage.md">}}).
|
||||
|
||||
@@ -38,11 +40,51 @@ Make sure to check the other doc articles for specific development tasks like [t
|
||||
The code for the frontend is located at [code.vikunja.io/frontend](https://code.vikunja.io/frontend).
|
||||
More instructions can be found in the repo's README.
|
||||
|
||||
You need to have [pnpm](https://pnpm.io/) and nodejs in version 16 or 18 installed.
|
||||
You need to have [pnpm](https://pnpm.io/) and Node.JS in version 18 or higher installed.
|
||||
|
||||
## Git flow
|
||||
## Pull Requests
|
||||
|
||||
All Pull Requests must be made [on our Gitea instance](https://kolaente.dev/vikunja).
|
||||
We cannot accept PRs on mirror sites.
|
||||
|
||||
Please try to make your pull request easy to review.
|
||||
For that, please read the [*Best Practices for Faster Reviews*](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) guide.
|
||||
It has lots of useful tips for any project you may want to contribute to.
|
||||
Some of the key points:
|
||||
|
||||
- Make small pull requests.
|
||||
The smaller, the faster to review and the more likely it will be merged soon.
|
||||
- Don't make changes unrelated to your PR.
|
||||
Maybe there are typos on some comments, maybe refactoring would be welcome on a function…
|
||||
but if that is not related to your PR, please make *another* PR for that.
|
||||
- Split big pull requests into multiple small ones.
|
||||
An incremental change will be faster to review than a huge PR.
|
||||
- Allow edits by maintainers. This way, the maintainers will take care of merging the PR later on instead of you.
|
||||
|
||||
### PR title and summary
|
||||
|
||||
In the PR title, describe the problem you are fixing, not how you are fixing it.
|
||||
Use the first comment as a summary of your PR.
|
||||
In the PR summary, you can describe exactly how you are fixing this problem.
|
||||
Keep this summary up-to-date as the PR evolves.
|
||||
|
||||
If your PR changes the UI, you must add **after** screenshots in the PR summary.
|
||||
If your PR closes an issue, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like
|
||||
|
||||
```text
|
||||
Fixes/Closes/Resolves #<ISSUE_NR_X>.
|
||||
Fixes/Closes/Resolves #<ISSUE_NR_Y>.
|
||||
```
|
||||
|
||||
to your summary.
|
||||
Each issue that will be closed must stand on a separate line.
|
||||
|
||||
If your PR is related to a discussion in the forum, you must add a link to the forum discussion.
|
||||
|
||||
### Git flow
|
||||
|
||||
The `main` branch is the latest and bleeding edge branch with all changes. Unstable releases are automatically created from this branch.
|
||||
New Pull-Requests should be made against the `main` branch.
|
||||
|
||||
A release gets tagged from the main branch with the version name as tag name.
|
||||
|
||||
@@ -52,4 +94,4 @@ Backports and point-releases should go to a `release/version` branch, based on t
|
||||
|
||||
We're using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) because they greatly simplify generating release notes.
|
||||
|
||||
It is not required to use them when creating a PR, but appreciated.
|
||||
It is not required to use them when creating a PR, but appreciated.
|
||||
@@ -19,7 +19,7 @@ To completely build Vikunja from source, you need to build the api and frontend.
|
||||
The Vikunja API has no other dependencies than go itself.
|
||||
That means compiling it boils down to these steps:
|
||||
|
||||
1. Make sure [Go](https://golang.org/doc/install) is properly installed on your system. You'll need at least Go `1.19`.
|
||||
1. Make sure [Go](https://golang.org/doc/install) is properly installed on your system. You'll need at least Go `1.21`.
|
||||
2. Make sure [Mage](https://magefile.org) is properly installed on your system.
|
||||
3. Clone the repo with `git clone https://code.vikunja.io/api` and switch into the directory.
|
||||
4. Run `mage build` in the source of this repo. This will build a binary in the root of the repo which will be able to run on your system.
|
||||
|
||||
@@ -333,6 +333,18 @@ Full path: `service.maxavatarsize`
|
||||
Environment path: `VIKUNJA_SERVICE_MAXAVATARSIZE`
|
||||
|
||||
|
||||
### demomode
|
||||
|
||||
If set to true, the frontend will show a big red warning not to use this instance for real data as it will be cleared out.
|
||||
You probably don't need to set this value, it was created specifically for usage on [try](https://try.vikunja.io).
|
||||
|
||||
Default: `false`
|
||||
|
||||
Full path: `service.demomode`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_DEMOMODE`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## database
|
||||
@@ -494,6 +506,49 @@ Full path: `database.tls`
|
||||
Environment path: `VIKUNJA_DATABASE_TLS`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## typesense
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to enable the Typesense integration. If true, all tasks will be synced to the configured Typesense
|
||||
instance and all search and filtering will run through Typesense instead of only through the database.
|
||||
Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
what you'd get with a database-only search.
|
||||
|
||||
Default: `false`
|
||||
|
||||
Full path: `typesense.enabled`
|
||||
|
||||
Environment path: `VIKUNJA_TYPESENSE_ENABLED`
|
||||
|
||||
|
||||
### url
|
||||
|
||||
The url to the Typesense instance you want to use. Can be hosted locally or in Typesense Cloud as long
|
||||
as Vikunja is able to reach it.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `typesense.url`
|
||||
|
||||
Environment path: `VIKUNJA_TYPESENSE_URL`
|
||||
|
||||
|
||||
### apikey
|
||||
|
||||
The Typesense API key you want to use.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `typesense.apikey`
|
||||
|
||||
Environment path: `VIKUNJA_TYPESENSE_APIKEY`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## redis
|
||||
@@ -914,6 +969,19 @@ Full path: `ratelimit.store`
|
||||
Environment path: `VIKUNJA_RATELIMIT_STORE`
|
||||
|
||||
|
||||
### noauthlimit
|
||||
|
||||
The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
password confirmation, email verification, password reset request) per minute. This limit cannot be disabled.
|
||||
You should only change this if you know what you're doing.
|
||||
|
||||
Default: `10`
|
||||
|
||||
Full path: `ratelimit.noauthlimit`
|
||||
|
||||
Environment path: `VIKUNJA_RATELIMIT_NOAUTHLIMIT`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## files
|
||||
@@ -1252,7 +1320,7 @@ Environment path: `VIKUNJA_DEFAULTSETTINGS_WEEK_START`
|
||||
|
||||
### language
|
||||
|
||||
The language of the user interface. Must be an ISO 639-1 language code. Will default to the browser language the user uses when signing up.
|
||||
The language of the user interface. Must be an ISO 639-1 language code followed by an ISO 3166-1 alpha-2 country code. Check https://kolaente.dev/vikunja/frontend/src/branch/main/src/i18n/lang for a list of possible languages. Will default to the browser language the user uses when signing up.
|
||||
|
||||
Default: `<unset>`
|
||||
|
||||
@@ -1272,3 +1340,53 @@ Full path: `defaultsettings.timezone`
|
||||
Environment path: `VIKUNJA_DEFAULTSETTINGS_TIMEZONE`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## webhooks
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to enable support for webhooks
|
||||
|
||||
Default: `true`
|
||||
|
||||
Full path: `webhooks.enabled`
|
||||
|
||||
Environment path: `VIKUNJA_WEBHOOKS_ENABLED`
|
||||
|
||||
|
||||
### timoutseconds
|
||||
|
||||
The timout in seconds until a webhook request fails when no response has been received.
|
||||
|
||||
Default: `30`
|
||||
|
||||
Full path: `webhooks.timoutseconds`
|
||||
|
||||
Environment path: `VIKUNJA_WEBHOOKS_TIMOUTSECONDS`
|
||||
|
||||
|
||||
### proxyurl
|
||||
|
||||
The URL of [a mole instance](https://github.com/frain-dev/mole) to use to proxy outgoing webhook requests. You should use this and configure appropriately if you're not the only one using your Vikunja instance. More info about why: https://webhooks.fyi/best-practices/webhook-providers#implement-security-on-egress-communication. Must be used in combination with `webhooks.password` (see below).
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `webhooks.proxyurl`
|
||||
|
||||
Environment path: `VIKUNJA_WEBHOOKS_PROXYURL`
|
||||
|
||||
|
||||
### proxypassword
|
||||
|
||||
The proxy password to use when authenticating against the proxy.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `webhooks.proxypassword`
|
||||
|
||||
Environment path: `VIKUNJA_WEBHOOKS_PROXYPASSWORD`
|
||||
|
||||
|
||||
|
||||
@@ -88,3 +88,27 @@ Keycloak Config:
|
||||
- Set `Root Url` to `https://vikunja.mydomain.com`
|
||||
- Set `Valid redirect URIs` to `/auth/openid/keycloak`
|
||||
- Create the client the navigate to the credentials tab and copy the `Client secret`
|
||||
|
||||
## Authentik
|
||||
|
||||
Authentik Config:
|
||||
- Create a new Provider called "Vikunja" in Authentik
|
||||
- Set the `Redirect URIs/Origins (RegEx)` to `https://vikunja.mydomain.com/auth/openid/authentik`
|
||||
- Copy the Client ID and Client Secret
|
||||
|
||||
Vikunja Config:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
openid:
|
||||
enabled: true
|
||||
redirecturl: "https://vikunja.mydomain.com/auth/openid/"
|
||||
providers:
|
||||
- name: authentik
|
||||
authurl: "https://authentik.mydomain.com/application/o/vikunja"
|
||||
logouturl: "https://authentik.mydomain.com/application/o/vikunja/end-session/"
|
||||
clientid: "" # copy from Authetik
|
||||
clientsecret: "" # copy from Authentik
|
||||
```
|
||||
|
||||
**Note:** The `authurl` that Vikunja requires is not the `Authorize URL` that you can see in the Provider. Vikunja uses Open ID Discovery to find the correct endpoint to use. Vikunja does this by automatically accessing the `OpenID Configuration URL` (usually `https://authentik.mydomain.com/application/o/vikunja/.well-known/openid-configuration`). Use this URL without the `.well-known/openid-configuration` as the `authurl`.
|
||||
|
||||
@@ -1,127 +1,316 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Reverse Proxy"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Setup behind a reverse proxy which also serves the frontend
|
||||
|
||||
These examples assume you have an instance of the backend running on your server listening on port `3456`.
|
||||
If you've changed this setting, you need to update the server configurations accordingly.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## NGINX
|
||||
|
||||
Below are two example configurations which you can put in your `nginx.conf`:
|
||||
|
||||
You may need to adjust `server_name` and `root` accordingly.
|
||||
|
||||
### with gzip enabled (recommended)
|
||||
|
||||
{{< highlight conf >}}
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://localhost:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
|
||||
</div>
|
||||
|
||||
### without gzip
|
||||
|
||||
{{< highlight conf >}}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://localhost:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
|
||||
</div>
|
||||
|
||||
## NGINX Proxy Manager (NPM)
|
||||
|
||||
1. Create a standard Proxy Host for the Vikunja Frontend within NPM and point it to the URL you plan to use. The next several steps will enable the Proxy Host to successfully navigate to the API (on port 3456).
|
||||
2. Verify that the page will pull up in your browser. (Do not bother trying to log in. It won't work. Trust me.)
|
||||
3. Now, we'll work with the NPM container, so you need to identify the container name for your NPM installation. e.g. NGINX-PM
|
||||
4. From the command line, enter `sudo docker exec -it [NGINX-PM container name] /bin/bash` and navigate to the proxy hosts folder where the `.conf` files are stashed. Probably `/data/nginx/proxy_host`. (This folder is a persistent folder created in the NPM container and mounted by NPM.)
|
||||
5. Locate the `.conf` file where the server_name inside the file matches your Vikunja Proxy Host. Once found, add the following code, unchanged, just above the existing location block in that file. (They are listed by number, not name.)
|
||||
```nginx
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://api:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
```
|
||||
6. After saving the edited file, return to NPM's UI browser window and refresh the page to verify your Proxy Host for Vikunja is still online.
|
||||
7. Now, switch over to your Vikunja browser window and hit refresh. If you configured your URL correctly in original Vikunja container, you should be all set and the browser will correctly show Vikunja. If not, you'll need to adjust the address in the top of the login subscreen to match your proxy address.
|
||||
|
||||
## Apache
|
||||
|
||||
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
||||
|
||||
{{< highlight aconf >}}
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
|
||||
<Proxy *>
|
||||
Order Deny,Allow
|
||||
Allow from all
|
||||
</Proxy>
|
||||
ProxyPass /api http://localhost:3456/api
|
||||
ProxyPassReverse /api http://localhost:3456/api
|
||||
ProxyPass /dav http://localhost:3456/dav
|
||||
ProxyPassReverse /dav http://localhost:3456/dav
|
||||
ProxyPass /.well-known http://localhost:3456/.well-known
|
||||
ProxyPassReverse /.well-known http://localhost:3456/.well-known
|
||||
|
||||
DocumentRoot /var/www/html
|
||||
RewriteEngine On
|
||||
RewriteRule ^\/?(favicon\.ico|assets|audio|fonts|images|manifest\.webmanifest|robots\.txt|sw\.js|workbox-.*|api|dav|\.well-known) - [L]
|
||||
RewriteRule ^(.*)$ /index.html [QSA,L]
|
||||
</VirtualHost>
|
||||
{{< /highlight >}}
|
||||
|
||||
**Note:** The apache modules `proxy`, `proxy_http` and `rewrite` must be enabled for this.
|
||||
|
||||
For more details see the [frontend apache configuration]({{< ref "install-frontend.md#apache">}}).
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Reverse Proxy"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Setup behind a reverse proxy which also serves the frontend
|
||||
|
||||
These examples assume you have an instance of the backend running on your server listening on port `3456`.
|
||||
If you've changed this setting, you need to update the server configurations accordingly.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## NGINX
|
||||
|
||||
Below are two example configurations which you can put in your `nginx.conf`:
|
||||
|
||||
You may need to adjust `server_name` and `root` accordingly.
|
||||
|
||||
### with gzip enabled (recommended)
|
||||
|
||||
{{< highlight conf >}}
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://localhost:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
|
||||
</div>
|
||||
|
||||
### without gzip
|
||||
|
||||
{{< highlight conf >}}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://localhost:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
|
||||
</div>
|
||||
|
||||
## NGINX Proxy Manager (NPM)
|
||||
|
||||
### Method 1
|
||||
|
||||
Following the [Docker Walkthrough]({{< ref "docker-start-to-finish.md" >}}) guide, you should be able to get Vikunja to work via HTTP connection to your server IP.
|
||||
|
||||
From there, all you have to do is adjust the following things:
|
||||
|
||||
#### In `docker-compose.yml`
|
||||
|
||||
Under `api:`,
|
||||
|
||||
1. Change `VIKUNJA_SERVICE_FRONTENDURL:` to your desired domain with `https://` and `/`.
|
||||
|
||||
2. Expose your desired port on host under `ports:`.
|
||||
|
||||
example:
|
||||
|
||||
```yaml
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <your-random-secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://vikunja.your-domain.com/ # change vikunja.your-domain.com to your desired domain/subdomain.
|
||||
ports:
|
||||
- 3456:3456 # Change 3456 on the left to the port of your choice.
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Under `frontend:`,
|
||||
|
||||
1. Add `VIKUNJA_API_URL:` under `environment:` and input your desired `API` domain with `https://` and `/api/v1/`. The `API` domain should be different from the one in `VIKUNJA_SERVICE_FRONTENDURL:`.
|
||||
|
||||
example:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
environment:
|
||||
VIKUNJA_API_URL: https://api.your-domain.com/api/v1/ # change api.your-domain.com to your desired domain/subdomain, it should be different from your frontend domain
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Under `proxy:`,
|
||||
|
||||
1. Since we'll be using Nginx Proxy Manager, it should by default uses the port `80` and thus you should change `ports:` to expose another port not occupied by any service.
|
||||
|
||||
example:
|
||||
|
||||
```yaml
|
||||
proxy:
|
||||
image: nginx
|
||||
ports:
|
||||
- 1078:80 # change the number infront (host port) to whatever you desire, but make sure it's not 80 which will be used by Nginx Proxy Manager
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
#### In your DNS provider
|
||||
|
||||
Add two `A` records that points to your server IP.
|
||||
|
||||
1. `vikunja` for accessing the frontend
|
||||
|
||||
2. `api` for accessing the api
|
||||
|
||||
You are of course free to change them to whatever domain/subdomain you desire and modify the `docker-compose.yml` accordingly but the two should be different.
|
||||
|
||||
(Tested on Cloudflare DNS. Settings are different for different DNS provider, in this case the end result should bei `vikunja.your-domain.com` and `api.your-domain.com` respectively.)
|
||||
|
||||
#### In Nginx Proxy Manager
|
||||
|
||||
Add two Proxy Host as you normally would, and you don't have to add anything extra in Advanced.
|
||||
|
||||
##### Frontend
|
||||
|
||||
Under `Details`:
|
||||
|
||||
```
|
||||
Domain Names:
|
||||
vikunja.your-domain.com
|
||||
Scheme:
|
||||
http
|
||||
Forward Hostname/IP:
|
||||
your-server-ip
|
||||
Forward Port:
|
||||
1078
|
||||
Cached Assets:
|
||||
Optional.
|
||||
Block Common Exploits:
|
||||
Toggled.
|
||||
Websockets Support:
|
||||
Toggled.
|
||||
```
|
||||
|
||||
Under `SSL`:
|
||||
|
||||
```
|
||||
SSL Certificate:
|
||||
However you prefer.
|
||||
Force SSL:
|
||||
Toggled.
|
||||
HTTP/2 Support:
|
||||
Toggled.
|
||||
HSTS Enabled:
|
||||
Toggled.
|
||||
HSTS Subdomains:
|
||||
Toggled.
|
||||
Use a DNS Challenge:
|
||||
Not toggled.
|
||||
Email Address for Let's Encrypt:
|
||||
your-email@email.com
|
||||
```
|
||||
|
||||
##### API
|
||||
|
||||
Under `Details`:
|
||||
|
||||
```
|
||||
Domain Names:
|
||||
api.your-domain.com
|
||||
Scheme:
|
||||
http
|
||||
Forward Hostname/IP:
|
||||
your-server-ip
|
||||
Forward Port:
|
||||
3456
|
||||
Cached Assets:
|
||||
Optional.
|
||||
Block Common Exploits:
|
||||
Toggled.
|
||||
Websockets Support:
|
||||
Toggled.
|
||||
```
|
||||
|
||||
Under `SSL`:
|
||||
|
||||
```
|
||||
SSL Certificate:
|
||||
However you prefer.
|
||||
Force SSL:
|
||||
Toggled.
|
||||
HTTP/2 Support:
|
||||
Toggled.
|
||||
HSTS Enabled:
|
||||
Toggled.
|
||||
HSTS Subdomains:
|
||||
Toggled.
|
||||
Use a DNS Challenge:
|
||||
Not toggled.
|
||||
Email Address for Let's Encrypt:
|
||||
your-email@email.com
|
||||
```
|
||||
|
||||
Your Vikunja service should now work and your HTTPS frontend should be able to reach the API after `docker-compose`.
|
||||
|
||||
### Method 2
|
||||
|
||||
1. Create a standard Proxy Host for the Vikunja Frontend within NPM and point it to the URL you plan to use. The next several steps will enable the Proxy Host to successfully navigate to the API (on port 3456).
|
||||
2. Verify that the page will pull up in your browser. (Do not bother trying to log in. It won't work. Trust me.)
|
||||
3. Now, we'll work with the NPM container, so you need to identify the container name for your NPM installation. e.g. NGINX-PM
|
||||
4. From the command line, enter `sudo docker exec -it [NGINX-PM container name] /bin/bash` and navigate to the proxy hosts folder where the `.conf` files are stashed. Probably `/data/nginx/proxy_host`. (This folder is a persistent folder created in the NPM container and mounted by NPM.)
|
||||
5. Locate the `.conf` file where the server_name inside the file matches your Vikunja Proxy Host. Once found, add the following code, unchanged, just above the existing location block in that file. (They are listed by number, not name.)
|
||||
```nginx
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://api:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
```
|
||||
6. After saving the edited file, return to NPM's UI browser window and refresh the page to verify your Proxy Host for Vikunja is still online.
|
||||
7. Now, switch over to your Vikunja browser window and hit refresh. If you configured your URL correctly in original Vikunja container, you should be all set and the browser will correctly show Vikunja. If not, you'll need to adjust the address in the top of the login subscreen to match your proxy address.
|
||||
|
||||
## Apache
|
||||
|
||||
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
||||
|
||||
{{< highlight aconf >}}
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
|
||||
<Proxy *>
|
||||
Order Deny,Allow
|
||||
Allow from all
|
||||
</Proxy>
|
||||
ProxyPass /api http://localhost:3456/api
|
||||
ProxyPassReverse /api http://localhost:3456/api
|
||||
ProxyPass /dav http://localhost:3456/dav
|
||||
ProxyPassReverse /dav http://localhost:3456/dav
|
||||
ProxyPass /.well-known http://localhost:3456/.well-known
|
||||
ProxyPassReverse /.well-known http://localhost:3456/.well-known
|
||||
|
||||
DocumentRoot /var/www/html
|
||||
RewriteEngine On
|
||||
RewriteRule ^\/?(favicon\.ico|assets|audio|fonts|images|manifest\.webmanifest|robots\.txt|sw\.js|workbox-.*|api|dav|\.well-known) - [L]
|
||||
RewriteRule ^(.*)$ /index.html [QSA,L]
|
||||
</VirtualHost>
|
||||
{{< /highlight >}}
|
||||
|
||||
**Note:** The apache modules `proxy`, `proxy_http` and `rewrite` must be enabled for this.
|
||||
|
||||
For more details see the [frontend apache configuration]({{< ref "install-frontend.md#apache">}}).
|
||||
|
||||
## Caddy
|
||||
|
||||
{{< highlight conf >}}
|
||||
vikunja.domainname.tld {
|
||||
@paths {
|
||||
path /api/* /.well-known/* /dav/*
|
||||
}
|
||||
handle @paths {
|
||||
reverse_proxy 127.0.0.1:3456
|
||||
}
|
||||
|
||||
handle {
|
||||
encode zstd gzip
|
||||
root * /var/www/html/vikunja
|
||||
try_files {path} index.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "Typesense"
|
||||
date: 2023-09-29T12:23:55+02:00
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Use Typesense for enhanced search capabilities
|
||||
|
||||
Vikunja supports using [Typesense](https://typesense.org/) for a better search experience.
|
||||
Typesense allows fast fulltext search including fuzzy matching support.
|
||||
It may return different results than what you'd get with a database-only search, but generally, the results are more relevant to what you're looking for.
|
||||
|
||||
This document explains how to set up and use Typesense with Vikunja.
|
||||
|
||||
## Setup
|
||||
|
||||
1. First, install Typesense on your system. Refer to [their documentation](https://typesense.org/docs/guide/install-typesense.html) for specific instructions.
|
||||
2. Once Typesense is available on your system and reachable by Vikunja, add the relevant configuration keys to your Vikunja config. [Check out the docs article about this]({{< ref "config.md#typesense">}}).
|
||||
3. Index all tasks currently in Vikunja. To do that, run the `vikunja index` command with the api binary. This may take a while, depending on the size of your instance.
|
||||
4. Restart the api. From now on, all task changes will be automatically indexed in Typesense.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "n8n"
|
||||
date: 2023-10-24T19:31:35+02:00
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Using Vikunja with n8n
|
||||
|
||||
Vikunja maintains a [community node](https://github.com/go-vikunja/n8n-vikunja-nodes) for [n8n](https://n8n.io),
|
||||
allowing you to easily integrate Vikunja with all kinds of other tools and services.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Installation
|
||||
|
||||
To install the node in your n8n installation:
|
||||
|
||||
1. In your n8n instance, go to **Settings > Community Nodes**.
|
||||
2. Select Install.
|
||||
3. Enter `n8n-nodes-vikunja` as the npm Package Name
|
||||
4. Agree to the risks of using community nodes: select I understand the risks of installing unverified code from a
|
||||
public source.
|
||||
5. Select Install. n8n installs the node, and returns to the Community Nodes list in Settings.
|
||||
6. Vikunja actions and triggers are now available in n8n.
|
||||
|
||||
[Official n8n docs about the installation](https://docs.n8n.io/integrations/community-nodes/installation/)
|
||||
|
||||
## Authentication
|
||||
|
||||
To authenticate your automation against Vikunja:
|
||||
|
||||
1. In Vikunja, go to **Settings > API Tokens** and create a new token. Use all scopes for the kind of task you want to
|
||||
do. \
|
||||
*Note:* If you want to use the webhook trigger node, the api token should have permissions to create, read and delete
|
||||
webhooks.
|
||||
2. Now in n8n, go to **Credentials** and then click on **Add Credential**.
|
||||
3. Search for `Vikunja API` and click *Continue*
|
||||
4. Enter the API key you created in step 1.
|
||||
5. Enter the API URL of your Vikunja instance, with `/api/v1` suffix.
|
||||
6. When you now create a Vikunja node, select the created credentials.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Webhooks"
|
||||
date: 2023-10-17T19:51:32+02:00
|
||||
draft: false
|
||||
type: doc
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Webhooks
|
||||
|
||||
Starting with version 0.22.0, Vikunja allows you to define webhooks to notify other services of events happening within Vikunja.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## How to create webhooks
|
||||
|
||||
To create a webhook, in the project options select "Webhooks". The form will allow you to create and modify webhooks.
|
||||
|
||||
Check out [the api docs](https://try.vikunja.io/api/v1/docs#tag/webhooks) for information about how to create webhooks programatically.
|
||||
|
||||
## Available events and their payload
|
||||
|
||||
All events registered as webhook events in [the event listeners definition](https://kolaente.dev/vikunja/api/src/branch/main/pkg/models/listeners.go#L69) can be used as webhook target.
|
||||
|
||||
A webhook payload will look similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_name": "task.created",
|
||||
"time": "2023-10-17T19:39:32.924194436+02:00",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
The `data` property will contain the raw event data as it was registered in the `listeners.go` file.
|
||||
|
||||
The `time` property holds the time when the webhook payload data was sent.
|
||||
It always uses the ISO 8601 format with date, time and time zone offset.
|
||||
|
||||
## Security considerations
|
||||
|
||||
### Signing
|
||||
|
||||
Vikunja allows you to provide a secret when creating the webhook.
|
||||
If you set a secret, all outgoing webhook requests will contain an `X-Vikunja-Signature` header with an HMAC signature over the webhook json payload.
|
||||
|
||||
Check out [webhooks.fyi](https://webhooks.fyi/security/hmac) for more information about how to validate the HMAC signature.
|
||||
|
||||
### Hosting webhook infrastructure
|
||||
|
||||
Vikunja has support to use [mole](https://github.com/frain-dev/mole) as a proxy for outgoing webhook requests.
|
||||
This allows you to prevent SSRF attacts on your own infrastructure.
|
||||
|
||||
You should use this and [configure it appropriately]({{< ref "../setup/config.md">}}#webhooks) if you're not the only one using your Vikunja instance.
|
||||
|
||||
Check out [webhooks.fyi](https://webhooks.fyi/best-practices/webhook-providers#implement-security-on-egress-communication) for more information about the attack vector and reasoning to prevent this.
|
||||
@@ -19,63 +19,65 @@ module code.vikunja.io/api
|
||||
require (
|
||||
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3
|
||||
dario.cat/mergo v1.0.0
|
||||
github.com/ThreeDotsLabs/watermill v1.2.0
|
||||
github.com/ThreeDotsLabs/watermill v1.3.5
|
||||
github.com/adlio/trello v1.10.0
|
||||
github.com/arran4/golang-ical v0.0.0-20230425234049-f69e132f2b0c
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
||||
github.com/arran4/golang-ical v0.2.2
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/bbrks/go-blurhash v1.1.1
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
|
||||
github.com/coreos/go-oidc/v3 v3.6.0
|
||||
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
github.com/cweill/gotests v1.6.0
|
||||
github.com/d4l3k/messagediff v1.2.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20230626224747-e794b9370d49
|
||||
github.com/gabriel-vasile/mimetype v1.4.2
|
||||
github.com/getsentry/sentry-go v0.22.0
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/getsentry/sentry-go v0.25.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0
|
||||
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/jinzhu/copier v0.3.5
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6
|
||||
github.com/labstack/echo-jwt/v4 v4.2.0
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/labstack/gommon v0.4.0
|
||||
github.com/labstack/echo/v4 v4.11.3
|
||||
github.com/labstack/gommon v0.4.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/magefile/mage v1.15.0
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/mattn/go-sqlite3 v1.14.19
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/redis/go-redis/v9 v9.3.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/swaggo/swag v1.8.12
|
||||
github.com/swaggo/swag v1.16.2
|
||||
github.com/tkuchiki/go-timezone v0.2.2
|
||||
github.com/typesense/typesense-go v0.9.0
|
||||
github.com/ulule/limiter/v3 v3.11.2
|
||||
github.com/wneessen/go-mail v0.3.9
|
||||
github.com/yuin/goldmark v1.5.4
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/image v0.9.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.10.0
|
||||
golang.org/x/term v0.10.0
|
||||
github.com/wneessen/go-mail v0.4.0
|
||||
github.com/yuin/goldmark v1.6.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/image v0.14.0
|
||||
golang.org/x/oauth2 v0.15.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.org/x/text v0.14.0
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
src.techknowlogick.com/xgo v1.7.1-0.20230606181629-494bc06f804a
|
||||
src.techknowlogick.com/xormigrate v1.5.0
|
||||
xorm.io/builder v0.3.12
|
||||
xorm.io/xorm v1.3.2
|
||||
src.techknowlogick.com/xgo v1.7.1-0.20231205202227-c7ed78300ce9
|
||||
src.techknowlogick.com/xormigrate v1.7.1
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.4
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -85,25 +87,34 @@ require (
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/bytedance/sonic v1.10.0 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deepmap/oapi-codegen v1.13.4 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.10 // indirect
|
||||
github.com/go-faster/city v1.0.1 // indirect
|
||||
github.com/go-faster/errors v0.6.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.3 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/go-openapi/swag v0.22.5 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.1 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
@@ -113,14 +124,16 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -129,38 +142,49 @@ require (
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/onsi/gomega v1.16.0 // indirect
|
||||
github.com/paulmach/orb v0.9.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.11.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/urfave/cli/v2 v2.3.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 // indirect
|
||||
go.opentelemetry.io/otel v1.15.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.15.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190610114120-2a4eb8b5dcc9+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.2
|
||||
|
||||
+3
-16
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -177,9 +177,6 @@ func init() {
|
||||
// Some variables have external dependencies (like git) which may not always be available.
|
||||
func initVars() {
|
||||
Tags = os.Getenv("TAGS")
|
||||
if !strings.Contains(Tags, "swagger") {
|
||||
Tags += " swagger"
|
||||
}
|
||||
setVersion()
|
||||
setBinLocation()
|
||||
setPkgVersion()
|
||||
@@ -349,10 +346,6 @@ const swaggerDocsFolderLocation = `./pkg/swagger/`
|
||||
// Generates the swagger docs from the code annotations
|
||||
func DoTheSwag() {
|
||||
mg.Deps(initVars)
|
||||
if _, err := os.Stat(swaggerDocsFolderLocation + "swagger.json"); err == nil {
|
||||
fmt.Println("Swagger docs already generated, not generating. Remove the files in " + swaggerDocsFolderLocation + " and run this command again to regenerate them.")
|
||||
return
|
||||
}
|
||||
|
||||
checkAndInstallGoTool("swag", "github.com/swaggo/swag/cmd/swag")
|
||||
runAndStreamOutput("swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", RootPath, "-o", RootPath+"/pkg/swagger")
|
||||
@@ -419,7 +412,7 @@ func checkGolangCiLintInstalled() {
|
||||
mg.Deps(initVars)
|
||||
if err := exec.Command("golangci-lint").Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
|
||||
fmt.Println("Please manually install golangci-lint by running")
|
||||
fmt.Println("curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.2")
|
||||
fmt.Println("curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -460,16 +453,12 @@ func (Build) Clean() error {
|
||||
if err := os.RemoveAll(BinLocation); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(swaggerDocsFolderLocation); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Builds a vikunja binary, ready to run
|
||||
func (Build) Build() {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(DoTheSwag)
|
||||
runAndStreamOutput("go", "build", Goflags[0], "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
|
||||
}
|
||||
|
||||
@@ -479,7 +468,6 @@ type Release mg.Namespace
|
||||
func (Release) Release(ctx context.Context) error {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(Release.Dirs)
|
||||
mg.Deps(DoTheSwag)
|
||||
|
||||
// Run compiling in parallel to speed it up
|
||||
errs, _ := errgroup.WithContext(ctx)
|
||||
@@ -521,7 +509,6 @@ func (Release) Dirs() error {
|
||||
|
||||
func runXgo(targets string) error {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(DoTheSwag)
|
||||
checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo")
|
||||
|
||||
extraLdflags := `-linkmode external -extldflags "-static" `
|
||||
@@ -770,7 +757,7 @@ func (Dev) MakeMigration() error {
|
||||
date := time.Now().Format("20060102150405")
|
||||
|
||||
migration := `// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+63
-21
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -38,21 +38,21 @@ type Todo struct {
|
||||
UID string
|
||||
|
||||
// Optional
|
||||
Summary string
|
||||
Description string
|
||||
Completed time.Time
|
||||
Organizer *user.User
|
||||
Priority int64 // 0-9, 1 is highest
|
||||
RelatedToUID string
|
||||
Color string
|
||||
Categories []string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
DueDate time.Time
|
||||
Duration time.Duration
|
||||
RepeatAfter int64
|
||||
RepeatMode models.TaskRepeatMode
|
||||
Alarms []Alarm
|
||||
Summary string
|
||||
Description string
|
||||
Completed time.Time
|
||||
Organizer *user.User
|
||||
Priority int64 // 0-9, 1 is highest
|
||||
Relations []Relation
|
||||
Color string
|
||||
Categories []string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
DueDate time.Time
|
||||
Duration time.Duration
|
||||
RepeatAfter int64
|
||||
RepeatMode models.TaskRepeatMode
|
||||
Alarms []Alarm
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time // last-mod
|
||||
@@ -66,6 +66,11 @@ type Alarm struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
type Relation struct {
|
||||
Type models.RelationKind
|
||||
UID string
|
||||
}
|
||||
|
||||
// Config is the caldav calendar config
|
||||
type Config struct {
|
||||
Name string
|
||||
@@ -147,11 +152,6 @@ STATUS:COMPLETED`
|
||||
ORGANIZER;CN=:` + t.Organizer.Username
|
||||
}
|
||||
|
||||
if t.RelatedToUID != "" {
|
||||
caldavtodos += `
|
||||
RELATED-TO:` + t.RelatedToUID
|
||||
}
|
||||
|
||||
if t.DueDate.Unix() > 0 {
|
||||
caldavtodos += `
|
||||
DUE:` + makeCalDavTimeFromTimeStamp(t.DueDate)
|
||||
@@ -185,6 +185,7 @@ CATEGORIES:` + strings.Join(t.Categories, ",")
|
||||
caldavtodos += `
|
||||
LAST-MODIFIED:` + makeCalDavTimeFromTimeStamp(t.Updated)
|
||||
caldavtodos += ParseAlarms(t.Alarms, t.Summary)
|
||||
caldavtodos += ParseRelations(t.Relations)
|
||||
caldavtodos += `
|
||||
END:VTODO`
|
||||
}
|
||||
@@ -222,6 +223,47 @@ END:VALARM`
|
||||
return caldavalarms
|
||||
}
|
||||
|
||||
func ParseRelations(relations []Relation) (caldavrelatedtos string) {
|
||||
|
||||
for _, r := range relations {
|
||||
switch r.Type {
|
||||
case models.RelationKindParenttask:
|
||||
caldavrelatedtos += `
|
||||
RELATED-TO;RELTYPE=PARENT:`
|
||||
case models.RelationKindSubtask:
|
||||
caldavrelatedtos += `
|
||||
RELATED-TO;RELTYPE=CHILD:`
|
||||
case models.RelationKindUnknown:
|
||||
continue
|
||||
case models.RelationKindRelated:
|
||||
continue
|
||||
case models.RelationKindDuplicateOf:
|
||||
continue
|
||||
case models.RelationKindDuplicates:
|
||||
continue
|
||||
case models.RelationKindBlocking:
|
||||
continue
|
||||
case models.RelationKindBlocked:
|
||||
continue
|
||||
case models.RelationKindPreceeds:
|
||||
continue
|
||||
case models.RelationKindFollows:
|
||||
continue
|
||||
case models.RelationKindCopiedFrom:
|
||||
continue
|
||||
case models.RelationKindCopiedTo:
|
||||
continue
|
||||
default:
|
||||
caldavrelatedtos += `
|
||||
RELATED-TO:`
|
||||
}
|
||||
|
||||
caldavrelatedtos += r.UID
|
||||
}
|
||||
|
||||
return caldavrelatedtos
|
||||
}
|
||||
|
||||
func makeCalDavTimeFromTimeStamp(ts time.Time) (caldavtime string) {
|
||||
return ts.In(time.UTC).Format(DateFormat) + "Z"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -326,6 +326,49 @@ ACTION:DISPLAY
|
||||
DESCRIPTION:Todo #1
|
||||
END:VALARM
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
{
|
||||
name: "with related-to",
|
||||
args: args{
|
||||
config: &Config{
|
||||
Name: "test",
|
||||
ProdID: "RandomProdID which is not random",
|
||||
},
|
||||
todos: []*Todo{
|
||||
{
|
||||
Summary: "Todo #1",
|
||||
Description: "Lorem Ipsum",
|
||||
UID: "randommduid",
|
||||
Relations: []Relation{
|
||||
{
|
||||
Type: models.RelationKindParenttask,
|
||||
UID: "parentuid",
|
||||
},
|
||||
{
|
||||
Type: models.RelationKindSubtask,
|
||||
UID: "subtaskuid",
|
||||
},
|
||||
},
|
||||
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCaldavtasks: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randommduid
|
||||
DTSTAMP:20181201T011204Z
|
||||
SUMMARY:Todo #1
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
LAST-MODIFIED:00010101T000000Z
|
||||
RELATED-TO;RELTYPE=PARENT:parentuid
|
||||
RELATED-TO;RELTYPE=CHILD:subtaskuid
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
}
|
||||
|
||||
+45
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -50,6 +50,16 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||
})
|
||||
}
|
||||
|
||||
var relations []Relation
|
||||
for reltype, tasks := range t.RelatedTasks {
|
||||
for _, r := range tasks {
|
||||
relations = append(relations, Relation{
|
||||
Type: reltype,
|
||||
UID: r.UID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
caldavtodos = append(caldavtodos, &Todo{
|
||||
Timestamp: t.Updated,
|
||||
UID: t.UID,
|
||||
@@ -68,6 +78,7 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||
RepeatAfter: t.RepeatAfter,
|
||||
RepeatMode: t.RepeatMode,
|
||||
Alarms: alarms,
|
||||
Relations: relations,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,8 +101,12 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
}
|
||||
// We put the vTodo details in a map to be able to handle them more easily
|
||||
task := make(map[string]ics.IANAProperty)
|
||||
var relations []ics.IANAProperty
|
||||
for _, c := range vTodo.UnknownPropertiesIANAProperties() {
|
||||
task[c.IANAToken] = c
|
||||
if strings.HasPrefix(c.IANAToken, "RELATED-TO") {
|
||||
relations = append(relations, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the priority
|
||||
@@ -134,6 +149,35 @@ func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
DoneAt: caldavTimeToTimestamp(task["COMPLETED"]),
|
||||
}
|
||||
|
||||
for _, c := range relations {
|
||||
var relTypeStr string
|
||||
if _, ok := c.ICalParameters["RELTYPE"]; ok {
|
||||
if len(c.ICalParameters["RELTYPE"]) != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
relTypeStr = c.ICalParameters["RELTYPE"][0]
|
||||
}
|
||||
|
||||
var relationKind models.RelationKind
|
||||
switch relTypeStr {
|
||||
case "PARENT":
|
||||
relationKind = models.RelationKindParenttask
|
||||
case "CHILD":
|
||||
relationKind = models.RelationKindSubtask
|
||||
default:
|
||||
relationKind = models.RelationKindParenttask
|
||||
}
|
||||
|
||||
if vTask.RelatedTasks == nil {
|
||||
vTask.RelatedTasks = make(map[models.RelationKind][]*models.Task)
|
||||
}
|
||||
|
||||
vTask.RelatedTasks[relationKind] = append(vTask.RelatedTasks[relationKind], &models.Task{
|
||||
UID: c.Value,
|
||||
})
|
||||
}
|
||||
|
||||
if task["STATUS"].Value == "COMPLETED" {
|
||||
vTask.Done = true
|
||||
}
|
||||
|
||||
+183
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -219,6 +219,70 @@ END:VCALENDAR`,
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With parent",
|
||||
args: args{content: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randomuid
|
||||
DTSTAMP:20181201T011204
|
||||
SUMMARY:SubTask #1
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
LAST-MODIFIED:00010101T000000
|
||||
RELATED-TO;RELTYPE=PARENT:randomuid_parent
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
wantVTask: &models.Task{
|
||||
Title: "SubTask #1",
|
||||
UID: "randomuid",
|
||||
Description: "Lorem Ipsum",
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindParenttask: {
|
||||
{
|
||||
UID: "randomuid_parent",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With subtask",
|
||||
args: args{content: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randomuid
|
||||
DTSTAMP:20181201T011204
|
||||
SUMMARY:Parent
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
LAST-MODIFIED:00010101T000000
|
||||
RELATED-TO;RELTYPE=CHILD:randomuid_child
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
wantVTask: &models.Task{
|
||||
Title: "Parent",
|
||||
UID: "randomuid",
|
||||
Description: "Lorem Ipsum",
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindSubtask: {
|
||||
{
|
||||
UID: "randomuid_child",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "example task from tasks.org app",
|
||||
args: args{content: `BEGIN:VCALENDAR
|
||||
@@ -392,6 +456,124 @@ ACTION:DISPLAY
|
||||
DESCRIPTION:Task 1
|
||||
END:VALARM
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
{
|
||||
name: "Format Task with Related Tasks as CalDAV",
|
||||
args: args{
|
||||
list: &models.ProjectWithTasksAndBuckets{
|
||||
Project: models.Project{
|
||||
Title: "List title",
|
||||
},
|
||||
},
|
||||
tasks: []*models.TaskWithComments{
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Parent Task",
|
||||
UID: "randomuid_parent",
|
||||
Description: "A parent task",
|
||||
Priority: 3,
|
||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindSubtask: {
|
||||
{
|
||||
Title: "Subtask 1",
|
||||
UID: "randomuid_child_1",
|
||||
Description: "The first child task",
|
||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
{
|
||||
Title: "Subtask 2",
|
||||
UID: "randomuid_child_2",
|
||||
Description: "The second child task",
|
||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Subtask 1",
|
||||
UID: "randomuid_child_1",
|
||||
Description: "The first child task",
|
||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindParenttask: {
|
||||
{
|
||||
Title: "Parent task",
|
||||
UID: "randomuid_parent",
|
||||
Description: "A parent task",
|
||||
Priority: 3,
|
||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Task: models.Task{
|
||||
Title: "Subtask 2",
|
||||
UID: "randomuid_child_2",
|
||||
Description: "The second child task",
|
||||
Created: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindParenttask: {
|
||||
{
|
||||
Title: "Parent task",
|
||||
UID: "randomuid_parent",
|
||||
Description: "A parent task",
|
||||
Priority: 3,
|
||||
Created: time.Unix(1543626721, 0).In(config.GetTimeZone()),
|
||||
Updated: time.Unix(1543626725, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCaldav: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:List title
|
||||
PRODID:-//Vikunja Todo App//EN
|
||||
BEGIN:VTODO
|
||||
UID:randomuid_parent
|
||||
DTSTAMP:20181201T011205Z
|
||||
SUMMARY:Parent Task
|
||||
DESCRIPTION:A parent task
|
||||
CREATED:20181201T011201Z
|
||||
PRIORITY:3
|
||||
LAST-MODIFIED:20181201T011205Z
|
||||
RELATED-TO;RELTYPE=CHILD:randomuid_child_1
|
||||
RELATED-TO;RELTYPE=CHILD:randomuid_child_2
|
||||
END:VTODO
|
||||
BEGIN:VTODO
|
||||
UID:randomuid_child_1
|
||||
DTSTAMP:20181201T011204Z
|
||||
SUMMARY:Subtask 1
|
||||
DESCRIPTION:The first child task
|
||||
CREATED:20181201T011204Z
|
||||
LAST-MODIFIED:20181201T011204Z
|
||||
RELATED-TO;RELTYPE=PARENT:randomuid_parent
|
||||
END:VTODO
|
||||
BEGIN:VTODO
|
||||
UID:randomuid_child_2
|
||||
DTSTAMP:20181201T011204Z
|
||||
SUMMARY:Subtask 2
|
||||
DESCRIPTION:The second child task
|
||||
CREATED:20181201T011204Z
|
||||
LAST-MODIFIED:20181201T011204Z
|
||||
RELATED-TO;RELTYPE=PARENT:randomuid_parent
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -33,7 +33,7 @@ var dumpCmd = &cobra.Command{
|
||||
Use: "dump",
|
||||
Short: "Dump all vikunja data into a zip file. Includes config, files and db.",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
initialize.FullInit()
|
||||
initialize.FullInitWithoutAsync()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
filename := "vikunja-dump_" + time.Now().Format("2006-01-02_15-03-05") + ".zip"
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/initialize"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(indexCmd)
|
||||
}
|
||||
|
||||
var indexCmd = &cobra.Command{
|
||||
Use: "index",
|
||||
Short: "Reindex all of Vikunja's data into Typesense. This will remove any existing index.",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
initialize.FullInitWithoutAsync()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !config.TypesenseEnabled.GetBool() {
|
||||
log.Error("Typesense not enabled")
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Indexing… This may take a while.")
|
||||
|
||||
err := models.CreateTypesenseCollections()
|
||||
if err != nil {
|
||||
log.Criticalf("Could not create Typesense collections: %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = models.ReindexAllTasks()
|
||||
if err != nil {
|
||||
log.Criticalf("Could not reindex all tasks into Typesense: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Done!")
|
||||
},
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -32,7 +32,7 @@ var restoreCmd = &cobra.Command{
|
||||
Short: "Restores all vikunja data from a vikunja dump.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
initialize.FullInit()
|
||||
initialize.FullInitWithoutAsync()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := dump.Restore(args[0]); err != nil {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+12
-4
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -46,6 +46,7 @@ var (
|
||||
userFlagEnableUser bool
|
||||
userFlagDisableUser bool
|
||||
userFlagDeleteNow bool
|
||||
userFlagDeleteConfirm bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -73,6 +74,9 @@ func init() {
|
||||
// User deletion flags
|
||||
userDeleteCmd.Flags().BoolVarP(&userFlagDeleteNow, "now", "n", false, "If provided, deletes the user immediately instead of sending them an email first.")
|
||||
|
||||
// Bypass confirm prompt
|
||||
userDeleteCmd.Flags().BoolVarP(&userFlagDeleteConfirm, "confirm", "c", false, "Bypasses any prompts confirming the deletion request, use with caution!")
|
||||
|
||||
userCmd.AddCommand(userListCmd, userCreateCmd, userUpdateCmd, userResetPasswordCmd, userChangeEnabledCmd, userDeleteCmd)
|
||||
rootCmd.AddCommand(userCmd)
|
||||
}
|
||||
@@ -100,12 +104,16 @@ func getPasswordFromFlagOrInput() (pw string) {
|
||||
}
|
||||
|
||||
func getUserFromArg(s *xorm.Session, arg string) *user.User {
|
||||
filter := user.User{}
|
||||
id, err := strconv.ParseInt(arg, 10, 64)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid user id: %s", err)
|
||||
log.Infof("Invalid user ID [%s], assuming username instead", arg)
|
||||
filter.Username = arg
|
||||
} else {
|
||||
filter.ID = id
|
||||
}
|
||||
|
||||
u, err := user.GetUserWithEmail(s, &user.User{ID: id})
|
||||
u, err := user.GetUserWithEmail(s, &filter)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get user: %s", err)
|
||||
}
|
||||
@@ -322,7 +330,7 @@ var userDeleteCmd = &cobra.Command{
|
||||
initialize.FullInit()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if userFlagDeleteNow {
|
||||
if userFlagDeleteNow && !userFlagDeleteConfirm {
|
||||
fmt.Println("You requested to delete the user immediately. Are you sure?")
|
||||
fmt.Println(`To confirm, please type "yes, I confirm" in all uppercase:`)
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+39
-13
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -19,7 +19,6 @@ package config
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -29,6 +28,7 @@ import (
|
||||
"time"
|
||||
_ "time/tzdata" // Imports time zone data instead of relying on the os
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
@@ -49,6 +49,7 @@ const (
|
||||
ServiceRootpath Key = `service.rootpath`
|
||||
ServiceStaticpath Key = `service.staticpath`
|
||||
ServiceMaxItemsPerPage Key = `service.maxitemsperpage`
|
||||
ServiceDemoMode Key = `service.demomode`
|
||||
// Deprecated: Use metrics.enabled
|
||||
ServiceEnableMetrics Key = `service.enablemetrics`
|
||||
ServiceMotd Key = `service.motd`
|
||||
@@ -87,6 +88,10 @@ const (
|
||||
DatabaseSslRootCert Key = `database.sslrootcert`
|
||||
DatabaseTLS Key = `database.tls`
|
||||
|
||||
TypesenseEnabled Key = `typesense.enabled`
|
||||
TypesenseURL Key = `typesense.url`
|
||||
TypesenseAPIKey Key = `typesense.apikey`
|
||||
|
||||
MailerEnabled Key = `mailer.enabled`
|
||||
MailerHost Key = `mailer.host`
|
||||
MailerPort Key = `mailer.port`
|
||||
@@ -117,11 +122,12 @@ const (
|
||||
LogMail Key = `log.mail`
|
||||
LogMailLevel Key = `log.maillevel`
|
||||
|
||||
RateLimitEnabled Key = `ratelimit.enabled`
|
||||
RateLimitKind Key = `ratelimit.kind`
|
||||
RateLimitPeriod Key = `ratelimit.period`
|
||||
RateLimitLimit Key = `ratelimit.limit`
|
||||
RateLimitStore Key = `ratelimit.store`
|
||||
RateLimitEnabled Key = `ratelimit.enabled`
|
||||
RateLimitKind Key = `ratelimit.kind`
|
||||
RateLimitPeriod Key = `ratelimit.period`
|
||||
RateLimitLimit Key = `ratelimit.limit`
|
||||
RateLimitStore Key = `ratelimit.store`
|
||||
RateLimitNoAuthRoutesLimit Key = `ratelimit.noauthlimit`
|
||||
|
||||
FilesBasePath Key = `files.basepath`
|
||||
FilesMaxSize Key = `files.maxsize`
|
||||
@@ -167,6 +173,11 @@ const (
|
||||
DefaultSettingsLanguage Key = `defaultsettings.language`
|
||||
DefaultSettingsTimezone Key = `defaultsettings.timezone`
|
||||
DefaultSettingsOverdueTaskRemindersTime Key = `defaultsettings.overdue_tasks_reminders_time`
|
||||
|
||||
WebhooksEnabled Key = `webhooks.enabled`
|
||||
WebhooksTimeoutSeconds Key = `webhooks.timeoutseconds`
|
||||
WebhooksProxyURL Key = `webhooks.proxyurl`
|
||||
WebhooksProxyPassword Key = `webhooks.proxypassword`
|
||||
)
|
||||
|
||||
// GetString returns a string config value
|
||||
@@ -296,6 +307,7 @@ func InitDefaultConfig() {
|
||||
ServiceEnableEmailReminders.setDefault(true)
|
||||
ServiceEnableUserDeletion.setDefault(true)
|
||||
ServiceMaxAvatarSize.setDefault(1024)
|
||||
ServiceDemoMode.setDefault(false)
|
||||
|
||||
// Auth
|
||||
AuthLocalEnabled.setDefault(true)
|
||||
@@ -317,6 +329,9 @@ func InitDefaultConfig() {
|
||||
DatabaseSslRootCert.setDefault("")
|
||||
DatabaseTLS.setDefault("false")
|
||||
|
||||
// Typesense
|
||||
TypesenseEnabled.setDefault(false)
|
||||
|
||||
// Mailer
|
||||
MailerEnabled.setDefault(false)
|
||||
MailerHost.setDefault("")
|
||||
@@ -353,6 +368,7 @@ func InitDefaultConfig() {
|
||||
RateLimitLimit.setDefault(100)
|
||||
RateLimitPeriod.setDefault(60)
|
||||
RateLimitStore.setDefault("memory")
|
||||
RateLimitNoAuthRoutesLimit.setDefault(10)
|
||||
// Files
|
||||
FilesBasePath.setDefault("files")
|
||||
FilesMaxSize.setDefault("20MB")
|
||||
@@ -378,6 +394,9 @@ func InitDefaultConfig() {
|
||||
DefaultSettingsAvatarProvider.setDefault("initials")
|
||||
DefaultSettingsOverdueTaskRemindersEnabled.setDefault(true)
|
||||
DefaultSettingsOverdueTaskRemindersTime.setDefault("9:00")
|
||||
// Webhook
|
||||
WebhooksEnabled.setDefault(true)
|
||||
WebhooksTimeoutSeconds.setDefault(30)
|
||||
}
|
||||
|
||||
// InitConfig initializes the config, sets defaults etc.
|
||||
@@ -391,13 +410,17 @@ func InitConfig() {
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// Just load environment variables
|
||||
_ = viper.ReadInConfig()
|
||||
log.ConfigLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString())
|
||||
|
||||
// Load the config file
|
||||
viper.AddConfigPath(ServiceRootpath.GetString())
|
||||
viper.AddConfigPath("/etc/vikunja/")
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Printf("No home directory found, not using config from ~/.config/vikunja/. Error was: %s\n", err.Error())
|
||||
log.Debugf("No home directory found, not using config from ~/.config/vikunja/. Error was: %s\n", err.Error())
|
||||
} else {
|
||||
viper.AddConfigPath(path.Join(homeDir, ".config", "vikunja"))
|
||||
}
|
||||
@@ -406,15 +429,18 @@ func InitConfig() {
|
||||
viper.SetConfigName("config")
|
||||
|
||||
err = viper.ReadInConfig()
|
||||
|
||||
if viper.ConfigFileUsed() != "" {
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
log.Infof("Using config file: %s", viper.ConfigFileUsed())
|
||||
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
log.Println("Using default config.")
|
||||
log.Warning(err.Error())
|
||||
log.Warning("Using default config.")
|
||||
} else {
|
||||
log.ConfigLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString())
|
||||
}
|
||||
} else {
|
||||
log.Println("No config file found, using default or config from environment variables.")
|
||||
log.Info("No config file found, using default or config from environment variables.")
|
||||
}
|
||||
|
||||
if RateLimitStore.GetString() == "keyvalue" {
|
||||
@@ -446,7 +472,7 @@ func InitConfig() {
|
||||
}
|
||||
|
||||
if ServiceEnableMetrics.GetBool() {
|
||||
log.Println("WARNING: service.enablemetrics is deprecated and will be removed in a future release. Please use metrics.enable.")
|
||||
log.Warning("service.enablemetrics is deprecated and will be removed in a future release. Please use metrics.enable.")
|
||||
MetricsEnabled.Set(true)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -79,7 +79,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
||||
}
|
||||
engine.SetTZDatabase(loc)
|
||||
engine.SetMapper(names.GonicMapper{})
|
||||
logger := log.NewXormLogger("")
|
||||
logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), config.LogDatabaseLevel.GetString())
|
||||
engine.SetLogger(logger)
|
||||
|
||||
x = engine
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
- id: 1
|
||||
title: 'test token 1'
|
||||
token_salt: iC1Qbpf7H1
|
||||
token_hash: a1813a558185d99f5197d2d549e4dd91292376aa00210229d70f77b57e165f6613fd12c1f790aa6493548cb9bceff33b45b4
|
||||
token_last_eight: 75f29d2e
|
||||
permissions: '{"tasks":["read_all","update"]}'
|
||||
expires_at: 2099-01-01 00:00:00
|
||||
owner_id: 1
|
||||
created: 2023-09-01 07:00:00
|
||||
# token in plaintext is tk_2eef46f40ebab3304919ab2e7e39993f75f29d2e
|
||||
- id: 2
|
||||
title: 'test token 2'
|
||||
token_salt: EtwMsqDfOA
|
||||
token_hash: 5c4d80c58947f21295064d473937709f1159ab09085eb59e38783da6032181069ec2e1d236486533b66999f9f4ac375b45f5
|
||||
token_last_eight: 235008c8
|
||||
permissions: '{"tasks":["read_all","update"]}'
|
||||
expires_at: 2023-01-01 00:00:00
|
||||
owner_id: 1
|
||||
created: 2023-09-01 07:00:00
|
||||
# token in plaintext is tk_a5e6f92ddbad68f49ee2c63e52174db0235008c8
|
||||
- id: 3
|
||||
title: 'test token 3'
|
||||
token_salt: AHeetyp1aB
|
||||
token_hash: da4b9c3aa72633274c37ab3419fbfbe4c5b79310b76027ac36f85e4c5ad0c2342a1d9e1c9b72ca07ec0a66ad2ee3505539af
|
||||
token_last_eight: 0b8dcb7c
|
||||
permissions: '{"tasks":["read_all","update"]}'
|
||||
expires_at: 2099-01-01 00:00:00
|
||||
owner_id: 2
|
||||
created: 2023-09-01 07:00:00
|
||||
# token in plaintext is tk_5e29ae2ae079781ff73b0a3e0fe4d75a0b8dcb7c
|
||||
@@ -18,7 +18,6 @@
|
||||
title: testbucket3
|
||||
project_id: 1
|
||||
created_by_id: 1
|
||||
is_done_bucket: 1
|
||||
position: 3
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
@@ -26,7 +25,6 @@
|
||||
title: testbucket4 - other project
|
||||
project_id: 2
|
||||
created_by_id: 1
|
||||
is_done_bucket: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
# The following are not or only partly owned by user 1
|
||||
@@ -40,6 +38,7 @@
|
||||
title: testbucket6
|
||||
project_id: 6
|
||||
created_by_id: 1
|
||||
position: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 7
|
||||
@@ -137,6 +136,7 @@
|
||||
title: testbucket22
|
||||
project_id: 6
|
||||
created_by_id: 1
|
||||
position: 2
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 23
|
||||
@@ -236,3 +236,9 @@
|
||||
created_by_id: 15
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 39
|
||||
title: testbucket38
|
||||
project_id: 38
|
||||
created_by_id: 15
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
@@ -5,6 +5,7 @@
|
||||
identifier: test1
|
||||
owner_id: 1
|
||||
position: 3
|
||||
done_bucket_id: 3
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
@@ -14,6 +15,7 @@
|
||||
identifier: test2
|
||||
owner_id: 3
|
||||
position: 2
|
||||
done_bucket_id: 4
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
@@ -50,6 +52,7 @@
|
||||
identifier: test6
|
||||
owner_id: 6
|
||||
position: 6
|
||||
default_bucket_id: 22
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
@@ -315,3 +318,21 @@
|
||||
position: 1
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
id: 37
|
||||
title: Project 37
|
||||
description: Lorem Ipsum
|
||||
identifier: test37
|
||||
owner_id: 16
|
||||
position: 1
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
id: 38
|
||||
title: Project 38 for Caldav tests
|
||||
description: Lorem Ipsum
|
||||
identifier: test38
|
||||
owner_id: 15
|
||||
position: 2
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
@@ -34,3 +34,39 @@
|
||||
relation_kind: 'related'
|
||||
created_by_id: 1
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 7
|
||||
task_id: 41
|
||||
other_task_id: 43
|
||||
relation_kind: 'subtask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 8
|
||||
task_id: 43
|
||||
other_task_id: 41
|
||||
relation_kind: 'parenttask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 9
|
||||
task_id: 41
|
||||
other_task_id: 44
|
||||
relation_kind: 'subtask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 10
|
||||
task_id: 44
|
||||
other_task_id: 41
|
||||
relation_kind: 'parenttask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 11
|
||||
task_id: 45
|
||||
other_task_id: 46
|
||||
relation_kind: 'subtask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 12
|
||||
task_id: 46
|
||||
other_task_id: 45
|
||||
relation_kind: 'parenttask'
|
||||
created_by_id: 15
|
||||
created: 2018-12-01 15:13:12
|
||||
@@ -374,5 +374,89 @@
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 1
|
||||
bucket_id: 38
|
||||
position: 39
|
||||
- id: 41
|
||||
uid: 'uid-caldav-test-parent-task'
|
||||
title: 'Parent task for Caldav Test'
|
||||
description: 'Description Caldav Test'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 36
|
||||
index: 40
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 40
|
||||
- id: 42
|
||||
uid: 'uid-caldav-test-parent-task-2'
|
||||
title: 'Parent task for Caldav Test 2'
|
||||
description: 'Description Caldav Test 2'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 36
|
||||
index: 41
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 41
|
||||
- id: 43
|
||||
uid: 'uid-caldav-test-child-task'
|
||||
title: 'Child task for Caldav Test'
|
||||
description: 'Description Caldav Test'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 36
|
||||
index: 42
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 42
|
||||
- id: 44
|
||||
uid: 'uid-caldav-test-child-task-2'
|
||||
title: 'Child task for Caldav Test '
|
||||
description: 'Description Caldav Test'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 38
|
||||
index: 43
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 43
|
||||
- id: 45
|
||||
uid: 'uid-caldav-test-parent-task-another-list'
|
||||
title: 'Parent task for Caldav Test'
|
||||
description: 'Description Caldav Test'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 36
|
||||
index: 44
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 44
|
||||
- id: 46
|
||||
uid: 'uid-caldav-test-child-task-another-list'
|
||||
title: 'Child task for Caldav Test '
|
||||
description: 'Description Caldav Test'
|
||||
priority: 3
|
||||
done: false
|
||||
created_by_id: 15
|
||||
project_id: 38
|
||||
index: 45
|
||||
due_date: 2023-03-01 15:00:00
|
||||
created: 2018-12-01 01:12:04
|
||||
updated: 2018-12-01 01:12:04
|
||||
bucket_id: 38
|
||||
position: 45
|
||||
@@ -118,3 +118,11 @@
|
||||
issuer: local
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 16
|
||||
username: 'user16'
|
||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||
email: 'user16@example.com'
|
||||
issuer: local
|
||||
default_project_id: 37
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
|
||||
@@ -100,3 +100,15 @@
|
||||
right: 0
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 18
|
||||
user_id: 15
|
||||
project_id: 36
|
||||
right: 0
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
- id: 19
|
||||
user_id: 15
|
||||
project_id: 38
|
||||
right: 0
|
||||
updated: 2018-12-02 15:13:12
|
||||
created: 2018-12-01 15:13:12
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+8
-7
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/names"
|
||||
@@ -52,7 +53,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) {
|
||||
}
|
||||
|
||||
engine.SetMapper(names.GonicMapper{})
|
||||
logger := log.NewXormLogger("DEBUG")
|
||||
logger := log.NewXormLogger(config.LogEnabled.GetBool(), config.LogDatabase.GetString(), "DEBUG")
|
||||
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
||||
engine.SetLogger(logger)
|
||||
engine.SetTZLocation(config.GetTimeZone())
|
||||
@@ -93,14 +94,14 @@ func AssertExists(t *testing.T, table string, values map[string]interface{}, cus
|
||||
} else {
|
||||
exists, err = x.Table(table).Where(values).Get(&v)
|
||||
}
|
||||
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
require.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
if !exists {
|
||||
|
||||
all := []map[string]interface{}{}
|
||||
err = x.Table(table).Find(&all)
|
||||
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
require.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
pretty, err := json.MarshalIndent(all, "", " ")
|
||||
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
require.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
|
||||
|
||||
t.Errorf(fmt.Sprintf("Entries %v do not exist in table %s\n\nFound entries instead: %v", values, table, string(pretty)))
|
||||
}
|
||||
@@ -110,13 +111,13 @@ func AssertExists(t *testing.T, table string, values map[string]interface{}, cus
|
||||
func AssertMissing(t *testing.T, table string, values map[string]interface{}) {
|
||||
v := make(map[string]interface{})
|
||||
exists, err := x.Table(table).Where(values).Exist(&v)
|
||||
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries don't exist in db, error was: %s", err))
|
||||
require.NoError(t, err, fmt.Sprintf("Failed to assert entries don't exist in db, error was: %s", err))
|
||||
assert.False(t, exists, fmt.Sprintf("Entries %v exist in table %s", values, table))
|
||||
}
|
||||
|
||||
// AssertCount checks if a number of entries exists in the database
|
||||
func AssertCount(t *testing.T, table string, where builder.Cond, count int64) {
|
||||
dbCount, err := x.Table(table).Where(where).Count()
|
||||
assert.NoError(t, err, fmt.Sprintf("Failed to assert count in db, error was: %s", err))
|
||||
require.NoError(t, err, fmt.Sprintf("Failed to assert count in db, error was: %s", err))
|
||||
assert.Equal(t, count, dbCount, fmt.Sprintf("Found %d entries instead of expected %d in table %s", dbCount, count, table))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,8 +22,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
|
||||
"github.com/go-testfixtures/testfixtures/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
@@ -108,5 +109,5 @@ func LoadFixtures() error {
|
||||
// LoadAndAssertFixtures loads all fixtures defined before and asserts they are correctly loaded
|
||||
func LoadAndAssertFixtures(t *testing.T) {
|
||||
err := LoadFixtures()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
vmetrics "code.vikunja.io/api/pkg/metrics"
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
@@ -39,7 +40,7 @@ type Event interface {
|
||||
|
||||
// InitEvents sets up everything needed to work with events
|
||||
func InitEvents() (err error) {
|
||||
logger := log.NewWatermillLogger()
|
||||
logger := log.NewWatermillLogger(config.LogEnabled.GetBool(), config.LogEvents.GetString(), config.LogEventsLevel.GetString())
|
||||
|
||||
router, err := message.NewRouter(
|
||||
message.RouterConfig{},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,9 +55,9 @@ func AssertDispatched(t *testing.T, event Event) {
|
||||
// TestListener takes an event and a listener and calls the listener's Handle method.
|
||||
func TestListener(t *testing.T, event Event, listener Listener) {
|
||||
content, err := json.Marshal(event)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg := message.NewMessage(watermill.NewUUID(), content)
|
||||
err = listener.Handle(msg)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -23,8 +23,10 @@ import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// This file handles storing and retrieving a file for different backends
|
||||
@@ -55,7 +57,7 @@ func InitTestFileFixtures(t *testing.T) {
|
||||
// Init fixture files
|
||||
filename := config.FilesBasePath.GetString() + "/1"
|
||||
err := afero.WriteFile(afs, filename, []byte("testfile1"), 0644)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// InitTests handles the actual bootstrapping of the test env
|
||||
@@ -77,6 +79,8 @@ func InitTests() {
|
||||
}
|
||||
|
||||
InitTestFileHandler()
|
||||
|
||||
keyvalue.InitStorage()
|
||||
}
|
||||
|
||||
// FileStat stats a file. This is an exported function to be able to test this from outide of the package
|
||||
|
||||
+13
-6
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -23,14 +23,16 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/metrics"
|
||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||
|
||||
"code.vikunja.io/web"
|
||||
"github.com/c2h5oh/datasize"
|
||||
"github.com/spf13/afero"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// File holds all information about a file
|
||||
@@ -148,10 +150,15 @@ func (f *File) Delete() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
return keyvalue.DecrBy(metrics.FilesCountKey, 1)
|
||||
}
|
||||
|
||||
// Save saves a file to storage
|
||||
func (f *File) Save(fcontent io.Reader) error {
|
||||
return afs.WriteReader(f.getFileName(), fcontent)
|
||||
func (f *File) Save(fcontent io.Reader) (err error) {
|
||||
err = afs.WriteReader(f.getFileName(), fcontent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return keyvalue.IncrBy(metrics.FilesCountKey, 1)
|
||||
}
|
||||
|
||||
+11
-10
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testfile struct {
|
||||
@@ -58,12 +59,12 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
ta := &testauth{id: 1}
|
||||
createdFile, err := Create(tf, "testfile", 100, ta)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check the file was created correctly
|
||||
file := &File{ID: createdFile.ID}
|
||||
err = file.LoadFileMetaByID()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), file.CreatedByID)
|
||||
assert.Equal(t, "testfile", file.Name)
|
||||
assert.Equal(t, uint64(100), file.Size)
|
||||
@@ -76,7 +77,7 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
ta := &testauth{id: 1}
|
||||
_, err := Create(tf, "testfile", 99999999999, ta)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrFileIsTooLarge(err))
|
||||
})
|
||||
}
|
||||
@@ -86,13 +87,13 @@ func TestFile_Delete(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 1}
|
||||
err := f.Delete()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 9999}
|
||||
err := f.Delete()
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrFileDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
@@ -102,13 +103,13 @@ func TestFile_LoadFileByID(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 1}
|
||||
err := f.LoadFileByID()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 9999}
|
||||
err := f.LoadFileByID()
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
})
|
||||
}
|
||||
@@ -118,14 +119,14 @@ func TestFile_LoadFileMetaByID(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 1}
|
||||
err := f.LoadFileMetaByID()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "test", f.Name)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
initFixtures(t)
|
||||
f := &File{ID: 9999}
|
||||
err := f.LoadFileMetaByID()
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrFileDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+18
-7
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -29,12 +29,16 @@ import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/auth/openid"
|
||||
"code.vikunja.io/api/pkg/modules/keyvalue"
|
||||
migrationHandler "code.vikunja.io/api/pkg/modules/migration/handler"
|
||||
"code.vikunja.io/api/pkg/red"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
)
|
||||
|
||||
// LightInit will only fullInit config, redis, logger but no db connection.
|
||||
func LightInit() {
|
||||
// Set logger
|
||||
log.InitLogger()
|
||||
|
||||
// Init the config
|
||||
config.InitConfig()
|
||||
|
||||
@@ -43,9 +47,6 @@ func LightInit() {
|
||||
|
||||
// Init keyvalue store
|
||||
keyvalue.InitStorage()
|
||||
|
||||
// Set logger
|
||||
log.InitLogger()
|
||||
}
|
||||
|
||||
// InitEngines intializes all db connections
|
||||
@@ -60,9 +61,8 @@ func InitEngines() {
|
||||
}
|
||||
}
|
||||
|
||||
// FullInit initializes all kinds of things in the right order
|
||||
func FullInit() {
|
||||
|
||||
// FullInitWithoutAsync does a full init without any async handlers (cron or events)
|
||||
func FullInitWithoutAsync() {
|
||||
LightInit()
|
||||
|
||||
// Initialize the files handler
|
||||
@@ -74,8 +74,17 @@ func FullInit() {
|
||||
// Set Engine
|
||||
InitEngines()
|
||||
|
||||
// Init Typesense
|
||||
models.InitTypesense()
|
||||
|
||||
// Start the mail daemon
|
||||
mail.StartMailDaemon()
|
||||
}
|
||||
|
||||
// FullInit initializes all kinds of things in the right order
|
||||
func FullInit() {
|
||||
|
||||
FullInitWithoutAsync()
|
||||
|
||||
// Start the cron
|
||||
cron.Init()
|
||||
@@ -86,11 +95,13 @@ func FullInit() {
|
||||
models.RegisterUserDeletionCron()
|
||||
models.RegisterOldExportCleanupCron()
|
||||
openid.CleanupSavedOpenIDProviders()
|
||||
models.RegisterPeriodicTypesenseResyncCron()
|
||||
|
||||
// Start processing events
|
||||
go func() {
|
||||
models.RegisterListeners()
|
||||
user.RegisterListeners()
|
||||
migrationHandler.RegisterListeners()
|
||||
err := events.InitEvents()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/modules/auth"
|
||||
"code.vikunja.io/api/pkg/routes"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAPIToken(t *testing.T) {
|
||||
t.Run("valid token", func(t *testing.T) {
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tasks/all", nil)
|
||||
res := httptest.NewRecorder()
|
||||
c := e.NewContext(req, res)
|
||||
h := routes.SetupTokenMiddleware()(func(c echo.Context) error {
|
||||
u, err := auth.GetAuthFromClaims(c)
|
||||
if err != nil {
|
||||
return c.String(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, u)
|
||||
})
|
||||
|
||||
req.Header.Set(echo.HeaderAuthorization, "Bearer tk_2eef46f40ebab3304919ab2e7e39993f75f29d2e") // Token 1
|
||||
require.NoError(t, h(c))
|
||||
// check if the request handlers "see" the request as if it came directly from that user
|
||||
assert.Contains(t, res.Body.String(), `"username":"user1"`)
|
||||
})
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tasks/all", nil)
|
||||
res := httptest.NewRecorder()
|
||||
c := e.NewContext(req, res)
|
||||
h := routes.SetupTokenMiddleware()(func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "test")
|
||||
})
|
||||
|
||||
req.Header.Set(echo.HeaderAuthorization, "Bearer tk_loremipsumdolorsitamet")
|
||||
require.Error(t, h(c))
|
||||
})
|
||||
t.Run("expired token", func(t *testing.T) {
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tasks/all", nil)
|
||||
res := httptest.NewRecorder()
|
||||
c := e.NewContext(req, res)
|
||||
h := routes.SetupTokenMiddleware()(func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "test")
|
||||
})
|
||||
|
||||
req.Header.Set(echo.HeaderAuthorization, "Bearer tk_a5e6f92ddbad68f49ee2c63e52174db0235008c8") // Token 2
|
||||
require.Error(t, h(c))
|
||||
})
|
||||
t.Run("valid token, invalid scope", func(t *testing.T) {
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects", nil)
|
||||
res := httptest.NewRecorder()
|
||||
c := e.NewContext(req, res)
|
||||
h := routes.SetupTokenMiddleware()(func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "test")
|
||||
})
|
||||
|
||||
req.Header.Set(echo.HeaderAuthorization, "Bearer tk_2eef46f40ebab3304919ab2e7e39993f75f29d2e")
|
||||
require.Error(t, h(c))
|
||||
})
|
||||
t.Run("jwt", func(t *testing.T) {
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tasks/all", nil)
|
||||
res := httptest.NewRecorder()
|
||||
c := e.NewContext(req, res)
|
||||
h := routes.SetupTokenMiddleware()(func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "test")
|
||||
})
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
u, err := user.GetUserByID(s, 1)
|
||||
require.NoError(t, err)
|
||||
jwt, err := auth.NewUserJWTAuthtoken(u, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set(echo.HeaderAuthorization, "Bearer "+jwt)
|
||||
require.NoError(t, h(c))
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,7 +21,9 @@ import (
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// This tests the following behaviour:
|
||||
@@ -96,47 +98,47 @@ func TestArchived(t *testing.T) {
|
||||
t.Run("task", func(t *testing.T) {
|
||||
t.Run("edit task", func(t *testing.T) {
|
||||
_, err := testTaskHandler.testUpdateWithUser(nil, map[string]string{"projecttask": taskID}, `{"title":"TestIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("delete", func(t *testing.T) {
|
||||
_, err := testTaskHandler.testDeleteWithUser(nil, map[string]string{"projecttask": taskID})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("add new labels", func(t *testing.T) {
|
||||
_, err := testLabelHandler.testCreateWithUser(nil, map[string]string{"projecttask": taskID}, `{"label_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("remove lables", func(t *testing.T) {
|
||||
_, err := testLabelHandler.testDeleteWithUser(nil, map[string]string{"projecttask": taskID, "label": "4"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("add assignees", func(t *testing.T) {
|
||||
_, err := testAssigneeHandler.testCreateWithUser(nil, map[string]string{"projecttask": taskID}, `{"user_id":3}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("remove assignees", func(t *testing.T) {
|
||||
_, err := testAssigneeHandler.testDeleteWithUser(nil, map[string]string{"projecttask": taskID, "user": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("add relation", func(t *testing.T) {
|
||||
_, err := testRelationHandler.testCreateWithUser(nil, map[string]string{"task": taskID}, `{"other_task_id":1,"relation_kind":"related"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("remove relation", func(t *testing.T) {
|
||||
_, err := testRelationHandler.testDeleteWithUser(nil, map[string]string{"task": taskID}, `{"other_task_id":2,"relation_kind":"related"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("add comment", func(t *testing.T) {
|
||||
_, err := testCommentHandler.testCreateWithUser(nil, map[string]string{"task": taskID}, `{"comment":"Lorem"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
t.Run("remove comment", func(t *testing.T) {
|
||||
@@ -145,7 +147,7 @@ func TestArchived(t *testing.T) {
|
||||
commentID = "16"
|
||||
}
|
||||
_, err := testCommentHandler.testDeleteWithUser(nil, map[string]string{"task": taskID, "commentid": commentID})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, errCode)
|
||||
})
|
||||
})
|
||||
@@ -155,17 +157,17 @@ func TestArchived(t *testing.T) {
|
||||
t.Run("archived parent project", func(t *testing.T) {
|
||||
t.Run("not editable", func(t *testing.T) {
|
||||
_, err := testProjectHandler.testUpdateWithUser(nil, map[string]string{"project": "21"}, `{"title":"TestIpsum","is_archived":true}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectIsArchived)
|
||||
})
|
||||
t.Run("no new tasks", func(t *testing.T) {
|
||||
_, err := testTaskHandler.testCreateWithUser(nil, map[string]string{"project": "21"}, `{"title":"Lorem"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectIsArchived)
|
||||
})
|
||||
t.Run("not unarchivable", func(t *testing.T) {
|
||||
_, err := testProjectHandler.testUpdateWithUser(nil, map[string]string{"project": "21"}, `{"title":"LoremIpsum","is_archived":false}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectIsArchived)
|
||||
})
|
||||
|
||||
@@ -175,17 +177,17 @@ func TestArchived(t *testing.T) {
|
||||
t.Run("archived individually", func(t *testing.T) {
|
||||
t.Run("not editable", func(t *testing.T) {
|
||||
_, err := testProjectHandler.testUpdateWithUser(nil, map[string]string{"project": "22"}, `{"title":"TestIpsum","is_archived":true}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectIsArchived)
|
||||
})
|
||||
t.Run("no new tasks", func(t *testing.T) {
|
||||
_, err := testTaskHandler.testCreateWithUser(nil, map[string]string{"project": "22"}, `{"title":"Lorem"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectIsArchived)
|
||||
})
|
||||
t.Run("unarchivable", func(t *testing.T) {
|
||||
rec, err := testProjectHandler.testUpdateWithUser(nil, map[string]string{"project": "22"}, `{"title":"LoremIpsum","is_archived":false}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"is_archived":false`)
|
||||
})
|
||||
|
||||
|
||||
+261
-18
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,10 +21,25 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/routes/caldav"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const vtodo = `BEGIN:VCALENDAR
|
||||
func TestCaldav(t *testing.T) {
|
||||
t.Run("Delivers VTODO for project", func(t *testing.T) {
|
||||
e, _ := setupTestEnv()
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.ProjectHandler, &testuser15, ``, nil, map[string]string{"project": "36"})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), "BEGIN:VCALENDAR")
|
||||
assert.Contains(t, rec.Body.String(), "PRODID:-//Vikunja Todo App//EN")
|
||||
assert.Contains(t, rec.Body.String(), "X-WR-CALNAME:Project 36 for Caldav tests")
|
||||
assert.Contains(t, rec.Body.String(), "BEGIN:VTODO")
|
||||
assert.Contains(t, rec.Body.String(), "END:VTODO")
|
||||
assert.Contains(t, rec.Body.String(), "END:VCALENDAR")
|
||||
})
|
||||
t.Run("Import VTODO", func(t *testing.T) {
|
||||
const vtodo = `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
@@ -44,25 +59,15 @@ END:VALARM
|
||||
END:VTODO
|
||||
END:VCALENDAR`
|
||||
|
||||
func TestCaldav(t *testing.T) {
|
||||
t.Run("Delivers VTODO for project", func(t *testing.T) {
|
||||
rec, err := newCaldavTestRequestWithUser(t, http.MethodGet, caldav.ProjectHandler, &testuser15, ``, nil, map[string]string{"project": "36"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), "BEGIN:VCALENDAR")
|
||||
assert.Contains(t, rec.Body.String(), "PRODID:-//Vikunja Todo App//EN")
|
||||
assert.Contains(t, rec.Body.String(), "X-WR-CALNAME:Project 36 for Caldav tests")
|
||||
assert.Contains(t, rec.Body.String(), "BEGIN:VTODO")
|
||||
assert.Contains(t, rec.Body.String(), "END:VTODO")
|
||||
assert.Contains(t, rec.Body.String(), "END:VCALENDAR")
|
||||
})
|
||||
t.Run("Import VTODO", func(t *testing.T) {
|
||||
rec, err := newCaldavTestRequestWithUser(t, http.MethodPut, caldav.TaskHandler, &testuser15, vtodo, nil, map[string]string{"project": "36", "task": "uid"})
|
||||
assert.NoError(t, err)
|
||||
e, _ := setupTestEnv()
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, vtodo, nil, map[string]string{"project": "36", "task": "uid"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
})
|
||||
t.Run("Export VTODO", func(t *testing.T) {
|
||||
rec, err := newCaldavTestRequestWithUser(t, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test"})
|
||||
assert.NoError(t, err)
|
||||
e, _ := setupTestEnv()
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test"})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), "BEGIN:VCALENDAR")
|
||||
assert.Contains(t, rec.Body.String(), "SUMMARY:Title Caldav Test")
|
||||
assert.Contains(t, rec.Body.String(), "DESCRIPTION:Description Caldav Test")
|
||||
@@ -75,3 +80,241 @@ func TestCaldav(t *testing.T) {
|
||||
assert.Contains(t, rec.Body.String(), "END:VALARM")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCaldavSubtasks(t *testing.T) {
|
||||
const vtodoHeader = `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:Project 36 for Caldav tests
|
||||
PRODID:-//Vikunja Todo App//EN
|
||||
`
|
||||
const vtodoFooter = `
|
||||
END:VCALENDAR`
|
||||
|
||||
t.Run("Import Task & Subtask", func(t *testing.T) {
|
||||
|
||||
const vtodoParentTaskStub = `BEGIN:VTODO
|
||||
UID:uid_parent_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav parent task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
END:VTODO`
|
||||
|
||||
const vtodoChildTaskStub = `BEGIN:VTODO
|
||||
UID:uid_child_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav child task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=PARENT:uid_parent_import
|
||||
END:VTODO`
|
||||
|
||||
const vtodoGrandChildTaskStub = `
|
||||
BEGIN:VTODO
|
||||
UID:uid_grand_child_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav grand child task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=PARENT:uid_child_import
|
||||
END:VTODO`
|
||||
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
const parentVTODO = vtodoHeader + vtodoParentTaskStub + vtodoFooter
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, parentVTODO, nil, map[string]string{"project": "36", "task": "uid_parent_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
const childVTODO = vtodoHeader + vtodoChildTaskStub + vtodoFooter
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, childVTODO, nil, map[string]string{"project": "36", "task": "uid_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
const grandChildVTODO = vtodoHeader + vtodoGrandChildTaskStub + vtodoFooter
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, grandChildVTODO, nil, map[string]string{"project": "36", "task": "uid_grand_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.ProjectHandler, &testuser15, ``, nil, map[string]string{"project": "36"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_parent_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid_parent_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid_grand_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_grand_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid_child_import")
|
||||
})
|
||||
|
||||
t.Run("Import Task & Subtask (Reverse - Subtask first)", func(t *testing.T) {
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
const vtodoGrandChildTaskStub = `
|
||||
BEGIN:VTODO
|
||||
UID:uid_grand_child_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav grand child task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=PARENT:uid_child_import
|
||||
END:VTODO`
|
||||
|
||||
const grandChildVTODO = vtodoHeader + vtodoGrandChildTaskStub + vtodoFooter
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, grandChildVTODO, nil, map[string]string{"project": "36", "task": "uid_grand_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
const vtodoChildTaskStub = `BEGIN:VTODO
|
||||
UID:uid_child_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav child task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=PARENT:uid_parent_import
|
||||
RELATED-TO;RELTYPE=CHILD:uid_grand_child_import
|
||||
END:VTODO`
|
||||
|
||||
const childVTODO = vtodoHeader + vtodoChildTaskStub + vtodoFooter
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, childVTODO, nil, map[string]string{"project": "36", "task": "uid_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
const vtodoParentTaskStub = `BEGIN:VTODO
|
||||
UID:uid_parent_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav parent task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=CHILD:uid_child_import
|
||||
END:VTODO`
|
||||
|
||||
const parentVTODO = vtodoHeader + vtodoParentTaskStub + vtodoFooter
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, parentVTODO, nil, map[string]string{"project": "36", "task": "uid_parent_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.ProjectHandler, &testuser15, ``, nil, map[string]string{"project": "36"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_parent_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid_parent_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid_grand_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_grand_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid_child_import")
|
||||
})
|
||||
|
||||
t.Run("Delete Subtask", func(t *testing.T) {
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodDelete, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-child-task"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 204, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodDelete, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-child-task-2"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 204, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-parent-task"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
|
||||
assert.NotContains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid-caldav-test-child-task")
|
||||
assert.NotContains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid-caldav-test-child-task-2")
|
||||
})
|
||||
|
||||
t.Run("Delete Parent Task", func(t *testing.T) {
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodDelete, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-parent-task"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 204, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-child-task"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
|
||||
assert.NotContains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCaldavSubtasksDifferentLists(t *testing.T) {
|
||||
t.Run("Import Parent Task & Child Task Different Lists", func(t *testing.T) {
|
||||
const vtodoParentTask = `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:Project 36 for Caldav tests
|
||||
PRODID:-//Vikunja Todo App//EN
|
||||
BEGIN:VTODO
|
||||
UID:uid_parent_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav parent task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
END:VTODO
|
||||
END:VCALENDAR`
|
||||
|
||||
const vtodoChildTask = `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:Project 38 for Caldav tests
|
||||
PRODID:-//Vikunja Todo App//EN
|
||||
BEGIN:VTODO
|
||||
UID:uid_child_import
|
||||
DTSTAMP:20230301T073337Z
|
||||
SUMMARY:Caldav child task
|
||||
CREATED:20230301T073337Z
|
||||
LAST-MODIFIED:20230301T073337Z
|
||||
RELATED-TO;RELTYPE=PARENT:uid_parent_import
|
||||
END:VTODO
|
||||
END:VCALENDAR`
|
||||
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, vtodoParentTask, nil, map[string]string{"project": "36", "task": "uid_parent_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodPut, caldav.TaskHandler, &testuser15, vtodoChildTask, nil, map[string]string{"project": "38", "task": "uid_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 201, rec.Result().StatusCode)
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid_parent_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_parent_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid_child_import")
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "38", "task": "uid_child_import"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid_child_import")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid_parent_import")
|
||||
})
|
||||
|
||||
t.Run("Check relationships across lists", func(t *testing.T) {
|
||||
e, _ := setupTestEnv()
|
||||
|
||||
rec, err := newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "36", "task": "uid-caldav-test-parent-task-another-list"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid-caldav-test-parent-task-another-list")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=CHILD:uid-caldav-test-child-task-another-list")
|
||||
|
||||
rec, err = newCaldavTestRequestWithUser(t, e, http.MethodGet, caldav.TaskHandler, &testuser15, ``, nil, map[string]string{"project": "38", "task": "uid-caldav-test-child-task-another-list"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, rec.Result().StatusCode)
|
||||
assert.Contains(t, rec.Body.String(), "UID:uid-caldav-test-child-task-another-list")
|
||||
assert.Contains(t, rec.Body.String(), "RELATED-TO;RELTYPE=PARENT:uid-caldav-test-parent-task-another-list")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,13 +21,15 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/routes"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHealthcheck(t *testing.T) {
|
||||
t.Run("healthcheck", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodGet, routes.HealthcheckHandler, ``, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), "OK")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -37,9 +37,11 @@ import (
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// These are the test users, the same way they are in the test database
|
||||
@@ -79,54 +81,13 @@ func setupTestEnv() (e *echo.Echo, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func bootstrapTestRequest(t *testing.T, method string, payload string, queryParam url.Values) (c echo.Context, rec *httptest.ResponseRecorder) {
|
||||
// Setup
|
||||
e, err := setupTestEnv()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Do the actual request
|
||||
func createRequest(e *echo.Echo, method string, payload string, queryParam url.Values, urlParams map[string]string) (c echo.Context, rec *httptest.ResponseRecorder) {
|
||||
req := httptest.NewRequest(method, "/", strings.NewReader(payload))
|
||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||
req.URL.RawQuery = queryParam.Encode()
|
||||
rec = httptest.NewRecorder()
|
||||
|
||||
c = e.NewContext(req, rec)
|
||||
return
|
||||
}
|
||||
|
||||
func newTestRequest(t *testing.T, method string, handler func(ctx echo.Context) error, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||
err = handler(c)
|
||||
return
|
||||
}
|
||||
|
||||
func addUserTokenToContext(t *testing.T, user *user.User, c echo.Context) {
|
||||
// Get the token as a string
|
||||
token, err := auth.NewUserJWTAuthtoken(user, false)
|
||||
assert.NoError(t, err)
|
||||
// We send the string token through the parsing function to get a valid jwt.Token
|
||||
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.ServiceJWTSecret.GetString()), nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
c.Set("user", tken)
|
||||
}
|
||||
|
||||
func addLinkShareTokenToContext(t *testing.T, share *models.LinkSharing, c echo.Context) {
|
||||
// Get the token as a string
|
||||
token, err := auth.NewLinkShareJWTAuthtoken(share)
|
||||
assert.NoError(t, err)
|
||||
// We send the string token through the parsing function to get a valid jwt.Token
|
||||
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.ServiceJWTSecret.GetString()), nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
c.Set("user", tken)
|
||||
}
|
||||
|
||||
func testRequestSetup(t *testing.T, method string, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, c echo.Context) {
|
||||
c, rec = bootstrapTestRequest(t, method, payload, queryParams)
|
||||
|
||||
var paramNames []string
|
||||
var paramValues []string
|
||||
for name, value := range urlParams {
|
||||
@@ -138,22 +99,65 @@ func testRequestSetup(t *testing.T, method string, payload string, queryParams u
|
||||
return
|
||||
}
|
||||
|
||||
func bootstrapTestRequest(t *testing.T, method string, payload string, queryParam url.Values, urlParams map[string]string) (c echo.Context, rec *httptest.ResponseRecorder) {
|
||||
// Setup
|
||||
e, err := setupTestEnv()
|
||||
require.NoError(t, err)
|
||||
|
||||
c, rec = createRequest(e, method, payload, queryParam, urlParams)
|
||||
return
|
||||
}
|
||||
|
||||
func newTestRequest(t *testing.T, method string, handler func(ctx echo.Context) error, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
var c echo.Context
|
||||
c, rec = bootstrapTestRequest(t, method, payload, queryParams, urlParams)
|
||||
err = handler(c)
|
||||
return
|
||||
}
|
||||
|
||||
func addUserTokenToContext(t *testing.T, user *user.User, c echo.Context) {
|
||||
// Get the token as a string
|
||||
token, err := auth.NewUserJWTAuthtoken(user, false)
|
||||
require.NoError(t, err)
|
||||
// We send the string token through the parsing function to get a valid jwt.Token
|
||||
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.ServiceJWTSecret.GetString()), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
c.Set("user", tken)
|
||||
}
|
||||
|
||||
func addLinkShareTokenToContext(t *testing.T, share *models.LinkSharing, c echo.Context) {
|
||||
// Get the token as a string
|
||||
token, err := auth.NewLinkShareJWTAuthtoken(share)
|
||||
require.NoError(t, err)
|
||||
// We send the string token through the parsing function to get a valid jwt.Token
|
||||
tken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.ServiceJWTSecret.GetString()), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
c.Set("user", tken)
|
||||
}
|
||||
|
||||
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *user.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||
var c echo.Context
|
||||
c, rec = bootstrapTestRequest(t, method, payload, queryParams, urlParams)
|
||||
addUserTokenToContext(t, user, c)
|
||||
err = handler(c)
|
||||
return
|
||||
}
|
||||
|
||||
func newTestRequestWithLinkShare(t *testing.T, method string, handler echo.HandlerFunc, share *models.LinkSharing, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||
var c echo.Context
|
||||
c, rec = bootstrapTestRequest(t, method, payload, queryParams, urlParams)
|
||||
addLinkShareTokenToContext(t, share, c)
|
||||
err = handler(c)
|
||||
return
|
||||
}
|
||||
|
||||
func newCaldavTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *user.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
|
||||
func newCaldavTestRequestWithUser(t *testing.T, e *echo.Echo, method string, handler echo.HandlerFunc, user *user.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
|
||||
var c echo.Context
|
||||
c, rec = createRequest(e, method, payload, queryParams, urlParams)
|
||||
c.Request().Header.Set(echo.HeaderContentType, echo.MIMETextPlain)
|
||||
|
||||
result, _ := caldav.BasicAuth(user.Username, "1234", c)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -20,11 +20,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBucket(t *testing.T) {
|
||||
@@ -52,7 +53,7 @@ func TestBucket(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket1`)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket2`)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket3`)
|
||||
@@ -63,87 +64,87 @@ func TestBucket(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
// Check the project was loaded successfully afterwards, see testReadOneWithUser
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "9999"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
t.Run("Empty title", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "1"}, `{"title":""}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "5"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "6"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "7"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "8"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "9"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "10"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "11"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "12"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "13"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "14"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "15"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "16"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "17"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
})
|
||||
@@ -151,82 +152,82 @@ func TestBucket(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "1", "bucket": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "20", "bucket": "5"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "6", "bucket": "6"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "7", "bucket": "7"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "8", "bucket": "8"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "9", "bucket": "9"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "10", "bucket": "10"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "11", "bucket": "11"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "12", "bucket": "12"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "13", "bucket": "13"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "14", "bucket": "14"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "15", "bucket": "15"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "16", "bucket": "16"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "17", "bucket": "17"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
})
|
||||
@@ -234,88 +235,88 @@ func TestBucket(t *testing.T) {
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9999"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "20"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "6"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "7"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "8"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "10"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "11"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "12"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "13"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "14"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "15"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "16"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "17"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Link Share", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
db.AssertExists(t, "buckets", map[string]interface{}{
|
||||
"project_id": 2,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,39 +22,41 @@ import (
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLinkSharingAuth(t *testing.T) {
|
||||
t.Run("Without Password", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.AuthenticateLinkShare, ``, nil, map[string]string{"share": "test"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Contains(t, rec.Body.String(), `"token":"`)
|
||||
assert.Contains(t, rec.Body.String(), `"project_id":1`)
|
||||
})
|
||||
t.Run("Without Password, Password Provided", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.AuthenticateLinkShare, `{"password":"somethingsomething"}`, nil, map[string]string{"share": "test"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Contains(t, rec.Body.String(), `"token":"`)
|
||||
assert.Contains(t, rec.Body.String(), `"project_id":1`)
|
||||
})
|
||||
t.Run("With Password, No Password Provided", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.AuthenticateLinkShare, ``, nil, map[string]string{"share": "testWithPassword"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeLinkSharePasswordRequired)
|
||||
})
|
||||
t.Run("With Password, Password Provided", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.AuthenticateLinkShare, `{"password":"1234"}`, nil, map[string]string{"share": "testWithPassword"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Contains(t, rec.Body.String(), `"token":"`)
|
||||
assert.Contains(t, rec.Body.String(), `"project_id":1`)
|
||||
})
|
||||
t.Run("With Wrong Password", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.AuthenticateLinkShare, `{"password":"someWrongPassword"}`, nil, map[string]string{"share": "testWithPassword"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeLinkSharePasswordInvalid)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,8 +22,10 @@ import (
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLinkSharing(t *testing.T) {
|
||||
@@ -66,68 +68,68 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
t.Run("read only", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "20"}, `{"right":0}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "20"}, `{"right":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("admin", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "20"}, `{"right":2}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Read only access", func(t *testing.T) {
|
||||
t.Run("read only", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9"}, `{"right":0}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9"}, `{"right":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("admin", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9"}, `{"right":2}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Write access", func(t *testing.T) {
|
||||
t.Run("read only", func(t *testing.T) {
|
||||
req, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "10"}, `{"right":0}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.Body.String(), `"hash":`)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
req, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "10"}, `{"right":1}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.Body.String(), `"hash":`)
|
||||
})
|
||||
t.Run("admin", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "10"}, `{"right":2}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Admin access", func(t *testing.T) {
|
||||
t.Run("read only", func(t *testing.T) {
|
||||
req, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "11"}, `{"right":0}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.Body.String(), `"hash":`)
|
||||
})
|
||||
t.Run("write", func(t *testing.T) {
|
||||
req, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "11"}, `{"right":1}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.Body.String(), `"hash":`)
|
||||
})
|
||||
t.Run("admin", func(t *testing.T) {
|
||||
req, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "11"}, `{"right":2}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.Body.String(), `"hash":`)
|
||||
})
|
||||
})
|
||||
@@ -159,7 +161,7 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectReadOnly.testReadAllWithLinkShare(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// Should only return the shared project, nothing else
|
||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||
@@ -169,7 +171,7 @@ func TestLinkSharing(t *testing.T) {
|
||||
})
|
||||
t.Run("Search", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectReadOnly.testReadAllWithLinkShare(url.Values{"s": []string{"est1"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// Should only return the shared project, nothing else
|
||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||
@@ -181,35 +183,35 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadOne", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectReadOnly.testReadOneWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testReadOneWithLinkShare(nil, map[string]string{"project": "9999999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Project 2, not shared with this token
|
||||
_, err := testHandlerProjectReadOnly.testReadOneWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `You don't have the right to see this`)
|
||||
})
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectReadOnly.testReadOneWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectWrite.testReadOneWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test2"`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectAdmin.testReadOneWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test3"`)
|
||||
})
|
||||
})
|
||||
@@ -217,28 +219,28 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testUpdateWithLinkShare(nil, map[string]string{"project": "9999999"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testUpdateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testUpdateWithLinkShare(nil, map[string]string{"project": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectWrite.testUpdateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"TestLoremIpsum","namespace_id":1}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectAdmin.testUpdateWithLinkShare(nil, map[string]string{"project": "3"}, `{"title":"TestLoremIpsum","namespace_id":2}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
})
|
||||
@@ -246,28 +248,28 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testDeleteWithLinkShare(nil, map[string]string{"project": "9999999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testDeleteWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testDeleteWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectWrite.testDeleteWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectAdmin.testDeleteWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
})
|
||||
@@ -277,23 +279,23 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testCreateWithLinkShare(nil, nil, `{"title":"Lorem"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectWrite.testCreateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectAdmin.testCreateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -325,51 +327,51 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectUserReadOnly.testReadAllWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[]`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectUserWrite.testReadAllWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[]`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectUserAdmin.testReadAllWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"username":"user1"`)
|
||||
})
|
||||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserReadOnly.testCreateWithLinkShare(nil, map[string]string{"project": "1"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserAdmin.testCreateWithLinkShare(nil, map[string]string{"project": "3"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"project": "1"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserWrite.testUpdateWithLinkShare(nil, map[string]string{"project": "2"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"project": "3"}, `{"user_id":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
@@ -377,17 +379,17 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserReadOnly.testDeleteWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserWrite.testDeleteWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectUserAdmin.testDeleteWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -417,51 +419,51 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectTeamReadOnly.testReadAllWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[]`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectTeamWrite.testReadAllWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[]`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerProjectTeamAdmin.testReadAllWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"name":"testteam1"`)
|
||||
})
|
||||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"project": "1"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"project": "3"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"project": "1"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"project": "2"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"project": "3"}, `{"team_id":1}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
@@ -469,17 +471,17 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamReadOnly.testDeleteWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamWrite.testDeleteWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerProjectTeamAdmin.testDeleteWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -533,7 +535,7 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskReadOnlyCollection.testReadAllWithLinkShare(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||
assert.Contains(t, rec.Body.String(), `task #2`)
|
||||
assert.Contains(t, rec.Body.String(), `task #3`)
|
||||
@@ -551,7 +553,7 @@ func TestLinkSharing(t *testing.T) {
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskWriteCollection.testReadAllWithLinkShare(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3"`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||
@@ -568,7 +570,7 @@ func TestLinkSharing(t *testing.T) {
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskAdminCollection.testReadAllWithLinkShare(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
||||
@@ -587,34 +589,34 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTaskReadOnly.testCreateWithLinkShare(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskAdmin.testCreateWithLinkShare(nil, map[string]string{"project": "3"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTaskReadOnly.testUpdateWithLinkShare(nil, map[string]string{"projecttask": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskWrite.testUpdateWithLinkShare(nil, map[string]string{"projecttask": "13"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskAdmin.testUpdateWithLinkShare(nil, map[string]string{"projecttask": "32"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
@@ -622,17 +624,17 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTaskReadOnly.testDeleteWithLinkShare(nil, map[string]string{"projecttask": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskWrite.testDeleteWithLinkShare(nil, map[string]string{"projecttask": "13"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerTaskAdmin.testDeleteWithLinkShare(nil, map[string]string{"projecttask": "32"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
})
|
||||
@@ -663,34 +665,34 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTeamReadOnly.testReadAllWithLinkShare(nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerTeamWrite.testReadAllWithLinkShare(nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerTeamAdmin.testReadAllWithLinkShare(nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"team": "1"}, `{"name":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"team": "2"}, `{"name":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"team": "3"}, `{"name":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
@@ -698,17 +700,17 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerTeamReadOnly.testDeleteWithLinkShare(nil, map[string]string{"team": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerTeamWrite.testDeleteWithLinkShare(nil, map[string]string{"team": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerTeamAdmin.testDeleteWithLinkShare(nil, map[string]string{"team": "3"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -739,51 +741,51 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareReadOnly.testReadAllWithLinkShare(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hash":"test"`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareWrite.testReadAllWithLinkShare(nil, map[string]string{"project": "2"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hash":"test2"`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareAdmin.testReadAllWithLinkShare(nil, map[string]string{"project": "3"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hash":"test3"`)
|
||||
})
|
||||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareReadOnly.testCreateWithLinkShare(nil, map[string]string{"project": "1"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareAdmin.testCreateWithLinkShare(nil, map[string]string{"project": "3"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareReadOnly.testUpdateWithLinkShare(nil, map[string]string{"share": "1"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareWrite.testUpdateWithLinkShare(nil, map[string]string{"share": "2"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareAdmin.testUpdateWithLinkShare(nil, map[string]string{"share": "3"}, `{}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
@@ -791,17 +793,17 @@ func TestLinkSharing(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareReadOnly.testDeleteWithLinkShare(nil, map[string]string{"share": "1"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareWrite.testDeleteWithLinkShare(nil, map[string]string{"share": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerLinkShareAdmin.testDeleteWithLinkShare(nil, map[string]string{"share": "3"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
@@ -31,12 +33,12 @@ func TestLogin(t *testing.T) {
|
||||
"username": "user1",
|
||||
"password": "1234"
|
||||
}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), "token")
|
||||
})
|
||||
t.Run("Empty payload", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Not existing user", func(t *testing.T) {
|
||||
@@ -44,7 +46,7 @@ func TestLogin(t *testing.T) {
|
||||
"username": "userWichDoesNotExist",
|
||||
"password": "1234"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
|
||||
})
|
||||
t.Run("Wrong password", func(t *testing.T) {
|
||||
@@ -52,7 +54,7 @@ func TestLogin(t *testing.T) {
|
||||
"username": "user1",
|
||||
"password": "wrong"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
|
||||
})
|
||||
t.Run("user with unconfirmed email", func(t *testing.T) {
|
||||
@@ -60,7 +62,7 @@ func TestLogin(t *testing.T) {
|
||||
"username": "user5",
|
||||
"password": "1234"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeEmailNotConfirmed)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,8 +22,10 @@ import (
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestProject(t *testing.T) {
|
||||
@@ -37,7 +39,7 @@ func TestProject(t *testing.T) {
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test2"`)
|
||||
assert.Contains(t, rec.Body.String(), `Test3`) // Shared directly via users_project
|
||||
@@ -47,7 +49,7 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Search", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"Test1"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test2`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test3`)
|
||||
@@ -56,7 +58,7 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Normal with archived projects", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"is_archived": []string{"true"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Test1`)
|
||||
assert.NotContains(t, rec.Body.String(), `Test2"`)
|
||||
assert.Contains(t, rec.Body.String(), `Test3`) // Shared directly via users_project
|
||||
@@ -69,7 +71,7 @@ func TestProject(t *testing.T) {
|
||||
t.Run("ReadOne", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1,"name":"","username":"user1",`)
|
||||
@@ -79,89 +81,89 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "9999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "20"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `You don't have the right to see this`)
|
||||
assert.Empty(t, rec.Result().Header.Get("x-max-rights"))
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "6"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test6"`)
|
||||
assert.Equal(t, "0", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "7"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test7"`)
|
||||
assert.Equal(t, "1", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "8"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test8"`)
|
||||
assert.Equal(t, "2", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "9"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test9"`)
|
||||
assert.Equal(t, "0", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "10"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test10"`)
|
||||
assert.Equal(t, "1", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "11"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test11"`)
|
||||
assert.Equal(t, "2", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "12"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test12"`)
|
||||
assert.Equal(t, "0", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "13"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test13"`)
|
||||
assert.Equal(t, "1", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "14"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test14"`)
|
||||
assert.Equal(t, "2", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "15"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test15"`)
|
||||
assert.Equal(t, "0", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "16"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test16"`)
|
||||
assert.Equal(t, "1", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadOneWithUser(nil, map[string]string{"project": "17"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Test17"`)
|
||||
assert.Equal(t, "2", rec.Result().Header.Get("x-max-right"))
|
||||
})
|
||||
@@ -171,100 +173,100 @@ func TestProject(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
// Check the project was loaded successfully afterwards, see testReadOneWithUser
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
// The description should not be updated but returned correctly
|
||||
assert.Contains(t, rec.Body.String(), `description":"Lorem Ipsum`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "9999"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Normal with updating the description", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "1"}, `{"title":"TestLoremIpsum","description":"Lorem Ipsum dolor sit amet"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum dolor sit amet`)
|
||||
})
|
||||
t.Run("Empty title", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "1"}, `{"title":""}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||
})
|
||||
t.Run("Title too long", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(1|250)")
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "20"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "6"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "7"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "8"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "9"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "10"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "11"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "12"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "13"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "14"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "15"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "16"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"project": "17"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
})
|
||||
@@ -272,82 +274,82 @@ func TestProject(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "20"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "6"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "7"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "8"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "9"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "10"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "11"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "12"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "13"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "14"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "15"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "16"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"project": "17"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
})
|
||||
@@ -356,7 +358,7 @@ func TestProject(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
// Check the project was loaded successfully after update, see testReadOneWithUser
|
||||
rec, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
@@ -364,7 +366,7 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Normal with description", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","description":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
@@ -372,34 +374,34 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Nonexisting parent project", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":99999}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Empty title", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":""}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields, "title: non zero value required")
|
||||
})
|
||||
t.Run("Title too long", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea taki"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message.(models.ValidationHTTPError).InvalidFields[0], "does not validate as runelength(1|250)")
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":20}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":32}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":33}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
@@ -407,7 +409,7 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":34}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
@@ -416,12 +418,12 @@ func TestProject(t *testing.T) {
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":9}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, nil, `{"title":"Lorem","parent_project_id":10}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
@@ -429,7 +431,7 @@ func TestProject(t *testing.T) {
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"namespace": "12"}, `{"title":"Lorem","parent_project_id":11}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem"`)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"owner":{"id":1`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,31 +22,33 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
t.Run("normal register", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
|
||||
"username": "newUser",
|
||||
"password": "1234",
|
||||
"password": "12345678",
|
||||
"email": "email@example.com"
|
||||
}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"username":"newUser"`)
|
||||
})
|
||||
t.Run("Empty payload", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Empty username", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
|
||||
"username": "",
|
||||
"password": "1234",
|
||||
"password": "12345678",
|
||||
"email": "email@example.com"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Empty password", func(t *testing.T) {
|
||||
@@ -55,34 +57,34 @@ func TestRegister(t *testing.T) {
|
||||
"password": "",
|
||||
"email": "email@example.com"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Empty email", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
|
||||
"username": "newUser",
|
||||
"password": "1234",
|
||||
"password": "12345678",
|
||||
"email": ""
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Already existing username", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
|
||||
"username": "user1",
|
||||
"password": "1234",
|
||||
"password": "12345678",
|
||||
"email": "email@example.com"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrorCodeUsernameExists)
|
||||
})
|
||||
t.Run("Already existing email", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
|
||||
"username": "newUser",
|
||||
"password": "1234",
|
||||
"password": "12345678",
|
||||
"email": "user1@example.com"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrorCodeUserEmailExists)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTaskCollection(t *testing.T) {
|
||||
@@ -39,7 +41,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||
assert.Contains(t, rec.Body.String(), `task #2 `)
|
||||
@@ -73,7 +75,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
})
|
||||
t.Run("Search", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -91,7 +93,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
})
|
||||
t.Run("Search case insensitive", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"tASk #6"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -112,65 +114,65 @@ func TestTaskCollection(t *testing.T) {
|
||||
// should equal priority asc
|
||||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
t.Run("by priority desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
})
|
||||
t.Run("by priority asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
// Due date without unix suffix
|
||||
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by due_date without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
t.Run("by duedate asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskField)
|
||||
})
|
||||
t.Run("invalid sort order", func(t *testing.T) {
|
||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"id"}, "order_by": []string{"loremipsum"}}, urlParams)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeInvalidSortOrder)
|
||||
})
|
||||
t.Run("invalid parameter", func(t *testing.T) {
|
||||
// Invalid parameter should not sort at all
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
||||
@@ -188,7 +190,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
urlParams,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -213,7 +215,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
urlParams,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -238,7 +240,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
urlParams,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// If no start date but an end date is specified, this should be null
|
||||
// since we don't have any tasks in the fixtures with an end date >
|
||||
// the current date.
|
||||
@@ -253,7 +255,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
urlParams,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -279,7 +281,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
|
||||
})
|
||||
})
|
||||
@@ -289,7 +291,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
nil,
|
||||
map[string]string{"project": "-2"}, // Actually a saved filter - contains the same filter arguments as the start and end date filter from above
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -311,7 +313,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
t.Run("ReadAll for all tasks", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||
assert.Contains(t, rec.Body.String(), `task #2 `)
|
||||
@@ -345,7 +347,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
})
|
||||
t.Run("Search", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -365,39 +367,39 @@ func TestTaskCollection(t *testing.T) {
|
||||
// should equal priority asc
|
||||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
t.Run("by priority desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
})
|
||||
t.Run("by priority asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||
})
|
||||
t.Run("by duedate asc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||
})
|
||||
t.Run("invalid parameter", func(t *testing.T) {
|
||||
// Invalid parameter should not sort at all
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
||||
@@ -415,7 +417,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -440,7 +442,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||
@@ -465,7 +467,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
// If no start date but an end date is specified, this should be null
|
||||
// since we don't have any tasks in the fixtures with an end date >
|
||||
// the current date.
|
||||
@@ -481,7 +483,7 @@ func TestTaskCollection(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -20,11 +20,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTaskComments(t *testing.T) {
|
||||
@@ -54,82 +55,82 @@ func TestTaskComments(t *testing.T) {
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "1", "commentid": "1"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "99999", "commentid": "9999"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
// Only the own comments can be updated
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "14", "commentid": "2"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "15", "commentid": "3"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "16", "commentid": "4"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "17", "commentid": "5"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "18", "commentid": "6"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "19", "commentid": "7"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "20", "commentid": "8"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "21", "commentid": "9"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "22", "commentid": "10"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "23", "commentid": "11"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "24", "commentid": "12"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "25", "commentid": "13"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"task": "26", "commentid": "14"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -137,82 +138,82 @@ func TestTaskComments(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "1", "commentid": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "99999", "commentid": "9999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
// Only the own comments can be deleted
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "14", "commentid": "2"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "15", "commentid": "3"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "16", "commentid": "4"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "17", "commentid": "5"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "18", "commentid": "6"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "19", "commentid": "7"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "20", "commentid": "8"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "21", "commentid": "9"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "22", "commentid": "10"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "23", "commentid": "11"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "24", "commentid": "12"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "25", "commentid": "13"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"task": "26", "commentid": "14"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
})
|
||||
@@ -220,88 +221,88 @@ func TestTaskComments(t *testing.T) {
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "1"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "9999"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "34"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "15"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "16"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "17"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "18"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "19"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "20"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "21"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "22"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "23"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "24"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "25"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"task": "26"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Link Share", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"task": "13"}, `{"comment":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"comment":"Lorem Ipsum"`)
|
||||
db.AssertExists(t, "task_comments", map[string]interface{}{
|
||||
"task_id": 13,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -20,11 +20,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTask(t *testing.T) {
|
||||
@@ -55,49 +56,49 @@ func TestTask(t *testing.T) {
|
||||
t.Run("Update task items", func(t *testing.T) {
|
||||
t.Run("Title", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"title":"task #1"`)
|
||||
})
|
||||
t.Run("Description", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"description":"Dolor sit amet"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"description":"Dolor sit amet"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Description to empty", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"description":""}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"description":""`)
|
||||
assert.NotContains(t, rec.Body.String(), `"description":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Done", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"done":true}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"done":true`)
|
||||
assert.NotContains(t, rec.Body.String(), `"done":false`)
|
||||
})
|
||||
t.Run("Undone", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "2"}, `{"done":false}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"done":false`)
|
||||
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
||||
})
|
||||
t.Run("Due date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"due_date": "2020-02-10T10:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"due_date":0`)
|
||||
})
|
||||
t.Run("Due date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "5"}, `{"due_date": null}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"due_date":"0001-01-01T00:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Reminders", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"reminders": [{"reminder": "2020-02-10T10:00:00Z"},{"reminder": "2020-02-11T10:00:00Z"}]}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminders":[`)
|
||||
assert.Contains(t, rec.Body.String(), `{"reminder":"2020-02-10T10:00:00Z"`)
|
||||
assert.Contains(t, rec.Body.String(), `{"reminder":"2020-02-11T10:00:00Z"`)
|
||||
@@ -105,109 +106,109 @@ func TestTask(t *testing.T) {
|
||||
})
|
||||
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders": []}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Reminders unset to null", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders": null}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Repeat after", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"repeat_after":3600}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"repeat_after":3600`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeat_after":0`)
|
||||
})
|
||||
t.Run("Repeat after unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "28"}, `{"repeat_after":0}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"repeat_after":0`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeat_after":3600`)
|
||||
})
|
||||
t.Run("Repeat after update done", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "28"}, `{"done":true}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"done":false`)
|
||||
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
||||
})
|
||||
t.Run("Assignees", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"assignees":[{"id":1}]}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `"assignees":[]`)
|
||||
})
|
||||
t.Run("Removing Assignees empty array", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "30"}, `{"assignees":[]}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||
})
|
||||
t.Run("Removing Assignees null", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "30"}, `{"assignees":null}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"assignees":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"assignees":[{"id":1`)
|
||||
})
|
||||
t.Run("Priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"priority":100}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"priority":100`)
|
||||
assert.NotContains(t, rec.Body.String(), `"priority":0`)
|
||||
})
|
||||
t.Run("Priority to 0", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "3"}, `{"priority":0}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"priority":0`)
|
||||
assert.NotContains(t, rec.Body.String(), `"priority":100`)
|
||||
})
|
||||
t.Run("Start date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"start_date":"2020-02-10T10:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"start_date":0`)
|
||||
})
|
||||
t.Run("Start date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "7"}, `{"start_date":"0001-01-01T00:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"start_date":"0001-01-01T00:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("End date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"end_date":"2020-02-10T12:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"end_date":"2020-02-10T12:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
||||
})
|
||||
t.Run("End date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "8"}, `{"end_date":"0001-01-01T00:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"end_date":"0001-01-01T00:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Color", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"hex_color":"f0f0f0"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hex_color":"f0f0f0"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hex_color":""`)
|
||||
})
|
||||
t.Run("Color unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "31"}, `{"hex_color":""}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hex_color":""`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hex_color":"f0f0f0"`)
|
||||
})
|
||||
t.Run("Percent Done", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"percent_done":0.1}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"percent_done":0.1`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percent_done":0,`)
|
||||
})
|
||||
t.Run("Percent Done unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "33"}, `{"percent_done":0}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"percent_done":0,`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percent_done":0.1`)
|
||||
})
|
||||
@@ -215,112 +216,112 @@ func TestTask(t *testing.T) {
|
||||
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "99999"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "14"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "15"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "16"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "17"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "18"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "19"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "20"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "21"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "22"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "23"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "24"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "25"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "26"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Move to other project", func(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"project_id":7}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"project_id":7`)
|
||||
assert.NotContains(t, rec.Body.String(), `"project_id":1`)
|
||||
})
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"project_id":20}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
t.Run("Read Only", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"project_id":6}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
})
|
||||
t.Run("Bucket", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"bucket_id":3}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"bucket_id":3`)
|
||||
assert.NotContains(t, rec.Body.String(), `"bucket_id":1`)
|
||||
})
|
||||
t.Run("Different Project", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"bucket_id":4}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotBelongToProject)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"bucket_id":9999}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
})
|
||||
@@ -328,81 +329,81 @@ func TestTask(t *testing.T) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "1"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "99999"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeTaskDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "14"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "15"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "16"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "17"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "18"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "19"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "20"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "21"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "22"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "23"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "24"})
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "25"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"projecttask": "26"})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Successfully deleted.`)
|
||||
})
|
||||
})
|
||||
@@ -410,106 +411,106 @@ func TestTask(t *testing.T) {
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9999"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeProjectDoesNotExist)
|
||||
})
|
||||
t.Run("Rights check", func(t *testing.T) {
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
// Owned by user13
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "20"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "6"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "7"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "8"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "9"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "10"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "11"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project Team readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "12"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "13"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project Team admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "14"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via Parent Project User readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "15"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "16"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via Parent Project User admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "17"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Bucket", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum","bucket_id":3}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"bucket_id":3`)
|
||||
assert.NotContains(t, rec.Body.String(), `"bucket_id":1`)
|
||||
})
|
||||
t.Run("Different Project", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum","bucket_id":4}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotBelongToProject)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"project": "1"}, `{"title":"Lorem Ipsum","bucket_id":9999}`)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
})
|
||||
t.Run("Link Share", func(t *testing.T) {
|
||||
rec, err := testHandlerLinkShareWrite.testCreateWithLinkShare(nil, map[string]string{"project": "2"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"project_id": 2,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,12 +22,13 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckToken(t *testing.T) {
|
||||
t.Run("Normal test", func(t *testing.T) {
|
||||
rec, err := newTestRequestWithUser(t, http.MethodPost, apiv1.CheckToken, &testuser1, "", nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `🍵`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserChangePassword(t *testing.T) {
|
||||
@@ -31,7 +33,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
"new_password": "12345",
|
||||
"old_password": "1234"
|
||||
}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `The password was updated successfully.`)
|
||||
})
|
||||
t.Run("Wrong old password", func(t *testing.T) {
|
||||
@@ -39,7 +41,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
"new_password": "12345",
|
||||
"old_password": "invalid"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
|
||||
})
|
||||
t.Run("Empty old password", func(t *testing.T) {
|
||||
@@ -47,7 +49,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
"new_password": "12345",
|
||||
"old_password": ""
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeEmptyOldPassword)
|
||||
})
|
||||
t.Run("Empty new password", func(t *testing.T) {
|
||||
@@ -55,7 +57,7 @@ func TestUserChangePassword(t *testing.T) {
|
||||
"new_password": "",
|
||||
"old_password": "1234"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeEmptyNewPassword)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,30 +22,32 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserConfirmEmail(t *testing.T) {
|
||||
t.Run("Normal test", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael"}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `The email was confirmed successfully.`)
|
||||
})
|
||||
t.Run("Empty payload", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, http.StatusPreconditionFailed, err.(*echo.HTTPError).Code)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
|
||||
})
|
||||
t.Run("Empty token", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": ""}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
|
||||
})
|
||||
t.Run("Invalid token", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": "invalidToken"}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -24,27 +24,28 @@ import (
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserRequestResetPasswordToken(t *testing.T) {
|
||||
t.Run("Normal requesting a password reset token", func(t *testing.T) {
|
||||
rec, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1@example.com"}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `Token was sent.`)
|
||||
})
|
||||
t.Run("Empty payload", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Invalid email address", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1example.com"}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, http.StatusBadRequest, err.(*echo.HTTPError).Code)
|
||||
})
|
||||
t.Run("No user with that email address", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1000@example.com"}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeUserDoesNotExist)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,8 +22,10 @@ import (
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserPasswordReset(t *testing.T) {
|
||||
@@ -32,12 +34,12 @@ func TestUserPasswordReset(t *testing.T) {
|
||||
"new_password": "1234",
|
||||
"token": "passwordresettesttoken"
|
||||
}`, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `The password was updated successfully.`)
|
||||
})
|
||||
t.Run("Empty payload", func(t *testing.T) {
|
||||
_, err := newTestRequest(t, http.MethodPost, apiv1.UserResetPassword, `{}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, http.StatusBadRequest, err.(*echo.HTTPError).Code)
|
||||
})
|
||||
t.Run("No new password", func(t *testing.T) {
|
||||
@@ -45,7 +47,7 @@ func TestUserPasswordReset(t *testing.T) {
|
||||
"new_password": "",
|
||||
"token": "passwordresettesttoken"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
|
||||
})
|
||||
t.Run("Invalid password reset token", func(t *testing.T) {
|
||||
@@ -53,7 +55,7 @@ func TestUserPasswordReset(t *testing.T) {
|
||||
"new_password": "1234",
|
||||
"token": "invalidtoken"
|
||||
}`, nil, nil)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, user.ErrCodeInvalidPasswordResetToken)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,18 +21,20 @@ import (
|
||||
"testing"
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserProject(t *testing.T) {
|
||||
t.Run("Normal test", func(t *testing.T) {
|
||||
rec, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserList, &testuser1, "", nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "null\n", rec.Body.String())
|
||||
})
|
||||
t.Run("Search for user3", func(t *testing.T) {
|
||||
rec, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserList, &testuser1, "", map[string][]string{"s": {"user3"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `user3`)
|
||||
assert.NotContains(t, rec.Body.String(), `user1`)
|
||||
assert.NotContains(t, rec.Body.String(), `user2`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,13 +21,15 @@ import (
|
||||
"testing"
|
||||
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserShow(t *testing.T) {
|
||||
t.Run("Normal test", func(t *testing.T) {
|
||||
rec, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserShow, &testuser1, "", nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"id":1`)
|
||||
assert.Contains(t, rec.Body.String(), `"username":"user1"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"email":""`)
|
||||
|
||||
+35
-31
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -22,9 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/op/go-logging"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// ErrFmt holds the format for all the console logging
|
||||
@@ -41,42 +39,45 @@ const logModule = `vikunja`
|
||||
// loginstance is the instance of the logger which is used under the hood to log
|
||||
var logInstance = logging.MustGetLogger(logModule)
|
||||
|
||||
// logpath is the path in which log files will be written.
|
||||
// This value is a mere fallback for other modules that could but shouldn't be used before calling ConfigLogger
|
||||
var logPath = "."
|
||||
|
||||
// InitLogger initializes the global log handler
|
||||
func InitLogger() {
|
||||
if !config.LogEnabled.GetBool() {
|
||||
// Disable all logging when loggin in general is disabled, overwriting everything a user might have set.
|
||||
config.LogStandard.Set("off")
|
||||
config.LogDatabase.Set("off")
|
||||
config.LogHTTP.Set("off")
|
||||
config.LogEcho.Set("off")
|
||||
config.LogEvents.Set("off")
|
||||
return
|
||||
}
|
||||
|
||||
// This show correct caller functions
|
||||
logInstance.ExtraCalldepth = 1
|
||||
|
||||
if config.LogStandard.GetString() == "file" {
|
||||
err := os.Mkdir(config.LogPath.GetString(), 0744)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
Fatalf("Could not create log folder: %s", err.Error())
|
||||
}
|
||||
// Init with stdout and INFO as default format and level
|
||||
logBackend := logging.NewLogBackend(os.Stdout, "", 0)
|
||||
backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n"))
|
||||
|
||||
backendLeveled := logging.AddModuleLevel(backend)
|
||||
backendLeveled.SetLevel(logging.INFO, logModule)
|
||||
|
||||
logInstance.SetBackend(backendLeveled)
|
||||
}
|
||||
|
||||
// ConfigLogger configures the global log handler
|
||||
func ConfigLogger(configLogEnabled bool, configLogStandard string, configLogPath string, configLogLevel string) {
|
||||
lvl := strings.ToUpper(configLogLevel)
|
||||
level, err := logging.LogLevel(lvl)
|
||||
if err != nil {
|
||||
Fatalf("Error setting standard log level %s: %s", lvl, err.Error())
|
||||
}
|
||||
|
||||
logPath = configLogPath
|
||||
|
||||
// The backend is the part which actually handles logging the log entries somewhere.
|
||||
cf := config.LogStandard.GetString()
|
||||
var backend logging.Backend
|
||||
backend = &NoopBackend{}
|
||||
if cf != "off" && cf != "false" {
|
||||
stdWriter := GetLogWriter("standard")
|
||||
|
||||
logBackend := logging.NewLogBackend(stdWriter, "", 0)
|
||||
backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n"))
|
||||
if configLogStandard == "false" {
|
||||
configLogStandard = "off"
|
||||
Warning("log.standard value 'false' is deprecated and will be removed in a future release. Please use the value 'off'.")
|
||||
}
|
||||
|
||||
level, err := logging.LogLevel(strings.ToUpper(config.LogLevel.GetString()))
|
||||
if err != nil {
|
||||
Fatalf("Error setting database log level: %s", err.Error())
|
||||
if configLogEnabled && configLogStandard != "off" {
|
||||
logBackend := logging.NewLogBackend(GetLogWriter(configLogStandard, "standard"), "", 0)
|
||||
backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n"))
|
||||
}
|
||||
|
||||
backendLeveled := logging.AddModuleLevel(backend)
|
||||
@@ -86,11 +87,14 @@ func InitLogger() {
|
||||
}
|
||||
|
||||
// GetLogWriter returns the writer to where the normal log goes, depending on the config
|
||||
func GetLogWriter(logfile string) (writer io.Writer) {
|
||||
func GetLogWriter(logfmt string, logfile string) (writer io.Writer) {
|
||||
writer = os.Stdout // Set the default case to prevent nil pointer panics
|
||||
switch viper.GetString("log." + logfile) {
|
||||
switch logfmt {
|
||||
case "file":
|
||||
fullLogFilePath := config.LogPath.GetString() + "/" + logfile + ".log"
|
||||
if err := os.MkdirAll(logPath, 0744); err != nil {
|
||||
Fatalf("Could not create log path: %s", err.Error())
|
||||
}
|
||||
fullLogFilePath := logPath + "/" + logfile + ".log"
|
||||
f, err := os.OpenFile(fullLogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
Fatalf("Could not create logfile %s: %s", fullLogFilePath, err.Error())
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/op/go-logging"
|
||||
"xorm.io/xorm/log"
|
||||
)
|
||||
@@ -33,19 +32,24 @@ type MailLogger struct {
|
||||
const mailFormat = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ [MAIL] %{id:03x}%{color:reset} %{message}`
|
||||
const mailLogModule = `vikunja_mail`
|
||||
|
||||
func NewMailLogger() *MailLogger {
|
||||
lvl := strings.ToUpper(config.LogMailLevel.GetString())
|
||||
// NewMailLogger creates and initializes a new mail logger
|
||||
func NewMailLogger(configLogEnabled bool, configLogMail string, configLogMailLevel string) *MailLogger {
|
||||
lvl := strings.ToUpper(configLogMailLevel)
|
||||
level, err := logging.LogLevel(lvl)
|
||||
if err != nil {
|
||||
Criticalf("Error setting database log level: %s", err.Error())
|
||||
Criticalf("Error setting mail log level %s: %s", lvl, err.Error())
|
||||
}
|
||||
|
||||
mailLogger := &MailLogger{
|
||||
logger: logging.MustGetLogger(mailLogModule),
|
||||
}
|
||||
|
||||
logBackend := logging.NewLogBackend(GetLogWriter("mail"), "", 0)
|
||||
backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(mailFormat+"\n"))
|
||||
var backend logging.Backend
|
||||
backend = &NoopBackend{}
|
||||
if configLogEnabled && configLogMail != "off" {
|
||||
logBackend := logging.NewLogBackend(GetLogWriter(configLogMail, "mail"), "", 0)
|
||||
backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(mailFormat+"\n"))
|
||||
}
|
||||
|
||||
backendLeveled := logging.AddModuleLevel(backend)
|
||||
backendLeveled.SetLevel(level, mailLogModule)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
@@ -34,8 +33,9 @@ type WatermillLogger struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
func NewWatermillLogger() *WatermillLogger {
|
||||
lvl := strings.ToUpper(config.LogEventsLevel.GetString())
|
||||
// NewXormLogger creates and initializes a new watermill logger
|
||||
func NewWatermillLogger(configLogEnabled bool, configLogEvents string, configLogEventsLevel string) *WatermillLogger {
|
||||
lvl := strings.ToUpper(configLogEventsLevel)
|
||||
level, err := logging.LogLevel(lvl)
|
||||
if err != nil {
|
||||
Criticalf("Error setting events log level %s: %s", lvl, err.Error())
|
||||
@@ -45,11 +45,14 @@ func NewWatermillLogger() *WatermillLogger {
|
||||
logger: logging.MustGetLogger(watermillLogModule),
|
||||
}
|
||||
|
||||
cf := config.LogEvents.GetString()
|
||||
var backend logging.Backend
|
||||
backend = &NoopBackend{}
|
||||
if cf != "off" && cf != "false" {
|
||||
logBackend := logging.NewLogBackend(GetLogWriter("events"), "", 0)
|
||||
if configLogEvents == "false" {
|
||||
configLogEvents = "off"
|
||||
Warning("log.events value 'false' is deprecated and will be removed in a future release. Please use the value 'off'.")
|
||||
}
|
||||
if configLogEnabled && configLogEvents != "off" {
|
||||
logBackend := logging.NewLogBackend(GetLogWriter(configLogEvents, "events"), "", 0)
|
||||
backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(watermillFmt+"\n"))
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/op/go-logging"
|
||||
"xorm.io/xorm/log"
|
||||
)
|
||||
@@ -38,21 +37,23 @@ type XormLogger struct {
|
||||
}
|
||||
|
||||
// NewXormLogger creates and initializes a new xorm logger
|
||||
func NewXormLogger(lvl string) *XormLogger {
|
||||
if lvl == "" {
|
||||
lvl = strings.ToUpper(config.LogDatabaseLevel.GetString())
|
||||
}
|
||||
func NewXormLogger(configLogEnabled bool, configLogDatabase string, configLogDatabaseLevel string) *XormLogger {
|
||||
lvl := strings.ToUpper(configLogDatabaseLevel)
|
||||
level, err := logging.LogLevel(lvl)
|
||||
if err != nil {
|
||||
Criticalf("Error setting database log level: %s", err.Error())
|
||||
Criticalf("Error setting database log level %s: %s", lvl, err.Error())
|
||||
}
|
||||
|
||||
xormLogger := &XormLogger{
|
||||
logger: logging.MustGetLogger(xormLogModule),
|
||||
}
|
||||
|
||||
logBackend := logging.NewLogBackend(GetLogWriter("database"), "", 0)
|
||||
backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(XormFmt+"\n"))
|
||||
var backend logging.Backend
|
||||
backend = &NoopBackend{}
|
||||
if configLogEnabled && configLogDatabase != "off" {
|
||||
logBackend := logging.NewLogBackend(GetLogWriter(configLogDatabase, "database"), "", 0)
|
||||
backend = logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(XormFmt+"\n"))
|
||||
}
|
||||
|
||||
backendLeveled := logging.AddModuleLevel(backend)
|
||||
backendLeveled.SetLevel(level, xormLogModule)
|
||||
@@ -64,8 +65,8 @@ func NewXormLogger(lvl string) *XormLogger {
|
||||
case logging.ERROR:
|
||||
xormLogger.level = log.LOG_ERR
|
||||
case logging.WARNING:
|
||||
case logging.NOTICE:
|
||||
xormLogger.level = log.LOG_WARNING
|
||||
case logging.NOTICE:
|
||||
case logging.INFO:
|
||||
xormLogger.level = log.LOG_INFO
|
||||
case logging.DEBUG:
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
@@ -56,7 +56,7 @@ func getClient() (*mail.Client, error) {
|
||||
ServerName: config.MailerHost.GetString(),
|
||||
}),
|
||||
mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout
|
||||
mail.WithLogger(log.NewMailLogger()),
|
||||
mail.WithLogger(log.NewMailLogger(config.LogEnabled.GetBool(), config.LogMail.GetString(), config.LogMailLevel.GetString())),
|
||||
mail.WithDebugLog(),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user