forked from vikunja/vikunja
Compare commits
1 Commits
master
...
fix/swagge
Author | SHA1 | Date | |
---|---|---|---|
616ed4ad89 |
|
@ -57,8 +57,6 @@ steps:
|
|||
- make goconst-check
|
||||
- make gocyclo-check
|
||||
- make static-check
|
||||
- wget -O - -q https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin v2.2.0 # Need to manually install as it does not support being installed via go modules like the rest.
|
||||
- make gosec-check
|
||||
- make build
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
|
12
Makefile
12
Makefile
|
@ -192,7 +192,7 @@ do-the-swag:
|
|||
@hash swag > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go install $(GOFLAGS) github.com/swaggo/swag/cmd/swag; \
|
||||
fi
|
||||
swag init -g pkg/routes/routes.go -o ./pkg/swagger;
|
||||
swag init --parseVendor -g pkg/routes/routes.go -o ./pkg/swagger;
|
||||
# Fix the generated swagger file, currently a workaround until swaggo can properly use go mod
|
||||
sed -i '/"definitions": {/a "code.vikunja.io.web.HTTPError": {"type": "object","properties": {"code": {"type": "integer"},"message": {"type": "string"}}},' pkg/swagger/docs.go;
|
||||
sed -i 's/code.vikunja.io\/web.HTTPError/code.vikunja.io.web.HTTPError/g' pkg/swagger/docs.go;
|
||||
|
@ -231,17 +231,15 @@ static-check:
|
|||
|
||||
.PHONY: gosec-check
|
||||
gosec-check:
|
||||
@hash gosec > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Please manually install gosec by running"; \
|
||||
echo "curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | bash -s -- -b $GOPATH/bin v2.2.0"; \
|
||||
exit 1; \
|
||||
@hash ./bin/gosec > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s 1.2.0; \
|
||||
fi
|
||||
gosec ./...
|
||||
for S in $(PACKAGES); do ./bin/gosec $$S || exit 1; done;
|
||||
|
||||
.PHONY: goconst-check
|
||||
goconst-check:
|
||||
@hash goconst > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/jgautheron/goconst/cmd/goconst; \
|
||||
go install $(GOFLAGS) github.com/jgautheron/goconst/cmd/goconst; \
|
||||
fi;
|
||||
fi
|
||||
for S in $(PACKAGES); do goconst $$S || exit 1; done;
|
||||
|
|
|
@ -106,14 +106,14 @@ log:
|
|||
path: <rootpath>logs
|
||||
# Whether to show any logging at all or none
|
||||
enabled: true
|
||||
# Where the error log should go. Possible values are stdout, stderr, file or off to disable error logging.
|
||||
errors: "stdout"
|
||||
# Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
standard: "stdout"
|
||||
# Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
level: "INFO"
|
||||
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||
database: "off"
|
||||
# The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
databaselevel: "WARNING"
|
||||
# The log level for database log messages. Possible values are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
databaselevel: "DEBUG"
|
||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||
http: "stdout"
|
||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
|
|
|
@ -37,6 +37,3 @@ menu:
|
|||
- name: Code
|
||||
url: https://code.vikunja.io/
|
||||
weight: 50
|
||||
- name: Community
|
||||
url: https://community.vikunja.io/
|
||||
weight: 60
|
||||
|
|
|
@ -149,14 +149,14 @@ log:
|
|||
path: <rootpath>logs
|
||||
# Whether to show any logging at all or none
|
||||
enabled: true
|
||||
# Where the error log should go. Possible values are stdout, stderr, file or off to disable error logging.
|
||||
errors: "stdout"
|
||||
# Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
standard: "stdout"
|
||||
# Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
level: "INFO"
|
||||
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||
database: "off"
|
||||
# The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
databaselevel: "WARNING"
|
||||
# The log level for database log messages. Possible values are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
databaselevel: "DEBUG"
|
||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||
http: "stdout"
|
||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
|
|
|
@ -12,16 +12,9 @@ menu:
|
|||
|
||||
This document describes the different errors Vikunja can return.
|
||||
|
||||
### Generic
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 0001 | 403 | Generic forbidden error. |
|
||||
|
||||
### User
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 1001 | 400 | A user with this username already exists. |
|
||||
| 1002 | 400 | A user with this email address already exists. |
|
||||
| 1004 | 400 | No username and password specified. |
|
||||
|
@ -34,32 +27,14 @@ This document describes the different errors Vikunja can return.
|
|||
| 1012 | 412 | Email address of the user not confirmed. |
|
||||
| 1013 | 412 | New password is empty. |
|
||||
| 1014 | 412 | Old password is empty. |
|
||||
| 1015 | 412 | Totp is already enabled for this user. |
|
||||
| 1016 | 412 | Totp is not enabled for this user. |
|
||||
| 1017 | 412 | The provided Totp passcode is invalid. |
|
||||
|
||||
### Validation
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 2001 | 400 | ID cannot be empty or 0. |
|
||||
| 2002 | 400 | Some of the request data was invalid. The response contains an aditional array with all invalid fields. |
|
||||
|
||||
### List
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 3001 | 404 | The list does not exist. |
|
||||
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
|
||||
| 3005 | 400 | The list title cannot be empty. |
|
||||
| 3006 | 404 | The list share does not exist. |
|
||||
| 3007 | 400 | A list with this identifier already exists. |
|
||||
| 3008 | 412 | The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list. |
|
||||
|
||||
### Task
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 4001 | 400 | The list task text cannot be empty. |
|
||||
| 4002 | 404 | The list task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
|
||||
|
@ -77,11 +52,6 @@ This document describes the different errors Vikunja can return.
|
|||
| 4015 | 404 | The task comment does not exist. |
|
||||
| 4016 | 403 | Invalid task field. |
|
||||
| 4017 | 403 | Invalid task filter comparator. |
|
||||
|
||||
### Namespace
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 5001 | 404 | The namspace does not exist. |
|
||||
| 5003 | 403 | The user does not have access to the specified namespace. |
|
||||
| 5006 | 400 | The namespace name cannot be empty. |
|
||||
|
@ -89,42 +59,15 @@ This document describes the different errors Vikunja can return.
|
|||
| 5010 | 403 | This team does not have access to that namespace. |
|
||||
| 5011 | 409 | This user has already access to that namespace. |
|
||||
| 5012 | 412 | The namespace is archived and can therefore only be accessed read only. |
|
||||
|
||||
### Team
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 6001 | 400 | The team name cannot be emtpy. |
|
||||
| 6002 | 404 | The team does not exist. |
|
||||
| 6004 | 409 | The team already has access to that namespace or list. |
|
||||
| 6005 | 409 | The user is already a member of that team. |
|
||||
| 6006 | 400 | Cannot delete the last team member. |
|
||||
| 6007 | 403 | The team does not have access to the list to perform that action. |
|
||||
|
||||
### User List Access
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 7002 | 409 | The user already has access to that list. |
|
||||
| 7003 | 403 | The user does not have access to that list. |
|
||||
|
||||
### Label
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 8001 | 403 | This label already exists on that task. |
|
||||
| 8002 | 404 | The label does not exist. |
|
||||
| 8003 | 403 | The user does not have access to this label. |
|
||||
|
||||
### Right
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 9001 | 403 | The right is invalid. |
|
||||
|
||||
### Kanban
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 10001 | 404 | The bucket does not exist. |
|
||||
| 10002 | 400 | The bucket does not belong to that list. |
|
||||
| 9001 | 403 | The right is invalid. |
|
2
docs/themes/vikunja
vendored
2
docs/themes/vikunja
vendored
|
@ -1 +1 @@
|
|||
Subproject commit cbc6a98e902516fcb0c8644283e17ebf5e74a2f3
|
||||
Subproject commit c0d3eccb16e0858c0b174865fd01dc0bd96b7618
|
25
go.mod
25
go.mod
|
@ -20,7 +20,8 @@ require (
|
|||
4d63.com/embedfiles v1.0.0 // indirect
|
||||
4d63.com/tz v1.1.0
|
||||
code.vikunja.io/web v0.0.0-20200208214421-c90649369427
|
||||
gitea.com/xorm/xorm-redis-cache v0.2.0
|
||||
gitea.com/xorm/tests v0.5.6 // indirect
|
||||
gitea.com/xorm/xorm-redis-cache v0.0.0-20191113062523-5a6a9e2ab9f2
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
|
@ -31,11 +32,14 @@ require (
|
|||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835
|
||||
github.com/go-openapi/jsonreference v0.19.3 // indirect
|
||||
github.com/go-openapi/spec v0.19.4 // indirect
|
||||
github.com/garyburd/redigo v1.6.0 // indirect
|
||||
github.com/go-openapi/spec v0.19.7 // indirect
|
||||
github.com/go-openapi/swag v0.19.8 // indirect
|
||||
github.com/go-redis/redis/v7 v7.2.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1
|
||||
github.com/go-xorm/core v0.6.2 // indirect
|
||||
github.com/go-xorm/xorm v0.7.9 // indirect
|
||||
github.com/golang/protobuf v1.3.5 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
||||
|
@ -45,8 +49,8 @@ require (
|
|||
github.com/labstack/echo/v4 v4.1.16
|
||||
github.com/labstack/gommon v0.3.0
|
||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
||||
github.com/lib/pq v1.4.0
|
||||
github.com/mailru/easyjson v0.7.0 // indirect
|
||||
github.com/lib/pq v1.3.0
|
||||
github.com/mailru/easyjson v0.7.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
|
@ -55,8 +59,7 @@ require (
|
|||
github.com/onsi/gomega v1.9.0 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
||||
github.com/pquerna/otp v1.2.0
|
||||
github.com/prometheus/client_golang v1.5.1
|
||||
github.com/prometheus/client_golang v0.9.4
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
|
@ -65,10 +68,10 @@ require (
|
|||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.6.3
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/swaggo/swag v1.6.3
|
||||
github.com/swaggo/swag v1.6.5
|
||||
github.com/ulule/limiter/v3 v3.5.0
|
||||
github.com/urfave/cli v1.22.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
|
||||
github.com/urfave/cli/v2 v2.1.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa // indirect
|
||||
|
@ -88,7 +91,7 @@ require (
|
|||
replace (
|
||||
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
|
||||
github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||
github.com/hpcloud/tail => github.com/jeffbean/tail v1.0.1 // See https://github.com/hpcloud/tail/pull/159
|
||||
github.com/hpcloud/tail => github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f // See https://github.com/hpcloud/tail/pull/159
|
||||
github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
||||
gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904
|
||||
)
|
||||
|
|
79
go.sum
79
go.sum
|
@ -13,10 +13,10 @@ code.vikunja.io/web v0.0.0-20200208214421-c90649369427 h1:6ps5r0OxZNRdmCavh1k/xM
|
|||
code.vikunja.io/web v0.0.0-20200208214421-c90649369427/go.mod h1:cuP1/ieGWAZzgQGw+QPt6Y5F0fVb/8Ol5NV4QSezGdo=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitea.com/xorm/tests v0.7.0 h1:pFcaxTGGAWw3rDuVfhBdyr+mX1uzdTtncyAKxkCQ/IE=
|
||||
gitea.com/xorm/tests v0.7.0/go.mod h1:ngmhQrSBgihBbOqw1hdReSQJAnTlbStYTn0vruUFwDc=
|
||||
gitea.com/xorm/xorm-redis-cache v0.2.0 h1:qglRHt6/7vJmDeld6j+n10M9PmruAh+Le2lgNraFu3g=
|
||||
gitea.com/xorm/xorm-redis-cache v0.2.0/go.mod h1:juYdjkmIKvLbPkdfBVKGVJ2daFQIJAgKsn4mL4ZK8Zk=
|
||||
gitea.com/xorm/tests v0.5.6 h1:bm5SwZD5B6LI4VinKf5bqOCJ8z3z5l0h43HkVuxZG3k=
|
||||
gitea.com/xorm/tests v0.5.6/go.mod h1:53b8exJwT/5JBCf5n5gMqKrIZAqIErdJCRtzS1AuiMM=
|
||||
gitea.com/xorm/xorm-redis-cache v0.0.0-20191113062523-5a6a9e2ab9f2 h1:85jEhrFlzlDrJ+CXoCQ24WtkZ7MEtt8yOYZuC9ewKyk=
|
||||
gitea.com/xorm/xorm-redis-cache v0.0.0-20191113062523-5a6a9e2ab9f2/go.mod h1:TJIwRA0Cl8Dn41dI4a3HNTq3uCBRm6ZYTxrz3Di1wuY=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
|
@ -34,7 +34,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
|
@ -45,16 +44,9 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM
|
|||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I=
|
||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
|
@ -104,7 +96,6 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6
|
|||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
|
||||
|
@ -123,10 +114,14 @@ github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k
|
|||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M=
|
||||
github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.8 h1:vfK6jLhs7OI4tAXkvkooviaE1JEPcw3mutyegLHHjmk=
|
||||
github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
|
@ -140,6 +135,12 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
|||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1 h1:SBIfzULODQQ7JV6AD931MvAz8pnkk6QCfMIcoOBDaXQ=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE=
|
||||
github.com/go-xorm/core v0.6.2 h1:EJLcSxf336POJr670wKB55Mah9f93xzvGYzNRgnT8/Y=
|
||||
github.com/go-xorm/core v0.6.2/go.mod h1:bwPIfLdm/FzWgVUH8WPVlr+uJhscvNGFcaZKXsI3n2c=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
|
||||
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
|
@ -167,9 +168,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
@ -196,8 +194,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
|||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jeffbean/tail v1.0.1 h1:mRuCwa9iq5kH1SBFAIWWFsg+NEi5+VVEf2E2ORVkKp8=
|
||||
github.com/jeffbean/tail v1.0.1/go.mod h1:+MhJ+VPZMpv8Ui6WRzpJFuWFKxBCZgVOo5HAmlw1sFc=
|
||||
github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f h1:UgkcqE2r+hkV2Pn+1JTUTaH4iEsrUFTwI2OiDOM//rE=
|
||||
github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f/go.mod h1:+MhJ+VPZMpv8Ui6WRzpJFuWFKxBCZgVOo5HAmlw1sFc=
|
||||
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591 h1:x/BpEhm6aL26o4TLtcU0loJ7B3+69jielrGc70V7Yb4=
|
||||
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
|
@ -217,7 +215,6 @@ github.com/klauspost/compress v1.9.6/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
|
|||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible h1:PkEEpmbrFXlMul8cOplR8nkcIM/NDbx+H6fq2+vaKAA=
|
||||
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible/go.mod h1:y1UhTNI4g0hVymJrI6yJ5/ohy09hNBeU8iJEZjgdDOw=
|
||||
github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 h1:j/BtLkVyqtAXvlb22nic/pSlWLuGx4gddouWVt65HDI=
|
||||
github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048/go.mod h1:dv6KyzAg9UuJWiE1pwkvvB2i0TvcQM6QhdsXLZ7K5KI=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
|
@ -244,8 +241,6 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
|||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.4.0 h1:TmtCFbH+Aw0AixwyttznSMQDgbR5Yed/Gg6S8Funrhc=
|
||||
github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
|
@ -256,6 +251,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
|
|||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
|
||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
|
@ -269,7 +266,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-oci8 v0.0.0-20191108001511-cbd8d5bc1da0/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
|
@ -323,45 +319,38 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
|
||||
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
|
||||
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=
|
||||
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shopspring/decimal v0.0.0-20191009025716-f1972eb1d1f5/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
|
@ -369,7 +358,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
|||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
|
@ -413,6 +401,10 @@ github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05
|
|||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||
github.com/swaggo/swag v1.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI=
|
||||
github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio=
|
||||
github.com/swaggo/swag v1.6.5 h1:2C+t+xyK6p1sujqncYO/VnMvPZcBJjNdKKyxbOdAW8o=
|
||||
github.com/swaggo/swag v1.6.5/go.mod h1:Y7ZLSS0d0DdxhWGVhQdu+Bu1QhaF5k0RD7FKdiAykeY=
|
||||
github.com/swaggo/swag v1.6.6-0.20200404095705-3b82b47aece6 h1:OUQztxMfijnku6tqmhbfe7+ERhDwaPhp47HW9qT3QC4=
|
||||
github.com/swaggo/swag v1.6.6-0.20200404095705-3b82b47aece6/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
|
@ -428,6 +420,7 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
|||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
|
@ -458,16 +451,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
|
||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200420192832-a76a400e3025 h1:U821GPBRQKVHpr6Os5UDBgF2FveIjlZYbz+e2n5wmmQ=
|
||||
golang.org/x/crypto v0.0.0-20200420192832-a76a400e3025/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a h1:y6sBfNd1b9Wy08a6K1Z1DZc4aXABUN5TKjkYhz7UKmo=
|
||||
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825 h1:dSChiwOTvzwbHFTMq2l6uRardHH7/E6SqEkqccinS/o=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
|
||||
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
@ -524,7 +507,6 @@ golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -538,7 +520,6 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -615,7 +596,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
|
@ -629,8 +609,11 @@ src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 h1:dE6ry9rVwDn3soD
|
|||
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
|
||||
src.techknowlogick.com/xormigrate v1.2.0 h1:bq9JaI48bxB+OddMghicjmV7sGmBUogJq4HmTN0DOcw=
|
||||
src.techknowlogick.com/xormigrate v1.2.0/go.mod h1:7so27LAfBRqAxbma5jKYeL4ykVG1Jhsv9ncSq1KBCs4=
|
||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
|
||||
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||
xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
func init() {
|
||||
migrateCmd.AddCommand(migrateListCmd)
|
||||
migrationRollbackCmd.Flags().StringVarP(&rollbackUntilFlag, "name", "n", "", "The id of the migration you want to roll back until.")
|
||||
_ = migrationRollbackCmd.MarkFlagRequired("name")
|
||||
migrationRollbackCmd.MarkFlagRequired("name")
|
||||
migrateCmd.AddCommand(migrationRollbackCmd)
|
||||
rootCmd.AddCommand(migrateCmd)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ type Key string
|
|||
|
||||
// These constants hold all config value keys
|
||||
const (
|
||||
// #nosec
|
||||
ServiceJWTSecret Key = `service.JWTSecret`
|
||||
ServiceInterface Key = `service.interface`
|
||||
ServiceFrontendurl Key = `service.frontendurl`
|
||||
|
@ -80,8 +79,8 @@ const (
|
|||
RedisDB Key = `redis.db`
|
||||
|
||||
LogEnabled Key = `log.enabled`
|
||||
LogErrors Key = `log.errors`
|
||||
LogStandard Key = `log.standard`
|
||||
LogLevel Key = `log.level`
|
||||
LogDatabase Key = `log.database`
|
||||
LogDatabaseLevel Key = `log.databaselevel`
|
||||
LogHTTP Key = `log.http`
|
||||
|
@ -213,10 +212,10 @@ func InitDefaultConfig() {
|
|||
RedisDB.setDefault(0)
|
||||
// Logger
|
||||
LogEnabled.setDefault(true)
|
||||
LogErrors.setDefault("stdout")
|
||||
LogStandard.setDefault("stdout")
|
||||
LogLevel.setDefault("INFO")
|
||||
LogDatabase.setDefault("off")
|
||||
LogDatabaseLevel.setDefault("WARNING")
|
||||
LogDatabaseLevel.setDefault("DEBUG")
|
||||
LogHTTP.setDefault("stdout")
|
||||
LogEcho.setDefault("off")
|
||||
LogPath.setDefault(ServiceRootpath.GetString() + "/logs")
|
||||
|
|
|
@ -70,7 +70,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
|||
}
|
||||
|
||||
engine.SetMapper(core.GonicMapper{})
|
||||
logger := log.NewXormLogger("")
|
||||
logger := log.NewXormLogger()
|
||||
engine.SetLogger(logger)
|
||||
|
||||
// Cache
|
||||
|
@ -81,7 +81,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
|||
cacher := caches.NewLRUCacher(caches.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
|
||||
engine.SetDefaultCacher(cacher)
|
||||
case "redis":
|
||||
cacher := xrc.NewRedisCacher(config.RedisEnabled.GetString(), config.RedisPassword.GetString(), xrc.DEFAULT_EXPIRATION, engine.Logger())
|
||||
cacher := xrc.NewRedisCacher(config.RedisEnabled.GetString(), config.RedisPassword.GetString(), xrc.DEFAULT_EXPIRATION, log.GetXormLoggerForRedis(logger))
|
||||
engine.SetDefaultCacher(cacher)
|
||||
default:
|
||||
log.Info("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
- id: 1
|
||||
title: testbucket1
|
||||
list_id: 1
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 2
|
||||
title: testbucket2
|
||||
list_id: 1
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 3
|
||||
title: testbucket3
|
||||
list_id: 1
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 4
|
||||
title: testbucket4 - other list
|
||||
list_id: 2
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
# The following are not or only partly owned by user 1
|
||||
- id: 5
|
||||
title: testbucket5
|
||||
list_id: 20
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 6
|
||||
title: testbucket6
|
||||
list_id: 6
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 7
|
||||
title: testbucket7
|
||||
list_id: 7
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 8
|
||||
title: testbucket8
|
||||
list_id: 8
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 9
|
||||
title: testbucket9
|
||||
list_id: 9
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 10
|
||||
title: testbucket10
|
||||
list_id: 10
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 11
|
||||
title: testbucket11
|
||||
list_id: 11
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 12
|
||||
title: testbucket13
|
||||
list_id: 12
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 13
|
||||
title: testbucket13
|
||||
list_id: 13
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 14
|
||||
title: testbucket14
|
||||
list_id: 14
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 15
|
||||
title: testbucket15
|
||||
list_id: 15
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 16
|
||||
title: testbucket16
|
||||
list_id: 16
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
||||
- id: 17
|
||||
title: testbucket17
|
||||
list_id: 17
|
||||
created_by_id: 1
|
||||
created: 1587244432
|
||||
updated: 1587244432
|
|
@ -7,7 +7,6 @@
|
|||
index: 1
|
||||
created: 1543626724
|
||||
updated: 1543626724
|
||||
bucket_id: 1
|
||||
- id: 2
|
||||
text: 'task #2 done'
|
||||
done: true
|
||||
|
@ -16,7 +15,6 @@
|
|||
index: 2
|
||||
created: 1543626724
|
||||
updated: 1543626724
|
||||
bucket_id: 1
|
||||
- id: 3
|
||||
text: 'task #3 high prio'
|
||||
done: false
|
||||
|
@ -26,7 +24,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
priority: 100
|
||||
bucket_id: 2
|
||||
- id: 4
|
||||
text: 'task #4 low prio'
|
||||
done: false
|
||||
|
@ -36,7 +33,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
priority: 1
|
||||
bucket_id: 2
|
||||
- id: 5
|
||||
text: 'task #5 higher due date'
|
||||
done: false
|
||||
|
@ -46,7 +42,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
due_date_unix: 1543636724
|
||||
bucket_id: 2
|
||||
- id: 6
|
||||
text: 'task #6 lower due date'
|
||||
done: false
|
||||
|
@ -56,7 +51,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
due_date_unix: 1543616724
|
||||
bucket_id: 3
|
||||
- id: 7
|
||||
text: 'task #7 with start date'
|
||||
done: false
|
||||
|
@ -66,7 +60,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
start_date_unix: 1544600000
|
||||
bucket_id: 3
|
||||
- id: 8
|
||||
text: 'task #8 with end date'
|
||||
done: false
|
||||
|
@ -76,7 +69,6 @@
|
|||
created: 1543626724
|
||||
updated: 1543626724
|
||||
end_date_unix: 1544700000
|
||||
bucket_id: 3
|
||||
- id: 9
|
||||
text: 'task #9 with start and end date'
|
||||
done: false
|
||||
|
|
|
@ -46,7 +46,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) {
|
|||
}
|
||||
|
||||
engine.SetMapper(core.GonicMapper{})
|
||||
logger := log.NewXormLogger("DEBUG")
|
||||
logger := log.NewXormLogger()
|
||||
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
||||
engine.SetLogger(logger)
|
||||
x = engine
|
||||
|
|
|
@ -1,300 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBucket(t *testing.T) {
|
||||
testHandler := webHandlerTest{
|
||||
user: &testuser1,
|
||||
strFunc: func() handler.CObject {
|
||||
return &models.Bucket{}
|
||||
},
|
||||
t: t,
|
||||
}
|
||||
t.Run("ReadAll", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(nil, map[string]string{"list": "1"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket1`)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket2`)
|
||||
assert.Contains(t, rec.Body.String(), `testbucket3`)
|
||||
assert.NotContains(t, rec.Body.String(), `testbucket4`) // Different List
|
||||
})
|
||||
})
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
// Check the list was loaded successfully afterwards, see testReadOneWithUser
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "1"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "12"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "13"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "14"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "15"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "16"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"bucket": "17"}, `{"title":"TestLoremIpsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"TestLoremIpsum"`)
|
||||
})
|
||||
})
|
||||
})
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "1"})
|
||||
assert.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)
|
||||
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{"bucket": "5"})
|
||||
assert.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{"bucket": "6"})
|
||||
assert.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{"bucket": "7"})
|
||||
assert.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{"bucket": "8"})
|
||||
assert.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{"bucket": "9"})
|
||||
assert.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{"bucket": "10"})
|
||||
assert.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{"bucket": "11"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "12"})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "13"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "14"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "15"})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "16"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testDeleteWithUser(nil, map[string]string{"bucket": "17"})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"message":"Successfully deleted."`)
|
||||
})
|
||||
})
|
||||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "1"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "9999"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeListDoesNotExist)
|
||||
})
|
||||
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{"list": "20"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "6"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "7"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "8"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "9"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "10"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.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{"list": "11"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceTeam readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "12"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "13"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceTeam admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "14"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
|
||||
t.Run("Shared Via NamespaceUser readonly", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "15"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser write", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "16"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
t.Run("Shared Via NamespaceUser admin", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "17"}, `{"title":"Lorem Ipsum"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"title":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -262,34 +262,34 @@ func TestLinkSharing(t *testing.T) {
|
|||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerListUserReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerListUserWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerListUserAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"userID":"user1"}`)
|
||||
assert.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 := testHandlerListUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerListUserWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerListUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerListUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
@ -354,34 +354,34 @@ func TestLinkSharing(t *testing.T) {
|
|||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerListTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"list": "1"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerListTeamWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamWrite.testCreateWithLinkShare(nil, map[string]string{"list": "2"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerListTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"list": "3"}, `{"teamID":1}`)
|
||||
assert.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 := testHandlerListTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"list": "1"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerListTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"list": "2"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerListTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"team_id":1}`)
|
||||
_, err := testHandlerListTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"list": "3"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
@ -862,34 +862,34 @@ func TestLinkSharing(t *testing.T) {
|
|||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceUserReadOnly.testCreateWithLinkShare(nil, nil, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserReadOnly.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceUserWrite.testCreateWithLinkShare(nil, nil, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserWrite.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceUserAdmin.testCreateWithLinkShare(nil, nil, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserAdmin.testCreateWithLinkShare(nil, nil, `{"userID":"user1"}`)
|
||||
assert.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 := testHandlerNamespaceUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceUserWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"user_id":"user1"}`)
|
||||
_, err := testHandlerNamespaceUserAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"userID":"user1"}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
@ -954,34 +954,34 @@ func TestLinkSharing(t *testing.T) {
|
|||
})
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Run("Shared readonly", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamReadOnly.testCreateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceTeamWrite.testCreateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamWrite.testCreateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamAdmin.testCreateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"teamID":1}`)
|
||||
assert.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 := testHandlerNamespaceTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamReadOnly.testUpdateWithLinkShare(nil, map[string]string{"namespace": "1"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared write", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamWrite.testUpdateWithLinkShare(nil, map[string]string{"namespace": "2"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
t.Run("Shared admin", func(t *testing.T) {
|
||||
_, err := testHandlerNamespaceTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"team_id":1}`)
|
||||
_, err := testHandlerNamespaceTeamAdmin.testUpdateWithLinkShare(nil, map[string]string{"namespace": "3"}, `{"teamID":1}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.(*echo.HTTPError).Message, `Forbidden`)
|
||||
})
|
||||
|
|
|
@ -95,33 +95,33 @@ func TestTaskCollection(t *testing.T) {
|
|||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
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)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":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)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
t.Run("by duedate", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date_unix"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":null,"dueDate":"2018-11-30T22:25:24Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date_unix"}, "order_by": []string{"desc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":6,"text":"task #6 lower due date`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":6,"text":"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_unix"}, "order_by": []string{"asc"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":null,"dueDate":"2018-11-30T22:25:24Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
||||
|
@ -137,10 +137,10 @@ func TestTaskCollection(t *testing.T) {
|
|||
// Invalid parameter should not sort at all
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, urlParams)
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"dueDate":1543616724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
})
|
||||
})
|
||||
t.Run("Date range", func(t *testing.T) {
|
||||
|
@ -207,7 +207,7 @@ func TestTaskCollection(t *testing.T) {
|
|||
// 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.
|
||||
assert.Equal(t, "[]\n", rec.Body.String())
|
||||
assert.Equal(t, "null\n", rec.Body.String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -270,42 +270,42 @@ func TestTaskCollection(t *testing.T) {
|
|||
t.Run("by priority", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
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)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":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)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":1,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"priority":100,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-4","index":4,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":null,"dueDate":null,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
// should equal duedate asc
|
||||
t.Run("by due_date", func(t *testing.T) {
|
||||
t.Run("by duedate", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date_unix"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":null,"dueDate":"2018-11-30T22:25:24Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
t.Run("by duedate desc", func(t *testing.T) {
|
||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date_unix"}, "order_by": []string{"desc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":6,"text":"task #6 lower due date`)
|
||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":6,"text":"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_unix"}, "order_by": []string{"asc"}}, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"done_at":null,"due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"done_at":null,"due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"priority":0,"start_date":null,"end_date":null,"assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":null,"dueDate":"2018-11-30T22:25:24Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":null,"dueDate":"2018-12-01T03:58:44Z","reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":null,"endDate":null,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","createdBy":{"id":1,"username":"user1","created":null,"updated":null}}]`)
|
||||
})
|
||||
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)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`)
|
||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"dueDate":1543616724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
|
||||
})
|
||||
})
|
||||
t.Run("Date range", func(t *testing.T) {
|
||||
|
@ -372,7 +372,7 @@ func TestTaskCollection(t *testing.T) {
|
|||
// 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.
|
||||
assert.Equal(t, "[]\n", rec.Body.String())
|
||||
assert.Equal(t, "null\n", rec.Body.String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -67,46 +67,46 @@ func TestTask(t *testing.T) {
|
|||
assert.NotContains(t, rec.Body.String(), `"done":true`)
|
||||
})
|
||||
t.Run("Due date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"due_date": "2020-02-10T10:00:00Z"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"dueDate": "2020-02-10T10:00:00Z"}`)
|
||||
assert.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`)
|
||||
assert.Contains(t, rec.Body.String(), `"dueDate":"2020-02-10T10:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"dueDate":0`)
|
||||
})
|
||||
t.Run("Due date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "5"}, `{"due_date": null}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "5"}, `{"dueDate": null}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"due_date":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
||||
assert.Contains(t, rec.Body.String(), `"dueDate":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"dueDate":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Reminders", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"reminder_dates": ["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"reminderDates": ["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates": null`)
|
||||
assert.Contains(t, rec.Body.String(), `"reminderDates":["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminderDates": null`)
|
||||
})
|
||||
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminder_dates": []}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminderDates": []}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
||||
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
||||
})
|
||||
t.Run("Reminders unset to null", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminder_dates": null}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "27"}, `{"reminderDates": null}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
||||
assert.Contains(t, rec.Body.String(), `"reminderDates":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"reminderDates":[1543626724,1543626824]`)
|
||||
})
|
||||
t.Run("Repeat after", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"repeat_after":3600}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"repeatAfter":3600}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"repeat_after":3600`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeat_after":0`)
|
||||
assert.Contains(t, rec.Body.String(), `"repeatAfter":3600`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeatAfter":0`)
|
||||
})
|
||||
t.Run("Repeat after unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "28"}, `{"repeat_after":0}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "28"}, `{"repeatAfter":0}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"repeat_after":0`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeat_after":3600`)
|
||||
assert.Contains(t, rec.Body.String(), `"repeatAfter":0`)
|
||||
assert.NotContains(t, rec.Body.String(), `"repeatAfter":3600`)
|
||||
})
|
||||
t.Run("Repeat after update done", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "28"}, `{"done":true}`)
|
||||
|
@ -145,52 +145,52 @@ func TestTask(t *testing.T) {
|
|||
assert.NotContains(t, rec.Body.String(), `"priority":100`)
|
||||
})
|
||||
t.Run("Start date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"start_date":"2020-02-10T10:00:00Z"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"startDate":"2020-02-10T10:00:00Z"}`)
|
||||
assert.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`)
|
||||
assert.Contains(t, rec.Body.String(), `"startDate":"2020-02-10T10:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"startDate":0`)
|
||||
})
|
||||
t.Run("Start date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "7"}, `{"start_date":null}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "7"}, `{"startDate":null}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"start_date":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
||||
assert.Contains(t, rec.Body.String(), `"startDate":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"startDate":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("End date", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"end_date":"2020-02-10T12:00:00Z"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"endDate":"2020-02-10T12:00:00Z"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"end_date":"2020-02-10T12:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"endDate":"2020-02-10T12:00:00Z"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"endDate":""`)
|
||||
})
|
||||
t.Run("End date unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "8"}, `{"end_date":null}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "8"}, `{"endDate":null}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"end_date":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
||||
assert.Contains(t, rec.Body.String(), `"endDate":null`)
|
||||
assert.NotContains(t, rec.Body.String(), `"endDate":"2020-02-10T10:00:00Z"`)
|
||||
})
|
||||
t.Run("Color", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"hex_color":"f0f0f0"}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"hexColor":"f0f0f0"}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hex_color":"f0f0f0"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hex_color":""`)
|
||||
assert.Contains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hexColor":""`)
|
||||
})
|
||||
t.Run("Color unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "31"}, `{"hex_color":""}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "31"}, `{"hexColor":""}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"hex_color":""`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hex_color":"f0f0f0"`)
|
||||
assert.Contains(t, rec.Body.String(), `"hexColor":""`)
|
||||
assert.NotContains(t, rec.Body.String(), `"hexColor":"f0f0f0"`)
|
||||
})
|
||||
t.Run("Percent Done", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"percent_done":0.1}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"percentDone":0.1}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"percent_done":0.1`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percent_done":0,`)
|
||||
assert.Contains(t, rec.Body.String(), `"percentDone":0.1`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percentDone":0,`)
|
||||
})
|
||||
t.Run("Percent Done unset", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "33"}, `{"percent_done":0}`)
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "33"}, `{"percentDone":0}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"percent_done":0,`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percent_done":0.1`)
|
||||
assert.Contains(t, rec.Body.String(), `"percentDone":0,`)
|
||||
assert.NotContains(t, rec.Body.String(), `"percentDone":0.1`)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -269,42 +269,6 @@ func TestTask(t *testing.T) {
|
|||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Move to other list", func(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"list_id":7}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"list_id":7`)
|
||||
assert.NotContains(t, rec.Body.String(), `"list_id":1`)
|
||||
})
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"list_id":20}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrorCodeGenericForbidden)
|
||||
})
|
||||
t.Run("Read Only", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"list_id":6}`)
|
||||
assert.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{"listtask": "1"}, `{"bucket_id":2}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"bucket_id":2`)
|
||||
assert.NotContains(t, rec.Body.String(), `"bucket_id":1`)
|
||||
})
|
||||
t.Run("Different List", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"bucket_id":4}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotBelongToList)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testUpdateWithUser(nil, map[string]string{"listtask": "1"}, `{"bucket_id":9999}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
})
|
||||
})
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
|
@ -470,23 +434,5 @@ func TestTask(t *testing.T) {
|
|||
assert.Contains(t, rec.Body.String(), `"text":"Lorem Ipsum"`)
|
||||
})
|
||||
})
|
||||
t.Run("Bucket", func(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
rec, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum","bucket_id":2}`)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, rec.Body.String(), `"bucket_id":2`)
|
||||
assert.NotContains(t, rec.Body.String(), `"bucket_id":1`)
|
||||
})
|
||||
t.Run("Different List", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum","bucket_id":4}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotBelongToList)
|
||||
})
|
||||
t.Run("Nonexisting Bucket", func(t *testing.T) {
|
||||
_, err := testHandler.testCreateWithUser(nil, map[string]string{"list": "1"}, `{"text":"Lorem Ipsum","bucket_id":9999}`)
|
||||
assert.Error(t, err)
|
||||
assertHandlerErrorCode(t, err, models.ErrCodeBucketDoesNotExist)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
"github.com/op/go-logging"
|
||||
"github.com/spf13/viper"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -35,15 +35,14 @@ const WebFmt = `${time_rfc3339_nano}: WEB ` + "\t" + `▶ ${remote_ip} ${id} ${m
|
|||
// Fmt is the general log format
|
||||
const Fmt = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ %{shortpkg}/%{shortfunc} %{id:03x}%{color:reset} %{message}`
|
||||
|
||||
const logModule = `vikunja`
|
||||
|
||||
// loginstance is the instance of the logger which is used under the hood to log
|
||||
var logInstance = logging.MustGetLogger(logModule)
|
||||
var logInstance = logging.MustGetLogger("vikunja")
|
||||
|
||||
// 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.LogErrors.Set("off")
|
||||
config.LogStandard.Set("off")
|
||||
config.LogDatabase.Set("off")
|
||||
config.LogHTTP.Set("off")
|
||||
|
@ -54,30 +53,36 @@ func InitLogger() {
|
|||
// This show correct caller functions
|
||||
logInstance.ExtraCalldepth = 1
|
||||
|
||||
if config.LogStandard.GetString() == "file" {
|
||||
if config.LogErrors.GetString() == "file" || 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())
|
||||
log.Fatal("Could not create log folder: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var logBackends []logging.Backend
|
||||
|
||||
// We define our two backends
|
||||
if config.LogStandard.GetString() != "off" {
|
||||
stdWriter := GetLogWriter("standard")
|
||||
stdBackend := logging.NewLogBackend(stdWriter, "", 0)
|
||||
|
||||
level, err := logging.LogLevel(strings.ToUpper(config.LogLevel.GetString()))
|
||||
if err != nil {
|
||||
Fatalf("Error setting database log level: %s", err.Error())
|
||||
}
|
||||
|
||||
logBackend := logging.NewLogBackend(stdWriter, "", 0)
|
||||
backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(Fmt+"\n"))
|
||||
|
||||
backendLeveled := logging.AddModuleLevel(backend)
|
||||
backendLeveled.SetLevel(level, logModule)
|
||||
|
||||
logInstance.SetBackend(backendLeveled)
|
||||
// Set the standard backend
|
||||
logBackends = append(logBackends, logging.NewBackendFormatter(stdBackend, logging.MustStringFormatter(Fmt+"\n")))
|
||||
}
|
||||
|
||||
if config.LogErrors.GetString() != "off" {
|
||||
errWriter := GetLogWriter("error")
|
||||
errBackend := logging.NewLogBackend(errWriter, "", 0)
|
||||
|
||||
// Only warnings and more severe messages should go to the error backend
|
||||
errBackendLeveled := logging.AddModuleLevel(errBackend)
|
||||
errBackendLeveled.SetLevel(logging.WARNING, "")
|
||||
logBackends = append(logBackends, errBackendLeveled)
|
||||
}
|
||||
|
||||
// Set our backends
|
||||
logging.SetBackend(logBackends...)
|
||||
}
|
||||
|
||||
// GetLogWriter returns the writer to where the normal log goes, depending on the config
|
||||
|
@ -85,10 +90,9 @@ func GetLogWriter(logfile string) (writer io.Writer) {
|
|||
writer = os.Stdout // Set the default case to prevent nil pointer panics
|
||||
switch viper.GetString("log." + logfile) {
|
||||
case "file":
|
||||
fullLogFilePath := config.LogPath.GetString() + "/" + logfile + ".log"
|
||||
f, err := os.OpenFile(fullLogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
f, err := os.OpenFile(config.LogPath.GetString()+"/"+logfile+".log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
Fatalf("Could not create logfile %s: %s", fullLogFilePath, err.Error())
|
||||
log.Fatal(err)
|
||||
}
|
||||
writer = f
|
||||
case "stderr":
|
||||
|
@ -110,7 +114,7 @@ func GetLogger() *logging.Logger {
|
|||
|
||||
// Debug is for debug messages
|
||||
func Debug(args ...interface{}) {
|
||||
logInstance.Debug(args...)
|
||||
logInstance.Debug(args)
|
||||
}
|
||||
|
||||
// Debugf is for debug messages
|
||||
|
@ -120,7 +124,7 @@ func Debugf(format string, args ...interface{}) {
|
|||
|
||||
// Info is for info messages
|
||||
func Info(args ...interface{}) {
|
||||
logInstance.Info(args...)
|
||||
logInstance.Info(args)
|
||||
}
|
||||
|
||||
// Infof is for info messages
|
||||
|
@ -130,7 +134,7 @@ func Infof(format string, args ...interface{}) {
|
|||
|
||||
// Error is for error messages
|
||||
func Error(args ...interface{}) {
|
||||
logInstance.Error(args...)
|
||||
logInstance.Error(args)
|
||||
}
|
||||
|
||||
// Errorf is for error messages
|
||||
|
@ -140,7 +144,7 @@ func Errorf(format string, args ...interface{}) {
|
|||
|
||||
// Warning is for warning messages
|
||||
func Warning(args ...interface{}) {
|
||||
logInstance.Warning(args...)
|
||||
logInstance.Warning(args)
|
||||
}
|
||||
|
||||
// Warningf is for warning messages
|
||||
|
@ -150,7 +154,7 @@ func Warningf(format string, args ...interface{}) {
|
|||
|
||||
// Critical is for critical messages
|
||||
func Critical(args ...interface{}) {
|
||||
logInstance.Critical(args...)
|
||||
logInstance.Critical(args)
|
||||
}
|
||||
|
||||
// Criticalf is for critical messages
|
||||
|
@ -160,7 +164,7 @@ func Criticalf(format string, args ...interface{}) {
|
|||
|
||||
// Fatal is for fatal messages
|
||||
func Fatal(args ...interface{}) {
|
||||
logInstance.Fatal(args...)
|
||||
logInstance.Fatal(args)
|
||||
}
|
||||
|
||||
// Fatalf is for fatal messages
|
||||
|
|
|
@ -19,8 +19,8 @@ package log
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/op/go-logging"
|
||||
"strings"
|
||||
"time"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm/log"
|
||||
)
|
||||
|
||||
|
@ -37,11 +37,8 @@ type XormLogger struct {
|
|||
}
|
||||
|
||||
// NewXormLogger creates and initializes a new xorm logger
|
||||
func NewXormLogger(lvl string) *XormLogger {
|
||||
if lvl == "" {
|
||||
lvl = strings.ToUpper(config.LogDatabaseLevel.GetString())
|
||||
}
|
||||
level, err := logging.LogLevel(lvl)
|
||||
func NewXormLogger() *XormLogger {
|
||||
level, err := logging.LogLevel(config.LogDatabaseLevel.GetString())
|
||||
if err != nil {
|
||||
Critical("Error setting database log level: %s", err.Error())
|
||||
}
|
||||
|
@ -139,3 +136,53 @@ func (x *XormLogger) ShowSQL(show ...bool) {
|
|||
func (x *XormLogger) IsShowSQL() bool {
|
||||
return x.showSQL
|
||||
}
|
||||
|
||||
// XormRedisCacherLogger is used as a compatibility layer to be able to re-use the same logger from xorm until
|
||||
// the redis cacher module accepts the same logger.
|
||||
// See https://gitea.com/xorm/xorm-redis-cache/issues/10
|
||||
type XormRedisCacherLogger struct {
|
||||
XormLogger
|
||||
}
|
||||
|
||||
// GetXormLoggerForRedis creates a new xorm logger which can be used with redis
|
||||
func GetXormLoggerForRedis(x *XormLogger) *XormRedisCacherLogger {
|
||||
return &XormRedisCacherLogger{*x}
|
||||
}
|
||||
|
||||
// Level returns the currently set log level
|
||||
func (x *XormRedisCacherLogger) Level() core.LogLevel {
|
||||
switch x.level {
|
||||
case log.LOG_DEBUG:
|
||||
return core.LOG_DEBUG
|
||||
case log.LOG_INFO:
|
||||
return core.LOG_INFO
|
||||
case log.LOG_WARNING:
|
||||
return core.LOG_WARNING
|
||||
case log.LOG_ERR:
|
||||
return core.LOG_ERR
|
||||
case log.LOG_OFF:
|
||||
return core.LOG_OFF
|
||||
case log.LOG_UNKNOWN:
|
||||
return core.LOG_UNKNOWN
|
||||
default:
|
||||
return core.LOG_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
// SetLevel sets the log level
|
||||
func (x *XormRedisCacherLogger) SetLevel(l core.LogLevel) {
|
||||
switch l {
|
||||
case core.LOG_DEBUG:
|
||||
x.level = log.LOG_DEBUG
|
||||
case core.LOG_INFO:
|
||||
x.level = log.LOG_INFO
|
||||
case core.LOG_WARNING:
|
||||
x.level = log.LOG_WARNING
|
||||
case core.LOG_ERR:
|
||||
x.level = log.LOG_ERR
|
||||
case core.LOG_OFF:
|
||||
x.level = log.LOG_OFF
|
||||
default:
|
||||
x.level = log.LOG_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ func StartMailDaemon() {
|
|||
|
||||
go func() {
|
||||
d := gomail.NewDialer(config.MailerHost.GetString(), config.MailerPort.GetInt(), config.MailerUsername.GetString(), config.MailerPassword.GetString())
|
||||
// #nosec
|
||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: config.MailerSkipTLSVerify.GetBool()}
|
||||
|
||||
var s gomail.SendCloser
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
)
|
||||
|
||||
type listTask20190430111111 struct {
|
||||
HexColor string `xorm:"varchar(6) null" json:"hex_color" valid:"runelength(0|6)" maxLength:"6"`
|
||||
HexColor string `xorm:"varchar(6) null" json:"hexColor" valid:"runelength(0|6)" maxLength:"6"`
|
||||
}
|
||||
|
||||
func (listTask20190430111111) TableName() string {
|
||||
|
|
|
@ -27,21 +27,21 @@ type listTask20190511202210 struct {
|
|||
Text string `xorm:"varchar(250) not null" json:"text" valid:"runelength(3|250)" minLength:"3" maxLength:"250"`
|
||||
Description string `xorm:"varchar(250)" json:"description" valid:"runelength(0|250)" maxLength:"250"`
|
||||
Done bool `xorm:"INDEX null" json:"done"`
|
||||
DoneAtUnix int64 `xorm:"INDEX null" json:"done_at"`
|
||||
DueDateUnix int64 `xorm:"int(11) INDEX null" json:"due_date"`
|
||||
RemindersUnix []int64 `xorm:"JSON TEXT null" json:"reminder_dates"`
|
||||
DoneAtUnix int64 `xorm:"INDEX null" json:"doneAt"`
|
||||
DueDateUnix int64 `xorm:"int(11) INDEX null" json:"dueDate"`
|
||||
RemindersUnix []int64 `xorm:"JSON TEXT null" json:"reminderDates"`
|
||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"` // ID of the user who put that task on the list
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"list_id" param:"list"`
|
||||
RepeatAfter int64 `xorm:"int(11) INDEX null" json:"repeat_after"`
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"listID" param:"list"`
|
||||
RepeatAfter int64 `xorm:"int(11) INDEX null" json:"repeatAfter"`
|
||||
ParentTaskID int64 `xorm:"int(11) INDEX null" json:"parentTaskID"`
|
||||
Priority int64 `xorm:"int(11) null" json:"priority"`
|
||||
StartDateUnix int64 `xorm:"int(11) INDEX null" json:"start_date" query:"-"`
|
||||
EndDateUnix int64 `xorm:"int(11) INDEX null" json:"end_date" query:"-"`
|
||||
HexColor string `xorm:"varchar(6) null" json:"hex_color" valid:"runelength(0|6)" maxLength:"6"`
|
||||
StartDateUnix int64 `xorm:"int(11) INDEX null" json:"startDate" query:"-"`
|
||||
EndDateUnix int64 `xorm:"int(11) INDEX null" json:"endDate" query:"-"`
|
||||
HexColor string `xorm:"varchar(6) null" json:"hexColor" valid:"runelength(0|6)" maxLength:"6"`
|
||||
UID string `xorm:"varchar(250) null" json:"-"`
|
||||
Sorting string `xorm:"-" json:"-" query:"sort"` // Parameter to sort by
|
||||
StartDateSortUnix int64 `xorm:"-" json:"-" query:"start_date"`
|
||||
EndDateSortUnix int64 `xorm:"-" json:"-" query:"end_date"`
|
||||
StartDateSortUnix int64 `xorm:"-" json:"-" query:"startdate"`
|
||||
EndDateSortUnix int64 `xorm:"-" json:"-" query:"enddate"`
|
||||
Created int64 `xorm:"created not null" json:"created"`
|
||||
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
)
|
||||
|
||||
type listTask20190514192749 struct {
|
||||
DoneAtUnix int64 `xorm:"INDEX null" json:"done_at"`
|
||||
DoneAtUnix int64 `xorm:"INDEX null" json:"doneAt"`
|
||||
}
|
||||
|
||||
func (listTask20190514192749) TableName() string {
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
)
|
||||
|
||||
type task20190920185205 struct {
|
||||
PercentDone float64 `xorm:"DOUBLE null" json:"percent_done"`
|
||||
PercentDone float64 `xorm:"DOUBLE null" json:"percentDone"`
|
||||
}
|
||||
|
||||
func (task20190920185205) TableName() string {
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
type task20191207220736 struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
||||
Index int64 `xorm:"int(11) not null default 0" json:"index"`
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"list_id" param:"list"`
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"listID" param:"list"`
|
||||
}
|
||||
|
||||
func (task20191207220736) TableName() string {
|
||||
|
|
|
@ -41,4 +41,5 @@ func init() {
|
|||
return tx.DropTables(list20200322214624{})
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type totp20200417175201 struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"-"`
|
||||
UserID int64 `xorm:"int(11) not null" json:"-"`
|
||||
Secret string `xorm:"varchar(20) not null" json:"secret"`
|
||||
Enabled bool `xorm:"null" json:"enabled"`
|
||||
URL string `xorm:"text null" json:"url"`
|
||||
}
|
||||
|
||||
func (t totp20200417175201) TableName() string {
|
||||
return "totp"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20200417175201",
|
||||
Description: "Add totp table",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(totp20200417175201{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(totp20200417175201{})
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type task20200418230432 struct {
|
||||
BucketID int64 `xorm:"int(11) null"`
|
||||
}
|
||||
|
||||
func (s task20200418230432) TableName() string {
|
||||
return "tasks"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20200418230432",
|
||||
Description: "Add bucket id property to task",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(task20200418230432{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(task20200418230432{})
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/timeutil"
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type bucket20200418230605 struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk"`
|
||||
Title string `xorm:"text not null"`
|
||||
ListID int64 `xorm:"int(11) not null"`
|
||||
Created timeutil.TimeStamp `xorm:"created not null"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated not null"`
|
||||
CreatedByID int64 `xorm:"int(11) not null"`
|
||||
}
|
||||
|
||||
func (b *bucket20200418230605) TableName() string {
|
||||
return "buckets"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20200418230605",
|
||||
Description: "Add bucket table",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(bucket20200418230605{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(bucket20200418230605{})
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"math"
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type task20200420215928 struct {
|
||||
Position float64 `xorm:"double null" json:"position"`
|
||||
}
|
||||
|
||||
func (s task20200420215928) TableName() string {
|
||||
return "tasks"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20200420215928",
|
||||
Description: "Add position property to task",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
err := tx.Sync2(task20200420215928{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a position according to their id -> gives a starting position
|
||||
tasks := []*models.Task{}
|
||||
err = tx.Find(&tasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, task := range tasks {
|
||||
task.Position = float64(task.ID) * math.Pow(2, 16)
|
||||
_, err = tx.Where("id = ?", task.ID).Update(task)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.DropTables(task20200420215928{})
|
||||
},
|
||||
})
|
||||
}
|
|
@ -73,8 +73,8 @@ func (bt *BulkTask) CanUpdate(a web.Auth) (bool, error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param task body models.BulkTask true "The task object. Looks like a normal task, the only difference is it uses an array of list_ids to update."
|
||||
// @Success 200 {object} models.Task "The updated task object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the task (aka its list)"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the task (aka its list)"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/bulk [post]
|
||||
func (bt *BulkTask) Update() (err error) {
|
||||
|
|
|
@ -573,7 +573,7 @@ func (err ErrTaskAttachmentIsTooLarge) HTTPError() web.HTTPError {
|
|||
|
||||
// ErrInvalidSortParam represents an error where the provided sort param is invalid
|
||||
type ErrInvalidSortParam struct {
|
||||
SortBy string
|
||||
SortBy sortProperty
|
||||
}
|
||||
|
||||
// IsErrInvalidSortParam checks if an error is ErrInvalidSortParam.
|
||||
|
@ -1178,7 +1178,7 @@ func IsErrInvalidRight(err error) bool {
|
|||
}
|
||||
|
||||
func (err ErrInvalidRight) Error() string {
|
||||
return fmt.Sprintf("Right invalid [Right: %d]", err.Right)
|
||||
return fmt.Sprintf(" right invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidRight holds the unique world-error code of this error
|
||||
|
@ -1192,62 +1192,3 @@ func (err ErrInvalidRight) HTTPError() web.HTTPError {
|
|||
Message: "The right is invalid.",
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Kanban
|
||||
// ========
|
||||
|
||||
// ErrBucketDoesNotExist represents an error where a kanban bucket does not exist
|
||||
type ErrBucketDoesNotExist struct {
|
||||
BucketID int64
|
||||
}
|
||||
|
||||
// IsErrBucketDoesNotExist checks if an error is ErrBucketDoesNotExist.
|
||||
func IsErrBucketDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrBucketDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBucketDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("Bucket does not exist [BucketID: %d]", err.BucketID)
|
||||
}
|
||||
|
||||
// ErrCodeBucketDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeBucketDoesNotExist = 10001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrBucketDoesNotExist) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusNotFound,
|
||||
Code: ErrCodeBucketDoesNotExist,
|
||||
Message: "This bucket does not exist.",
|
||||
}
|
||||
}
|
||||
|
||||
// ErrBucketDoesNotBelongToList represents an error where a kanban bucket does not belong to a list
|
||||
type ErrBucketDoesNotBelongToList struct {
|
||||
BucketID int64
|
||||
ListID int64
|
||||
}
|
||||
|
||||
// IsErrBucketDoesNotBelongToList checks if an error is ErrBucketDoesNotBelongToList.
|
||||
func IsErrBucketDoesNotBelongToList(err error) bool {
|
||||
_, ok := err.(ErrBucketDoesNotBelongToList)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBucketDoesNotBelongToList) Error() string {
|
||||
return fmt.Sprintf("Bucket does not not belong to list [BucketID: %d, ListID: %d]", err.BucketID, err.ListID)
|
||||
}
|
||||
|
||||
// ErrCodeBucketDoesNotBelongToList holds the unique world-error code of this error
|
||||
const ErrCodeBucketDoesNotBelongToList = 10002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrBucketDoesNotBelongToList) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
Code: ErrCodeBucketDoesNotBelongToList,
|
||||
Message: "This bucket does not belong to that list.",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/timeutil"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bucket represents a kanban bucket
|
||||
type Bucket struct {
|
||||
// The unique, numeric id of this bucket.
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"bucket"`
|
||||
// The title of this bucket.
|
||||
Title string `xorm:"text not null" valid:"required" minLength:"1" json:"title"`
|
||||
// The list this bucket belongs to.
|
||||
ListID int64 `xorm:"int(11) not null" json:"list_id" param:"list"`
|
||||
// All tasks which belong to this bucket.
|
||||
Tasks []*Task `xorm:"-" json:"tasks"`
|
||||
|
||||
// A timestamp when this bucket was created. You cannot change this value.
|
||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
||||
// A timestamp when this bucket was last updated. You cannot change this value.
|
||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
||||
|
||||
// The user who initially created the bucket.
|
||||
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
||||
|
||||
web.Rights `xorm:"-" json:"-"`
|
||||
web.CRUDable `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for this bucket.
|
||||
func (b *Bucket) TableName() string {
|
||||
return "buckets"
|
||||
}
|
||||
|
||||
func getBucketByID(id int64) (b *Bucket, err error) {
|
||||
b = &Bucket{}
|
||||
exists, err := x.Where("id = ?", id).Get(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
return b, ErrBucketDoesNotExist{BucketID: id}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadAll returns all buckets with their tasks for a certain list
|
||||
// @Summary Get all kanban buckets of a list
|
||||
// @Description Returns all kanban buckets with belong to a list including their tasks.
|
||||
// @tags task
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "List Id"
|
||||
// @Success 200 {array} models.Bucket "The buckets with their tasks"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /lists/{id}/buckets [get]
|
||||
func (b *Bucket) ReadAll(auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
||||
// Note: I'm ignoring pagination for now since I've yet to figure out a way on how to make it work
|
||||
// I'll probably just don't do it and instead make individual tasks archivable.
|
||||
|
||||
// Get all buckets for this list
|
||||
buckets := []*Bucket{
|
||||
{
|
||||
// This is the default bucket for all tasks which are not associated to a bucket.
|
||||
ID: 0,
|
||||
Title: "Not associated to a bucket",
|
||||
ListID: b.ListID,
|
||||
Created: timeutil.FromTime(time.Now()),
|
||||
Updated: timeutil.FromTime(time.Now()),
|
||||
CreatedByID: auth.GetID(),
|
||||
},
|
||||
}
|
||||
|
||||
buckets[0].CreatedBy, err = user.GetFromAuth(auth)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = x.Where("list_id = ?", b.ListID).Find(&buckets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Make a map from the bucket slice with their id as key so that we can use it to put the tasks in their buckets
|
||||
bucketMap := make(map[int64]*Bucket, len(buckets))
|
||||
userIDs := make([]int64, 0, len(buckets))
|
||||
for _, bb := range buckets {
|
||||
bucketMap[bb.ID] = bb
|
||||
userIDs = append(userIDs, bb.CreatedByID)
|
||||
}
|
||||
|
||||
// Get all users
|
||||
users := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, bb := range buckets {
|
||||
bb.CreatedBy = users[bb.CreatedByID]
|
||||
}
|
||||
|
||||
// Get all tasks for this list
|
||||
opts := &taskOptions{
|
||||
sortby: []*sortParam{
|
||||
{
|
||||
sortBy: taskPropertyPosition,
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
},
|
||||
}
|
||||
tasks, _, _, err := getTasksForLists([]*List{{ID: b.ListID}}, opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Put all tasks in their buckets
|
||||
// All tasks which are not associated to any bucket will have bucket id 0 which is the nil value for int64
|
||||
// Since we created a bucked with that id at the beginning, all tasks should be in there.
|
||||
for _, task := range tasks {
|
||||
bucketMap[task.BucketID].Tasks = append(bucketMap[task.BucketID].Tasks, task)
|
||||
}
|
||||
|
||||
return buckets, len(buckets), int64(len(buckets)), nil
|
||||
}
|
||||
|
||||
// Create creates a new bucket
|
||||
// @Summary Create a new bucket
|
||||
// @Description Creates a new kanban bucket on a list.
|
||||
// @tags task
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "List Id"
|
||||
// @Param bucket body models.Bucket true "The bucket object"
|
||||
// @Success 200 {object} models.Bucket "The created bucket object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid bucket object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/buckets [put]
|
||||
func (b *Bucket) Create(a web.Auth) (err error) {
|
||||
b.CreatedByID = a.GetID()
|
||||
|
||||
_, err = x.Insert(b)
|
||||
return
|
||||
}
|
||||
|
||||
// Update Updates an existing bucket
|
||||
// @Summary Update an existing bucket
|
||||
// @Description Updates an existing kanban bucket.
|
||||
// @tags task
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Param listID path int true "List Id"
|
||||
// @Param bucketID path int true "Bucket Id"
|
||||
// @Param bucket body models.Bucket true "The bucket object"
|
||||
// @Success 200 {object} models.Bucket "The created bucket object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid bucket object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The bucket does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/buckets/{bucketID} [post]
|
||||
func (b *Bucket) Update() (err error) {
|
||||
_, err = x.Where("id = ?", b.ID).Update(b)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes a bucket, but no tasks
|
||||
// @Summary Deletes an existing bucket
|
||||
// @Description Deletes an existing kanban bucket and dissociates all of its task. It does not delete any tasks.
|
||||
// @tags task
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Param listID path int true "List Id"
|
||||
// @Param bucketID path int true "Bucket Id"
|
||||
// @Success 200 {object} models.Message "Successfully deleted."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The bucket does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/buckets/{bucketID} [delete]
|
||||
func (b *Bucket) Delete() (err error) {
|
||||
// Remove all associations of tasks to that bucket
|
||||
_, err = x.Where("bucket_id = ?", b.ID).Cols("bucket_id").Update(&Task{BucketID: 0})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the bucket itself
|
||||
_, err = x.Where("id = ?", b.ID).Delete(&Bucket{})
|
||||
return
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
|
||||
// CanCreate checks if a user can create a new bucket
|
||||
func (b *Bucket) CanCreate(a web.Auth) (bool, error) {
|
||||
l := &List{ID: b.ListID}
|
||||
return l.CanWrite(a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if a user can update an existing bucket
|
||||
func (b *Bucket) CanUpdate(a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete an existing bucket
|
||||
func (b *Bucket) CanDelete(a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(a)
|
||||
}
|
||||
|
||||
// canDoBucket checks if the bucket exists and if the user has the right to act on it
|
||||
func (b *Bucket) canDoBucket(a web.Auth) (bool, error) {
|
||||
bb, err := getBucketByID(b.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
l := &List{ID: bb.ListID}
|
||||
return l.CanWrite(a)
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBucket_ReadAll(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
testuser := &user.User{ID: 1}
|
||||
b := &Bucket{ListID: 1}
|
||||
bucketsInterface, _, _, err := b.ReadAll(testuser, "", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buckets, is := bucketsInterface.([]*Bucket)
|
||||
assert.True(t, is)
|
||||
|
||||
// Assert that we have a user for each bucket
|
||||
assert.Equal(t, testuser.ID, buckets[0].CreatedBy.ID)
|
||||
assert.Equal(t, testuser.ID, buckets[1].CreatedBy.ID)
|
||||
assert.Equal(t, testuser.ID, buckets[2].CreatedBy.ID)
|
||||
assert.Equal(t, testuser.ID, buckets[3].CreatedBy.ID)
|
||||
|
||||
// Assert our three test buckets + one for all tasks without a bucket
|
||||
assert.Len(t, buckets, 4)
|
||||
|
||||
// Assert all tasks are in the right bucket
|
||||
assert.Len(t, buckets[0].Tasks, 10)
|
||||
assert.Len(t, buckets[1].Tasks, 2)
|
||||
assert.Len(t, buckets[2].Tasks, 3)
|
||||
assert.Len(t, buckets[3].Tasks, 3)
|
||||
|
||||
// Assert we have bucket 0, 1, 2, 3 but not 4 (that belongs to a different list)
|
||||
assert.Equal(t, int64(1), buckets[1].ID)
|
||||
assert.Equal(t, int64(2), buckets[2].ID)
|
||||
assert.Equal(t, int64(3), buckets[3].ID)
|
||||
|
||||
// Kinda assert all tasks are in the right buckets
|
||||
assert.Equal(t, int64(0), buckets[0].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(1), buckets[1].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(1), buckets[1].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[2].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[2].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[2].Tasks[2].BucketID)
|
||||
assert.Equal(t, int64(3), buckets[3].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(3), buckets[3].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(3), buckets[3].Tasks[2].BucketID)
|
||||
}
|
|
@ -60,7 +60,7 @@ func (Label) TableName() string {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param label body models.Label true "The label object"
|
||||
// @Success 200 {object} models.Label "The created label object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid label object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels [put]
|
||||
func (l *Label) Create(a web.Auth) (err error) {
|
||||
|
@ -86,9 +86,9 @@ func (l *Label) Create(a web.Auth) (err error) {
|
|||
// @Param id path int true "Label ID"
|
||||
// @Param label body models.Label true "The label object"
|
||||
// @Success 200 {object} models.Label "The created label object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid label object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to update the label."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Label not found."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to update the label."
|
||||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [put]
|
||||
func (l *Label) Update() (err error) {
|
||||
|
@ -110,8 +110,8 @@ func (l *Label) Update() (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Label ID"
|
||||
// @Success 200 {object} models.Label "The label was successfully deleted."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to delete the label."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Label not found."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to delete the label."
|
||||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [delete]
|
||||
func (l *Label) Delete() (err error) {
|
||||
|
@ -165,8 +165,8 @@ func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls in
|
|||
// @Param id path int true "Label ID"
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.Label "The label"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the label"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Label not found"
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the label"
|
||||
// @Failure 404 {object} web.HTTPError "Label not found"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [get]
|
||||
func (l *Label) ReadOne() (err error) {
|
||||
|
|
|
@ -52,8 +52,8 @@ func (LabelTask) TableName() string {
|
|||
// @Param task path int true "Task ID"
|
||||
// @Param label path int true "Label ID"
|
||||
// @Success 200 {object} models.Message "The label was successfully removed."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to remove the label."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Label not found."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to remove the label."
|
||||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels/{label} [delete]
|
||||
func (lt *LabelTask) Delete() (err error) {
|
||||
|
@ -71,9 +71,9 @@ func (lt *LabelTask) Delete() (err error) {
|
|||
// @Param task path int true "Task ID"
|
||||
// @Param label body models.LabelTask true "The label object"
|
||||
// @Success 200 {object} models.LabelTask "The created label relation object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid label object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to add the label."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The label does not exist."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to add the label."
|
||||
// @Failure 404 {object} web.HTTPError "The label does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels [put]
|
||||
func (lt *LabelTask) Create(a web.Auth) (err error) {
|
||||
|
@ -336,7 +336,7 @@ type LabelTaskBulk struct {
|
|||
// @Param label body models.LabelTaskBulk true "The array of labels"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.LabelTaskBulk "The updated labels object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid label object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/labels/bulk [post]
|
||||
func (ltb *LabelTaskBulk) Create(a web.Auth) (err error) {
|
||||
|
|
|
@ -77,7 +77,7 @@ func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error
|
|||
share = &LinkSharing{}
|
||||
share.ID = int64(claims["id"].(float64))
|
||||
share.Hash = claims["hash"].(string)
|
||||
share.ListID = int64(claims["list_id"].(float64))
|
||||
share.ListID = int64(claims["listID"].(float64))
|
||||
share.Right = Right(claims["right"].(float64))
|
||||
share.SharedByID = int64(claims["sharedByID"].(float64))
|
||||
return
|
||||
|
@ -93,9 +93,9 @@ func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error
|
|||
// @Param list path int true "List ID"
|
||||
// @Param label body models.LinkSharing true "The new link share object"
|
||||
// @Success 200 {object} models.LinkSharing "The created link share object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid link share object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to add the list share."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The list does not exist."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid link share object provided."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to add the list share."
|
||||
// @Failure 404 {object} web.HTTPError "The list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares [put]
|
||||
func (share *LinkSharing) Create(a web.Auth) (err error) {
|
||||
|
@ -116,8 +116,8 @@ func (share *LinkSharing) Create(a web.Auth) (err error) {
|
|||
// @Param share path int true "Share ID"
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.LinkSharing "The share links"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No access to the list"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Share Link not found."
|
||||
// @Failure 403 {object} web.HTTPError "No access to the list"
|
||||
// @Failure 404 {object} web.HTTPError "Share Link not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares/{share} [get]
|
||||
func (share *LinkSharing) ReadOne() (err error) {
|
||||
|
@ -205,8 +205,8 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
// @Param list path int true "List ID"
|
||||
// @Param share path int true "Share Link ID"
|
||||
// @Success 200 {object} models.Message "The link was successfully removed."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to remove the link."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Share Link not found."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to remove the link."
|
||||
// @Failure 404 {object} web.HTTPError "Share Link not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares/{share} [delete]
|
||||
func (share *LinkSharing) Delete() (err error) {
|
||||
|
|
|
@ -103,7 +103,7 @@ func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error
|
|||
// @Param is_archived query bool false "If true, also returns all archived lists."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.List "The lists"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists [get]
|
||||
func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
|
@ -145,7 +145,7 @@ func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "List ID"
|
||||
// @Success 200 {object} models.List "The list"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [get]
|
||||
func (l *List) ReadOne() (err error) {
|
||||
|
@ -425,8 +425,8 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
// @Param id path int true "List ID"
|
||||
// @Param list body models.List true "The list with updated values you want to update."
|
||||
// @Success 200 {object} models.List "The updated list."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [post]
|
||||
func (l *List) Update() (err error) {
|
||||
|
@ -458,8 +458,8 @@ func updateListByTaskID(taskID int64) (err error) {
|
|||
// @Param namespaceID path int true "Namespace ID"
|
||||
// @Param list body models.List true "The list you want to create."
|
||||
// @Success 200 {object} models.List "The created list."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/lists [put]
|
||||
func (l *List) Create(a web.Auth) (err error) {
|
||||
|
@ -488,8 +488,8 @@ func (l *List) Create(a web.Auth) (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "List ID"
|
||||
// @Success 200 {object} models.Message "The list was successfully deleted."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid list object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [delete]
|
||||
func (l *List) Delete() (err error) {
|
||||
|
|
|
@ -26,7 +26,7 @@ type TeamList struct {
|
|||
// The unique, numeric id of this list <-> team relation.
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||
// The team id.
|
||||
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
|
||||
TeamID int64 `xorm:"int(11) not null INDEX" json:"teamID" param:"team"`
|
||||
// The list id.
|
||||
ListID int64 `xorm:"int(11) not null INDEX" json:"-" param:"list"`
|
||||
// The right this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
|
@ -62,9 +62,9 @@ type TeamWithRight struct {
|
|||
// @Param id path int true "List ID"
|
||||
// @Param list body models.TeamList true "The team you want to add to the list."
|
||||
// @Success 200 {object} models.TeamList "The created team<->list relation."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid team list object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The team does not exist."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid team list object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The team does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/teams [put]
|
||||
func (tl *TeamList) Create(a web.Auth) (err error) {
|
||||
|
@ -116,8 +116,8 @@ func (tl *TeamList) Create(a web.Auth) (err error) {
|
|||
// @Param listID path int true "List ID"
|
||||
// @Param teamID path int true "Team ID"
|
||||
// @Success 200 {object} models.Message "The team was successfully deleted."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Team or list does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 404 {object} web.HTTPError "Team or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/teams/{teamID} [delete]
|
||||
func (tl *TeamList) Delete() (err error) {
|
||||
|
@ -162,7 +162,7 @@ func (tl *TeamList) Delete() (err error) {
|
|||
// @Param s query string false "Search teams by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.TeamWithRight "The teams with their right."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the list."
|
||||
// @Failure 403 {object} web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/teams [get]
|
||||
func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
|
@ -227,8 +227,8 @@ func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Param list body models.TeamList true "The team you want to update."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.TeamList "The updated team <-> list relation."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have admin-access to the list"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Team or list does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have admin-access to the list"
|
||||
// @Failure 404 {object} web.HTTPError "Team or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/teams/{teamID} [post]
|
||||
func (tl *TeamList) Update() (err error) {
|
||||
|
|
|
@ -27,7 +27,7 @@ type ListUser struct {
|
|||
// The unique, numeric id of this list <-> user relation.
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||
// The username.
|
||||
Username string `xorm:"-" json:"user_id" param:"user"`
|
||||
Username string `xorm:"-" json:"userID" param:"user"`
|
||||
// Used internally to reference the user
|
||||
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||
// The list id.
|
||||
|
@ -65,9 +65,9 @@ type UserWithRight struct {
|
|||
// @Param id path int true "List ID"
|
||||
// @Param list body models.ListUser true "The user you want to add to the list."
|
||||
// @Success 200 {object} models.ListUser "The created user<->list relation."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid user list object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The user does not exist."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid user list object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The user does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/users [put]
|
||||
func (lu *ListUser) Create(a web.Auth) (err error) {
|
||||
|
@ -123,8 +123,8 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
|||
// @Param listID path int true "List ID"
|
||||
// @Param userID path int true "User ID"
|
||||
// @Success 200 {object} models.Message "The user was successfully removed from the list."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "user or list does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 404 {object} web.HTTPError "user or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/users/{userID} [delete]
|
||||
func (lu *ListUser) Delete() (err error) {
|
||||
|
@ -168,7 +168,7 @@ func (lu *ListUser) Delete() (err error) {
|
|||
// @Param s query string false "Search users by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.UserWithRight "The users with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the list."
|
||||
// @Failure 403 {object} web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/users [get]
|
||||
func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
@ -223,8 +223,8 @@ func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Param list body models.ListUser true "The user you want to update."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.ListUser "The updated user <-> list relation."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have admin-access to the list"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User or list does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have admin-access to the list"
|
||||
// @Failure 404 {object} web.HTTPError "User or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/users/{userID} [post]
|
||||
func (lu *ListUser) Update() (err error) {
|
||||
|
|
|
@ -51,7 +51,6 @@ func GetTables() []interface{} {
|
|||
&TaskRelation{},
|
||||
&TaskAttachment{},
|
||||
&TaskComment{},
|
||||
&Bucket{},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ func (n *Namespace) CheckIsArchived() error {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Namespace ID"
|
||||
// @Success 200 {object} models.Namespace "The Namespace"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to that namespace."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to that namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id} [get]
|
||||
func (n *Namespace) ReadOne() (err error) {
|
||||
|
@ -335,8 +335,8 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Security JWTKeyAuth
|
||||
// @Param namespace body models.Namespace true "The namespace you want to create."
|
||||
// @Success 200 {object} models.Namespace "The created namespace."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces [put]
|
||||
func (n *Namespace) Create(a web.Auth) (err error) {
|
||||
|
@ -370,8 +370,8 @@ func (n *Namespace) Create(a web.Auth) (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Namespace ID"
|
||||
// @Success 200 {object} models.Message "The namespace was successfully deleted."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id} [delete]
|
||||
func (n *Namespace) Delete() (err error) {
|
||||
|
@ -428,8 +428,8 @@ func (n *Namespace) Delete() (err error) {
|
|||
// @Param id path int true "Namespace ID"
|
||||
// @Param namespace body models.Namespace true "The namespace with updated values you want to update."
|
||||
// @Success 200 {object} models.Namespace "The updated namespace."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid namespace object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespace/{id} [post]
|
||||
func (n *Namespace) Update() (err error) {
|
||||
|
|
|
@ -26,7 +26,7 @@ type TeamNamespace struct {
|
|||
// The unique, numeric id of this namespace <-> team relation.
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||
// The team id.
|
||||
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
|
||||
TeamID int64 `xorm:"int(11) not null INDEX" json:"teamID" param:"team"`
|
||||
// The namespace id.
|
||||
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
||||
// The right this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
|
@ -56,9 +56,9 @@ func (TeamNamespace) TableName() string {
|
|||
// @Param id path int true "Namespace ID"
|
||||
// @Param namespace body models.TeamNamespace true "The team you want to add to the namespace."
|
||||
// @Success 200 {object} models.TeamNamespace "The created team<->namespace relation."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid team namespace object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The team does not exist."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The team does not have access to the namespace"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid team namespace object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The team does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The team does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/teams [put]
|
||||
func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
||||
|
@ -105,8 +105,8 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
|||
// @Param namespaceID path int true "Namespace ID"
|
||||
// @Param teamID path int true "team ID"
|
||||
// @Success 200 {object} models.Message "The team was successfully deleted."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The team does not have access to the namespace"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "team or namespace does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The team does not have access to the namespace"
|
||||
// @Failure 404 {object} web.HTTPError "team or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/teams/{teamID} [delete]
|
||||
func (tn *TeamNamespace) Delete() (err error) {
|
||||
|
@ -147,7 +147,7 @@ func (tn *TeamNamespace) Delete() (err error) {
|
|||
// @Param s query string false "Search teams by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.TeamWithRight "The teams with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the namespace."
|
||||
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/teams [get]
|
||||
func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
@ -208,8 +208,8 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
// @Param namespace body models.TeamNamespace true "The team you want to update."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.TeamNamespace "The updated team <-> namespace relation."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The team does not have admin-access to the namespace"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "Team or namespace does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The team does not have admin-access to the namespace"
|
||||
// @Failure 404 {object} web.HTTPError "Team or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/teams/{teamID} [post]
|
||||
func (tn *TeamNamespace) Update() (err error) {
|
||||
|
|
|
@ -27,7 +27,7 @@ type NamespaceUser struct {
|
|||
// The unique, numeric id of this namespace <-> user relation.
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||
// The username.
|
||||
Username string `xorm:"-" json:"user_id" param:"user"`
|
||||
Username string `xorm:"-" json:"userID" param:"user"`
|
||||
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||
// The namespace id
|
||||
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
||||
|
@ -58,9 +58,9 @@ func (NamespaceUser) TableName() string {
|
|||
// @Param id path int true "Namespace ID"
|
||||
// @Param namespace body models.NamespaceUser true "The user you want to add to the namespace."
|
||||
// @Success 200 {object} models.NamespaceUser "The created user<->namespace relation."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid user namespace object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The user does not exist."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid user namespace object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The user does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/users [put]
|
||||
func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
||||
|
@ -114,8 +114,8 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
|||
// @Param namespaceID path int true "Namespace ID"
|
||||
// @Param userID path int true "user ID"
|
||||
// @Success 200 {object} models.Message "The user was successfully deleted."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "user or namespace does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 404 {object} web.HTTPError "user or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/users/{userID} [delete]
|
||||
func (nu *NamespaceUser) Delete() (err error) {
|
||||
|
@ -154,7 +154,7 @@ func (nu *NamespaceUser) Delete() (err error) {
|
|||
// @Param s query string false "Search users by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.UserWithRight "The users with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the namespace."
|
||||
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/users [get]
|
||||
func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
@ -210,8 +210,8 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
// @Param namespace body models.NamespaceUser true "The user you want to update."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.NamespaceUser "The updated user <-> namespace relation."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have admin-access to the namespace"
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User or namespace does not exist."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have admin-access to the namespace"
|
||||
// @Failure 404 {object} web.HTTPError "User or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/users/{userID} [post]
|
||||
func (nu *NamespaceUser) Update() (err error) {
|
||||
|
|
|
@ -162,7 +162,7 @@ func (t *Task) setTaskAssignees(assignees []*user.User) {
|
|||
// @Param taskID path int true "Task ID"
|
||||
// @Param userID path int true "Assignee user ID"
|
||||
// @Success 200 {object} models.Message "The assignee was successfully deleted."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "Not allowed to delete the assignee."
|
||||
// @Failure 403 {object} web.HTTPError "Not allowed to delete the assignee."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees/{userID} [delete]
|
||||
func (la *TaskAssginee) Delete() (err error) {
|
||||
|
@ -185,7 +185,7 @@ func (la *TaskAssginee) Delete() (err error) {
|
|||
// @Param assignee body models.TaskAssginee true "The assingee object"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.TaskAssginee "The created assingee object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees [put]
|
||||
func (la *TaskAssginee) Create(a web.Auth) (err error) {
|
||||
|
@ -296,7 +296,7 @@ type BulkAssignees struct {
|
|||
// @Param assignee body models.BulkAssignees true "The array of assignees"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.TaskAssginee "The created assingees object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees/bulk [post]
|
||||
func (ba *BulkAssignees) Create(a web.Auth) (err error) {
|
||||
|
|
|
@ -67,8 +67,7 @@ func validateTaskField(fieldName string) error {
|
|||
taskPropertyPercentDone,
|
||||
taskPropertyUID,
|
||||
taskPropertyCreated,
|
||||
taskPropertyUpdated,
|
||||
taskPropertyPosition:
|
||||
taskPropertyUpdated:
|
||||
return nil
|
||||
}
|
||||
return ErrInvalidTaskField{TaskField: fieldName}
|
||||
|
@ -107,7 +106,7 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
var sort = make([]*sortParam, 0, len(tf.SortBy))
|
||||
for i, s := range tf.SortBy {
|
||||
param := &sortParam{
|
||||
sortBy: s,
|
||||
sortBy: sortProperty(s),
|
||||
orderBy: orderAscending,
|
||||
}
|
||||
// This checks if tf.OrderBy has an entry with the same index as the current entry from tf.SortBy
|
||||
|
|
|
@ -16,12 +16,21 @@
|
|||
|
||||
package models
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/timeutil"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type (
|
||||
sortParam struct {
|
||||
sortBy string
|
||||
sortBy sortProperty
|
||||
orderBy sortOrder // asc or desc
|
||||
}
|
||||
|
||||
sortProperty string
|
||||
|
||||
sortOrder string
|
||||
)
|
||||
|
||||
|
@ -43,9 +52,12 @@ const (
|
|||
taskPropertyUID string = "uid"
|
||||
taskPropertyCreated string = "created"
|
||||
taskPropertyUpdated string = "updated"
|
||||
taskPropertyPosition string = "position"
|
||||
)
|
||||
|
||||
func (p sortProperty) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
const (
|
||||
orderInvalid sortOrder = "invalid"
|
||||
orderAscending sortOrder = "asc"
|
||||
|
@ -70,5 +82,139 @@ func (sp *sortParam) validate() error {
|
|||
if sp.orderBy != orderDescending && sp.orderBy != orderAscending {
|
||||
return ErrInvalidSortOrder{OrderBy: sp.orderBy}
|
||||
}
|
||||
return validateTaskField(sp.sortBy)
|
||||
return validateTaskField(string(sp.sortBy))
|
||||
}
|
||||
|
||||
type taskComparator func(lhs, rhs *Task) int64
|
||||
|
||||
func mustMakeComparator(fieldName string) taskComparator {
|
||||
field, ok := reflect.TypeOf(&Task{}).Elem().FieldByName(fieldName)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Field '%s' has not been found on Task", fieldName))
|
||||
}
|
||||
|
||||
extractProp := func(task *Task) interface{} {
|
||||
return reflect.ValueOf(task).Elem().FieldByIndex(field.Index).Interface()
|
||||
}
|
||||
|
||||
// Special case for handling TimeStamp types
|
||||
if field.Type.Name() == "TimeStamp" {
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
return int64(extractProp(lhs).(timeutil.TimeStamp)) - int64(extractProp(rhs).(timeutil.TimeStamp))
|
||||
}
|
||||
}
|
||||
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Int64:
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
return extractProp(lhs).(int64) - extractProp(rhs).(int64)
|
||||
}
|
||||
case reflect.Float64:
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
floatLHS, floatRHS := extractProp(lhs).(float64), extractProp(rhs).(float64)
|
||||
if floatLHS > floatRHS {
|
||||
return 1
|
||||
} else if floatLHS < floatRHS {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
case reflect.String:
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
strLHS, strRHS := extractProp(lhs).(string), extractProp(rhs).(string)
|
||||
if strLHS > strRHS {
|
||||
return 1
|
||||
} else if strLHS < strRHS {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
case reflect.Bool:
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
boolLHS, boolRHS := extractProp(lhs).(bool), extractProp(rhs).(bool)
|
||||
if !boolLHS && boolRHS {
|
||||
return -1
|
||||
} else if boolLHS && !boolRHS {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unsupported type for sorting: %s", field.Type.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
// This is a map of properties that can be sorted by
|
||||
// and their appropriate comparator function.
|
||||
// The comparator function sorts in ascending mode.
|
||||
var propertyComparators = map[string]taskComparator{
|
||||
taskPropertyID: mustMakeComparator("ID"),
|
||||
taskPropertyText: mustMakeComparator("Text"),
|
||||
taskPropertyDescription: mustMakeComparator("Description"),
|
||||
taskPropertyDone: mustMakeComparator("Done"),
|
||||
taskPropertyDoneAtUnix: mustMakeComparator("DoneAt"),
|
||||
taskPropertyDueDateUnix: mustMakeComparator("DueDate"),
|
||||
taskPropertyCreatedByID: mustMakeComparator("CreatedByID"),
|
||||
taskPropertyListID: mustMakeComparator("ListID"),
|
||||
taskPropertyRepeatAfter: mustMakeComparator("RepeatAfter"),
|
||||
taskPropertyPriority: mustMakeComparator("Priority"),
|
||||
taskPropertyStartDateUnix: mustMakeComparator("StartDate"),
|
||||
taskPropertyEndDateUnix: mustMakeComparator("EndDate"),
|
||||
taskPropertyHexColor: mustMakeComparator("HexColor"),
|
||||
taskPropertyPercentDone: mustMakeComparator("PercentDone"),
|
||||
taskPropertyUID: mustMakeComparator("UID"),
|
||||
taskPropertyCreated: mustMakeComparator("Created"),
|
||||
taskPropertyUpdated: mustMakeComparator("Updated"),
|
||||
}
|
||||
|
||||
// Creates a taskComparator that sorts by the first comparator and falls back to
|
||||
// the second one (and so on...) if the properties were equal.
|
||||
func combineComparators(comparators ...taskComparator) taskComparator {
|
||||
return func(lhs, rhs *Task) int64 {
|
||||
for _, compare := range comparators {
|
||||
res := compare(lhs, rhs)
|
||||
if res != 0 {
|
||||
return res
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func sortTasks(tasks []*Task, by []*sortParam) {
|
||||
|
||||
// Always sort at least by id asc so we have a consistent order of items every time
|
||||
// If we would not do this, we would get a different order for items with the same content every time
|
||||
// the slice is sorted. To circumvent this, we always order at least by ID.
|
||||
if len(by) == 0 ||
|
||||
(len(by) > 0 && by[len(by)-1].sortBy != sortProperty(taskPropertyID)) { // Don't sort by ID last if the id parameter is already passed as the last parameter.
|
||||
by = append(by, &sortParam{sortBy: sortProperty(taskPropertyID), orderBy: orderAscending})
|
||||
}
|
||||
|
||||
comparators := make([]taskComparator, 0, len(by))
|
||||
for _, param := range by {
|
||||
comparator, ok := propertyComparators[string(param.sortBy)]
|
||||
if !ok {
|
||||
panic("No suitable comparator for sortBy found! Param was " + param.sortBy)
|
||||
}
|
||||
|
||||
// This is a descending sort, so we need to negate the comparator (i.e. switch the inputs).
|
||||
if param.orderBy == orderDescending {
|
||||
oldComparator := comparator
|
||||
comparator = func(lhs, rhs *Task) int64 {
|
||||
return oldComparator(lhs, rhs) * -1
|
||||
}
|
||||
}
|
||||
|
||||
comparators = append(comparators, comparator)
|
||||
}
|
||||
|
||||
combinedComparator := combineComparators(comparators...)
|
||||
|
||||
sort.Slice(tasks, func(i, j int) bool {
|
||||
lhs, rhs := tasks[i], tasks[j]
|
||||
|
||||
res := combinedComparator(lhs, rhs)
|
||||
return res <= 0
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -59,12 +62,11 @@ func TestSortParamValidation(t *testing.T) {
|
|||
taskPropertyUID,
|
||||
taskPropertyCreated,
|
||||
taskPropertyUpdated,
|
||||
taskPropertyPosition,
|
||||
} {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
s := &sortParam{
|
||||
orderBy: orderAscending,
|
||||
sortBy: test,
|
||||
sortBy: sortProperty(test),
|
||||
}
|
||||
err := s.validate()
|
||||
assert.NoError(t, err)
|
||||
|
@ -90,3 +92,774 @@ func TestSortParamValidation(t *testing.T) {
|
|||
assert.True(t, IsErrInvalidTaskField(err))
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
task1 = &Task{
|
||||
ID: 1,
|
||||
Text: "aaa",
|
||||
Description: "Lorem Ipsum",
|
||||
Done: true,
|
||||
DoneAt: 1543626000,
|
||||
ListID: 1,
|
||||
UID: "JywtBPCESImlyKugvaZWrxmXAFAWXFISMeXYImEh",
|
||||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
}
|
||||
task2 = &Task{
|
||||
ID: 2,
|
||||
Text: "bbb",
|
||||
Description: "Arem Ipsum",
|
||||
Done: true,
|
||||
DoneAt: 1543626724,
|
||||
CreatedByID: 1,
|
||||
ListID: 2,
|
||||
PercentDone: 0.3,
|
||||
StartDate: 1543626724,
|
||||
Created: 1553626724,
|
||||
Updated: 1553626724,
|
||||
}
|
||||
task3 = &Task{
|
||||
ID: 3,
|
||||
Text: "ccc",
|
||||
DueDate: 1583626724,
|
||||
Priority: 100,
|
||||
ListID: 3,
|
||||
HexColor: "000000",
|
||||
PercentDone: 0.1,
|
||||
Updated: 1555555555,
|
||||
}
|
||||
task4 = &Task{
|
||||
ID: 4,
|
||||
Text: "ddd",
|
||||
Priority: 1,
|
||||
StartDate: 1643626724,
|
||||
ListID: 1,
|
||||
}
|
||||
task5 = &Task{
|
||||
ID: 5,
|
||||
Text: "eef",
|
||||
Priority: 50,
|
||||
UID: "shggzCHQWLhGNMNsOGOCOjcVkInOYjTAnORqTkdL",
|
||||
DueDate: 1543636724,
|
||||
Updated: 1565555555,
|
||||
}
|
||||
task6 = &Task{
|
||||
ID: 6,
|
||||
Text: "eef",
|
||||
DueDate: 1543616724,
|
||||
RepeatAfter: 6400,
|
||||
CreatedByID: 2,
|
||||
HexColor: "ffffff",
|
||||
}
|
||||
task7 = &Task{
|
||||
ID: 7,
|
||||
Text: "mmmn",
|
||||
Description: "Zoremis",
|
||||
StartDate: 1544600000,
|
||||
EndDate: 1584600000,
|
||||
UID: "tyzCZuLMSKhwclJOsDyDcUdyVAPBDOPHNTBOLTcW",
|
||||
}
|
||||
task8 = &Task{
|
||||
ID: 8,
|
||||
Text: "b123",
|
||||
EndDate: 1544700000,
|
||||
}
|
||||
task9 = &Task{
|
||||
ID: 9,
|
||||
Done: true,
|
||||
DoneAt: 1573626724,
|
||||
Text: "a123",
|
||||
RepeatAfter: 86000,
|
||||
StartDate: 1544600000,
|
||||
EndDate: 1544700000,
|
||||
}
|
||||
task10 = &Task{
|
||||
ID: 10,
|
||||
Text: "zzz",
|
||||
Priority: 10,
|
||||
PercentDone: 1,
|
||||
}
|
||||
)
|
||||
|
||||
type taskSortTestCase struct {
|
||||
name string
|
||||
wantAsc []*Task
|
||||
wantDesc []*Task
|
||||
sortProperty string
|
||||
}
|
||||
|
||||
var taskSortTestCases = []taskSortTestCase{
|
||||
{
|
||||
name: "id",
|
||||
sortProperty: taskPropertyID,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task9,
|
||||
task8,
|
||||
task7,
|
||||
task6,
|
||||
task5,
|
||||
task4,
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
sortProperty: taskPropertyText,
|
||||
wantAsc: []*Task{
|
||||
task9,
|
||||
task1,
|
||||
task8,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task7,
|
||||
task5,
|
||||
task6,
|
||||
task4,
|
||||
task3,
|
||||
task2,
|
||||
task8,
|
||||
task1,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
sortProperty: taskPropertyDescription,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task2,
|
||||
task1,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "done",
|
||||
sortProperty: taskPropertyDone,
|
||||
wantAsc: []*Task{
|
||||
// These are not
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
// These are done
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
// These are done
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
// These are not
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "done at",
|
||||
sortProperty: taskPropertyDoneAtUnix,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "due date",
|
||||
sortProperty: taskPropertyDueDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task6,
|
||||
task5,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "created by id",
|
||||
sortProperty: taskPropertyCreatedByID,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task2,
|
||||
task6,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task6,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list id",
|
||||
sortProperty: taskPropertyListID,
|
||||
wantAsc: []*Task{
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task4,
|
||||
task2,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "repeat after",
|
||||
sortProperty: taskPropertyRepeatAfter,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
task6,
|
||||
task9,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task9,
|
||||
task6,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "priority",
|
||||
sortProperty: taskPropertyPriority,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task4,
|
||||
task10,
|
||||
task5,
|
||||
task3,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task3,
|
||||
task5,
|
||||
task10,
|
||||
task4,
|
||||
task1,
|
||||
task2,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "start date",
|
||||
sortProperty: taskPropertyStartDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task10,
|
||||
task2,
|
||||
task7,
|
||||
task9,
|
||||
task4,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task4,
|
||||
task7,
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task5,
|
||||
task6,
|
||||
task8,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "end date",
|
||||
sortProperty: taskPropertyEndDateUnix,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task10,
|
||||
task8,
|
||||
task9,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hex color",
|
||||
sortProperty: taskPropertyHexColor,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task3,
|
||||
task6,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task6,
|
||||
task3,
|
||||
task1,
|
||||
task2,
|
||||
task4,
|
||||
task5,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "percent done",
|
||||
sortProperty: taskPropertyPercentDone,
|
||||
wantAsc: []*Task{
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task3,
|
||||
task2,
|
||||
task10,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task10,
|
||||
task2,
|
||||
task3,
|
||||
task1,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uid",
|
||||
sortProperty: taskPropertyUID,
|
||||
wantAsc: []*Task{
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task5,
|
||||
task7,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task7,
|
||||
task5,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task4,
|
||||
task6,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "created",
|
||||
sortProperty: taskPropertyCreated,
|
||||
wantAsc: []*Task{
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task2,
|
||||
task1,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "updated",
|
||||
sortProperty: taskPropertyUpdated,
|
||||
wantAsc: []*Task{
|
||||
task4,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
task1,
|
||||
task2,
|
||||
task3,
|
||||
task5,
|
||||
},
|
||||
wantDesc: []*Task{
|
||||
task5,
|
||||
task3,
|
||||
task2,
|
||||
task1,
|
||||
task4,
|
||||
task6,
|
||||
task7,
|
||||
task8,
|
||||
task9,
|
||||
task10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestTaskSort(t *testing.T) {
|
||||
|
||||
assertTestSliceMatch := func(t *testing.T, got, want []*Task) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("Slices do not match in order")
|
||||
t.Error("Got\t| Want")
|
||||
for in, task := range got {
|
||||
fail := ""
|
||||
if task.ID != want[in].ID {
|
||||
fail = "wrong"
|
||||
}
|
||||
t.Errorf("\t%d\t| %d \t%s", task.ID, want[in].ID, fail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, testCase := range taskSortTestCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Run("asc default", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantAsc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantAsc)
|
||||
})
|
||||
t.Run("asc", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantAsc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantAsc)
|
||||
})
|
||||
t.Run("desc", func(t *testing.T) {
|
||||
by := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(testCase.sortProperty),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(testCase.wantDesc).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, by)
|
||||
|
||||
assertTestSliceMatch(t, got, testCase.wantDesc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Other cases
|
||||
t.Run("Order by Done Ascending and ID Descending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Not done
|
||||
task10,
|
||||
task8,
|
||||
task7,
|
||||
task6,
|
||||
task5,
|
||||
task4,
|
||||
task3,
|
||||
|
||||
// Done
|
||||
task9,
|
||||
task2,
|
||||
task1,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyID),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
})
|
||||
t.Run("Order by Done Ascending and Text Descending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Not done
|
||||
task10,
|
||||
task7,
|
||||
task5,
|
||||
task6,
|
||||
task4,
|
||||
task3,
|
||||
task8,
|
||||
// Done
|
||||
task2,
|
||||
task1,
|
||||
task9,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyText),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
})
|
||||
t.Run("Order by Done Descending and Text Ascending", func(t *testing.T) {
|
||||
want := []*Task{
|
||||
// Done
|
||||
task9,
|
||||
task1,
|
||||
task2,
|
||||
// Not done
|
||||
task8,
|
||||
task3,
|
||||
task4,
|
||||
task5,
|
||||
task6,
|
||||
task7,
|
||||
task10,
|
||||
}
|
||||
sortParams := []*sortParam{
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyDone),
|
||||
orderBy: orderDescending,
|
||||
},
|
||||
{
|
||||
sortBy: sortProperty(taskPropertyText),
|
||||
orderBy: orderAscending,
|
||||
},
|
||||
}
|
||||
|
||||
got := deepcopy.Copy(want).([]*Task)
|
||||
|
||||
// Destroy wanted order to obtain some slice we can sort
|
||||
rand.Shuffle(len(got), func(i, j int) {
|
||||
got[i], got[j] = got[j], got[i]
|
||||
})
|
||||
|
||||
sortTasks(got, sortParams)
|
||||
|
||||
assertTestSliceMatch(t, got, want)
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
CreatedByID: 1,
|
||||
CreatedBy: user1,
|
||||
ListID: 1,
|
||||
BucketID: 1,
|
||||
Labels: []*Label{
|
||||
{
|
||||
ID: 4,
|
||||
|
@ -115,7 +114,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
CreatedByID: 1,
|
||||
CreatedBy: user1,
|
||||
ListID: 1,
|
||||
BucketID: 1,
|
||||
Labels: []*Label{
|
||||
{
|
||||
ID: 4,
|
||||
|
@ -142,7 +140,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
Priority: 100,
|
||||
BucketID: 2,
|
||||
}
|
||||
task4 := &Task{
|
||||
ID: 4,
|
||||
|
@ -156,7 +153,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
Priority: 1,
|
||||
BucketID: 2,
|
||||
}
|
||||
task5 := &Task{
|
||||
ID: 5,
|
||||
|
@ -170,7 +166,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
DueDate: 1543636724,
|
||||
BucketID: 2,
|
||||
}
|
||||
task6 := &Task{
|
||||
ID: 6,
|
||||
|
@ -184,7 +179,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
DueDate: 1543616724,
|
||||
BucketID: 3,
|
||||
}
|
||||
task7 := &Task{
|
||||
ID: 7,
|
||||
|
@ -198,7 +192,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
StartDate: 1544600000,
|
||||
BucketID: 3,
|
||||
}
|
||||
task8 := &Task{
|
||||
ID: 8,
|
||||
|
@ -212,7 +205,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
EndDate: 1544700000,
|
||||
BucketID: 3,
|
||||
}
|
||||
task9 := &Task{
|
||||
ID: 9,
|
||||
|
@ -453,7 +445,6 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
ListID: 1,
|
||||
Created: 1543626724,
|
||||
Updated: 1543626724,
|
||||
BucketID: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -53,7 +53,7 @@ func (tc *TaskComment) TableName() string {
|
|||
// @Param relation body models.TaskComment true "The task comment object"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.TaskComment "The created task comment object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments [put]
|
||||
func (tc *TaskComment) Create(a web.Auth) (err error) {
|
||||
|
@ -82,8 +82,8 @@ func (tc *TaskComment) Create(a web.Auth) (err error) {
|
|||
// @Param taskID path int true "Task ID"
|
||||
// @Param commentID path int true "Comment ID"
|
||||
// @Success 200 {object} models.Message "The task comment was successfully deleted."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The task comment was not found."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [delete]
|
||||
func (tc *TaskComment) Delete() error {
|
||||
|
@ -104,8 +104,8 @@ func (tc *TaskComment) Delete() error {
|
|||
// @Param taskID path int true "Task ID"
|
||||
// @Param commentID path int true "Comment ID"
|
||||
// @Success 200 {object} models.TaskComment "The updated task comment object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The task comment was not found."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [post]
|
||||
func (tc *TaskComment) Update() error {
|
||||
|
@ -126,8 +126,8 @@ func (tc *TaskComment) Update() error {
|
|||
// @Param taskID path int true "Task ID"
|
||||
// @Param commentID path int true "Comment ID"
|
||||
// @Success 200 {object} models.TaskComment "The task comment object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The task comment was not found."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [get]
|
||||
func (tc *TaskComment) ReadOne() (err error) {
|
||||
|
|
|
@ -113,7 +113,7 @@ type RelatedTaskMap map[RelationKind][]*Task
|
|||
// @Param relation body models.TaskRelation true "The relation object"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.TaskRelation "The created task relation object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/relations [put]
|
||||
func (rel *TaskRelation) Create(a web.Auth) error {
|
||||
|
@ -193,8 +193,8 @@ func (rel *TaskRelation) Create(a web.Auth) error {
|
|||
// @Param relation body models.TaskRelation true "The relation object"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Success 200 {object} models.Message "The task relation was successfully deleted."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The task relation was not found."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The task relation was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/relations [delete]
|
||||
func (rel *TaskRelation) Delete() error {
|
||||
|
|
|
@ -24,11 +24,10 @@ import (
|
|||
"code.vikunja.io/api/pkg/utils"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/imdario/mergo"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// Task represents an task in a todolist
|
||||
|
@ -42,30 +41,30 @@ type Task struct {
|
|||
// Whether a task is done or not.
|
||||
Done bool `xorm:"INDEX null" json:"done"`
|
||||
// The time when a task was marked as done.
|
||||
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"done_at"`
|
||||
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"doneAt"`
|
||||
// The time when the task is due.
|
||||
DueDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'due_date_unix'" json:"due_date"`
|
||||
DueDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'due_date_unix'" json:"dueDate"`
|
||||
// An array of datetimes when the user wants to be reminded of the task.
|
||||
Reminders []timeutil.TimeStamp `xorm:"-" json:"reminder_dates"`
|
||||
Reminders []timeutil.TimeStamp `xorm:"-" json:"reminderDates"`
|
||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"` // ID of the user who put that task on the list
|
||||
// The list this task belongs to.
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"list_id" param:"list"`
|
||||
ListID int64 `xorm:"int(11) INDEX not null" json:"listID" param:"list"`
|
||||
// An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as "undone" and then increase all remindes and the due date by its amount.
|
||||
RepeatAfter int64 `xorm:"int(11) INDEX null" json:"repeat_after"`
|
||||
RepeatAfter int64 `xorm:"int(11) INDEX null" json:"repeatAfter"`
|
||||
// The task priority. Can be anything you want, it is possible to sort by this later.
|
||||
Priority int64 `xorm:"int(11) null" json:"priority"`
|
||||
// When this task starts.
|
||||
StartDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'start_date_unix'" json:"start_date" query:"-"`
|
||||
StartDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'start_date_unix'" json:"startDate" query:"-"`
|
||||
// When this task ends.
|
||||
EndDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'end_date_unix'" json:"end_date" query:"-"`
|
||||
EndDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'end_date_unix'" json:"endDate" query:"-"`
|
||||
// An array of users who are assigned to this task
|
||||
Assignees []*user.User `xorm:"-" json:"assignees"`
|
||||
// An array of labels which are associated with this task.
|
||||
Labels []*Label `xorm:"-" json:"labels"`
|
||||
// The task color in hex
|
||||
HexColor string `xorm:"varchar(6) null" json:"hex_color" valid:"runelength(0|6)" maxLength:"6"`
|
||||
HexColor string `xorm:"varchar(6) null" json:"hexColor" valid:"runelength(0|6)" maxLength:"6"`
|
||||
// Determines how far a task is left from being done
|
||||
PercentDone float64 `xorm:"DOUBLE null" json:"percent_done"`
|
||||
PercentDone float64 `xorm:"DOUBLE null" json:"percentDone"`
|
||||
|
||||
// The task identifier, based on the list identifier and the task's index
|
||||
Identifier string `xorm:"-" json:"identifier"`
|
||||
|
@ -86,19 +85,8 @@ type Task struct {
|
|||
// A timestamp when this task was last updated. You cannot change this value.
|
||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
||||
|
||||
// BucketID is the ID of the kanban bucket this task belongs to.
|
||||
BucketID int64 `xorm:"int(11) null" json:"bucket_id"`
|
||||
|
||||
// The position of the task - any task list can be sorted as usual by this parameter.
|
||||
// When accessing tasks via kanban buckets, this is primarily used to sort them based on a range
|
||||
// We're using a float64 here to make it possible to put any task within any two other tasks (by changing the number).
|
||||
// You would calculate the new position between two tasks with something like task3.position = (task2.position - task1.position) / 2.
|
||||
// A 64-Bit float leaves plenty of room to initially give tasks a position with 2^16 difference to the previous task
|
||||
// which also leaves a lot of room for rearranging and sorting later.
|
||||
Position float64 `xorm:"double null" json:"position"`
|
||||
|
||||
// The user who initially created the task.
|
||||
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
||||
CreatedBy *user.User `xorm:"-" json:"createdBy" valid:"-"`
|
||||
|
||||
web.CRUDable `xorm:"-" json:"-"`
|
||||
web.Rights `xorm:"-" json:"-"`
|
||||
|
@ -152,7 +140,7 @@ func (t *Task) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
return nil, 0, 0, nil
|
||||
}
|
||||
|
||||
func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
func getRawTasksForLists(lists []*List, opts *taskOptions) (taskMap map[int64]*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
// Get all list IDs and get the tasks
|
||||
var listIDs []int64
|
||||
|
@ -160,15 +148,6 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
|||
listIDs = append(listIDs, l.ID)
|
||||
}
|
||||
|
||||
// Add the id parameter as the last parameter to sorty by default, but only if it is not already passed as the last parameter.
|
||||
if len(opts.sortby) == 0 ||
|
||||
len(opts.sortby) > 0 && opts.sortby[len(opts.sortby)-1].sortBy != taskPropertyID {
|
||||
opts.sortby = append(opts.sortby, &sortParam{
|
||||
sortBy: taskPropertyID,
|
||||
orderBy: orderAscending,
|
||||
})
|
||||
}
|
||||
|
||||
// Since xorm does not use placeholders for order by, it is possible to expose this with sql injection if we're directly
|
||||
// passing user input to the db.
|
||||
// As a workaround to prevent this, we check for valid column names here prior to passing it to the db.
|
||||
|
@ -178,19 +157,7 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
|||
if err := param.validate(); err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
orderby += param.sortBy + " " + param.orderBy.String()
|
||||
|
||||
// Postgres sorts by default entries with null values after ones with values.
|
||||
// To make that consistent with the sort order we have and other dbms, we're adding a separate clause here.
|
||||
if x.Dialect().URI().DBType == schemas.POSTGRES {
|
||||
if param.orderBy == orderAscending {
|
||||
orderby += " NULLS FIRST"
|
||||
}
|
||||
if param.orderBy == orderDescending {
|
||||
orderby += " NULLS LAST"
|
||||
}
|
||||
}
|
||||
|
||||
orderby += param.sortBy.String() + " " + param.orderBy.String()
|
||||
if (i + 1) < len(opts.sortby) {
|
||||
orderby += ", "
|
||||
}
|
||||
|
@ -214,6 +181,8 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
|||
}
|
||||
}
|
||||
|
||||
taskMap = make(map[int64]*Task)
|
||||
|
||||
// Then return all tasks for that lists
|
||||
query := x.
|
||||
OrderBy(orderby)
|
||||
|
@ -236,8 +205,7 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
|||
query = query.Limit(limit, start)
|
||||
}
|
||||
|
||||
tasks = []*Task{}
|
||||
err = query.Find(&tasks)
|
||||
err = query.Find(&taskMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -248,25 +216,23 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
|||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return tasks, len(tasks), totalItems, nil
|
||||
return taskMap, len(taskMap), totalItems, nil
|
||||
}
|
||||
|
||||
func getTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
tasks, resultCount, totalItems, err = getRawTasksForLists(lists, opts)
|
||||
taskMap, resultCount, totalItems, err := getRawTasksForLists(lists, opts)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
taskMap := make(map[int64]*Task, len(tasks))
|
||||
for _, t := range tasks {
|
||||
taskMap[t.ID] = t
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
tasks, err = addMoreInfoToTasks(taskMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
// Because the list is fully unsorted (since we're dealing with maps)
|
||||
// we have to manually sort the tasks again here.
|
||||
sortTasks(tasks, opts.sortby)
|
||||
|
||||
return tasks, resultCount, totalItems, err
|
||||
}
|
||||
|
@ -302,28 +268,25 @@ func (bt *BulkTask) GetTasksByIDs() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
err = x.In("id", bt.IDs).Find(&bt.Tasks)
|
||||
taskMap := make(map[int64]*Task, len(bt.Tasks))
|
||||
err = x.In("id", bt.IDs).Find(&taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bt.Tasks, err = addMoreInfoToTasks(taskMap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTasksByUIDs gets all tasks from a bunch of uids
|
||||
func GetTasksByUIDs(uids []string) (tasks []*Task, err error) {
|
||||
tasks = []*Task{}
|
||||
err = x.In("uid", uids).Find(&tasks)
|
||||
taskMap := make(map[int64]*Task)
|
||||
err = x.In("uid", uids).Find(&taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
taskMap := make(map[int64]*Task, len(tasks))
|
||||
for _, t := range tasks {
|
||||
taskMap[t.ID] = t
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
tasks, err = addMoreInfoToTasks(taskMap)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -335,7 +298,7 @@ func getRemindersForTasks(taskIDs []int64) (reminders []*TaskReminder, err error
|
|||
|
||||
// This function takes a map with pointers and returns a slice with pointers to tasks
|
||||
// It adds more stuff like assignees/labels/etc to a bunch of tasks
|
||||
func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
||||
func addMoreInfoToTasks(taskMap map[int64]*Task) (tasks []*Task, err error) {
|
||||
|
||||
// No need to iterate over users and stuff if the list doesn't has tasks
|
||||
if len(taskMap) == 0 {
|
||||
|
@ -385,7 +348,7 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
|||
In("task_id", taskIDs).
|
||||
Find(&attachments)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileIDs := []int64{}
|
||||
|
@ -480,22 +443,19 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
|||
taskMap[rt.TaskID].RelatedTasks[rt.RelationKind] = append(taskMap[rt.TaskID].RelatedTasks[rt.RelationKind], fullRelatedTasks[rt.OtherTaskID])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkBucketAndTaskBelongToSameList(fullTask *Task, bucketID int64) (err error) {
|
||||
if bucketID != 0 {
|
||||
b, err := getBucketByID(bucketID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fullTask.ListID != b.ListID {
|
||||
return ErrBucketDoesNotBelongToList{
|
||||
ListID: fullTask.ListID,
|
||||
BucketID: fullTask.BucketID,
|
||||
}
|
||||
}
|
||||
// make a complete slice from the map
|
||||
tasks = []*Task{}
|
||||
for _, t := range taskMap {
|
||||
tasks = append(tasks, t)
|
||||
}
|
||||
|
||||
// Sort the output. In Go, contents on a map are put on that map in no particular order.
|
||||
// Because of this, tasks are not sorted anymore in the output, this leads to confiusion.
|
||||
// To avoid all this, we need to sort the slice afterwards
|
||||
sort.Slice(tasks, func(i, j int) bool {
|
||||
return tasks[i].ID < tasks[j].ID
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -509,8 +469,8 @@ func checkBucketAndTaskBelongToSameList(fullTask *Task, bucketID int64) (err err
|
|||
// @Param id path int true "List ID"
|
||||
// @Param task body models.Task true "The task object"
|
||||
// @Success 200 {object} models.Task "The created task object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [put]
|
||||
func (t *Task) Create(a web.Auth) (err error) {
|
||||
|
@ -538,12 +498,6 @@ func (t *Task) Create(a web.Auth) (err error) {
|
|||
t.UID = utils.MakeRandomString(40)
|
||||
}
|
||||
|
||||
// If there is a bucket set, make sure they belong to the same list as the task
|
||||
err = checkBucketAndTaskBelongToSameList(t, t.BucketID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the index for this task
|
||||
latestTask := &Task{}
|
||||
_, err = x.Where("list_id = ?", t.ListID).OrderBy("id desc").Get(latestTask)
|
||||
|
@ -554,10 +508,6 @@ func (t *Task) Create(a web.Auth) (err error) {
|
|||
t.Index = latestTask.Index + 1
|
||||
t.CreatedByID = u.ID
|
||||
t.CreatedBy = u
|
||||
// If no position was supplied, set a default one
|
||||
if t.Position == 0 {
|
||||
t.Position = float64(latestTask.ID+1) * math.Pow(2, 16)
|
||||
}
|
||||
if _, err = x.Insert(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -588,8 +538,8 @@ func (t *Task) Create(a web.Auth) (err error) {
|
|||
// @Param id path int true "Task ID"
|
||||
// @Param task body models.Task true "The task object"
|
||||
// @Success 200 {object} models.Task "The updated task object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the task (aka its list)"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the task (aka its list)"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id} [post]
|
||||
func (t *Task) Update() (err error) {
|
||||
|
@ -623,12 +573,6 @@ func (t *Task) Update() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// If there is a bucket set, make sure they belong to the same list as the task
|
||||
err = checkBucketAndTaskBelongToSameList(&ot, t.BucketID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Update the labels
|
||||
//
|
||||
// Maybe FIXME:
|
||||
|
@ -689,19 +633,10 @@ func (t *Task) Update() (err error) {
|
|||
if t.HexColor == "" {
|
||||
ot.HexColor = ""
|
||||
}
|
||||
// Percent Done
|
||||
// Percent DOnw
|
||||
if t.PercentDone == 0 {
|
||||
ot.PercentDone = 0
|
||||
}
|
||||
// Bucket ID
|
||||
// Yes it is possible to move a task back into the empty bucket
|
||||
if t.BucketID == 0 {
|
||||
ot.BucketID = 0
|
||||
}
|
||||
// Position
|
||||
if t.Position == 0 {
|
||||
ot.Position = 0
|
||||
}
|
||||
|
||||
_, err = x.ID(t.ID).
|
||||
Cols("text",
|
||||
|
@ -714,11 +649,7 @@ func (t *Task) Update() (err error) {
|
|||
"end_date_unix",
|
||||
"hex_color",
|
||||
"done_at_unix",
|
||||
"percent_done",
|
||||
"list_id",
|
||||
"bucket_id",
|
||||
"position",
|
||||
).
|
||||
"percent_done").
|
||||
Update(ot)
|
||||
*t = ot
|
||||
if err != nil {
|
||||
|
@ -865,8 +796,8 @@ func (t *Task) updateReminders(reminders []timeutil.TimeStamp) (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Task ID"
|
||||
// @Success 200 {object} models.Message "The created task object."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid task ID provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task ID provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id} [delete]
|
||||
func (t *Task) Delete() (err error) {
|
||||
|
@ -907,16 +838,16 @@ func (t *Task) ReadOne() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
tasks, err := addMoreInfoToTasks(taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(taskMap) == 0 {
|
||||
if len(tasks) == 0 {
|
||||
return ErrTaskDoesNotExist{t.ID}
|
||||
}
|
||||
|
||||
*t = *taskMap[t.ID]
|
||||
*t = *tasks[0]
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -59,24 +59,12 @@ func (t *Task) CanWrite(a web.Auth) (canWrite bool, err error) {
|
|||
// Helper function to check if a user can do stuff on a list task
|
||||
func (t *Task) canDoTask(a web.Auth) (bool, error) {
|
||||
// Get the task
|
||||
ot, err := GetTaskByIDSimple(t.ID)
|
||||
lI, err := GetTaskByIDSimple(t.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if we're moving the task into a different list to check if the user has sufficient rights for that on the new list
|
||||
if t.ListID != 0 && t.ListID != ot.ListID {
|
||||
newList := &List{ID: t.ListID}
|
||||
can, err := newList.CanWrite(a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !can {
|
||||
return false, ErrGenericForbidden{}
|
||||
}
|
||||
}
|
||||
|
||||
// A user can do a task if it has write acces to its list
|
||||
l := &List{ID: ot.ListID}
|
||||
l := &List{ID: lI.ListID}
|
||||
return l.CanWrite(a)
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ import (
|
|||
// @Param id path int true "Team ID"
|
||||
// @Param team body models.TeamMember true "The user to be added to a team."
|
||||
// @Success 200 {object} models.TeamMember "The newly created member object"
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid member object provided."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 400 {object} web.HTTPError "Invalid member object provided."
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id}/members [put]
|
||||
func (tm *TeamMember) Create(a web.Auth) (err error) {
|
||||
|
|
|
@ -35,7 +35,7 @@ type Team struct {
|
|||
CreatedByID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||
|
||||
// The user who created this team.
|
||||
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
||||
CreatedBy *user.User `xorm:"-" json:"createdBy"`
|
||||
// An array of all members in this team.
|
||||
Members []*TeamUser `xorm:"-" json:"members"`
|
||||
|
||||
|
@ -174,7 +174,7 @@ func addMoreInfoToTeams(teams []*Team) (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Team ID"
|
||||
// @Success 200 {object} models.Team "The team"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 403 {object} web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [get]
|
||||
func (t *Team) ReadOne() (err error) {
|
||||
|
@ -242,7 +242,7 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
// @Security JWTKeyAuth
|
||||
// @Param team body models.Team true "The team you want to create."
|
||||
// @Success 200 {object} models.Team "The created team."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid team object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams [put]
|
||||
func (t *Team) Create(a web.Auth) (err error) {
|
||||
|
@ -282,7 +282,7 @@ func (t *Team) Create(a web.Auth) (err error) {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "Team ID"
|
||||
// @Success 200 {object} models.Message "The team was successfully deleted."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid team object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [delete]
|
||||
func (t *Team) Delete() (err error) {
|
||||
|
@ -325,7 +325,7 @@ func (t *Team) Delete() (err error) {
|
|||
// @Param id path int true "Team ID"
|
||||
// @Param team body models.Team true "The team with updated values you want to update."
|
||||
// @Success 200 {object} models.Team "The updated team."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid team object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [post]
|
||||
func (t *Team) Update() (err error) {
|
||||
|
|
|
@ -56,9 +56,7 @@ func SetupTests() {
|
|||
"teams",
|
||||
"users",
|
||||
"users_list",
|
||||
"users_namespace",
|
||||
"buckets",
|
||||
)
|
||||
"users_namespace")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func NewLinkShareJWTAuthtoken(share *models.LinkSharing) (token string, err erro
|
|||
claims["type"] = AuthTypeLinkShare
|
||||
claims["id"] = share.ID
|
||||
claims["hash"] = share.Hash
|
||||
claims["list_id"] = share.ListID
|
||||
claims["listID"] = share.ListID
|
||||
claims["right"] = share.Right
|
||||
claims["sharedByID"] = share.SharedByID
|
||||
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||
|
|
|
@ -18,6 +18,7 @@ package v1
|
|||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -38,7 +39,7 @@ type LinkShareToken struct {
|
|||
// @Produce json
|
||||
// @Param share path string true "The share hash"
|
||||
// @Success 200 {object} v1.Token "The valid jwt auth token."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid link share object provided."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid link share object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /shares/{share}/auth [post]
|
||||
func AuthenticateLinkShare(c echo.Context) error {
|
||||
|
|
|
@ -39,7 +39,6 @@ type Token struct {
|
|||
// @Param credentials body user.UserLogin true "The login credentials"
|
||||
// @Success 200 {object} v1.Token
|
||||
// @Failure 400 {object} models.Message "Invalid user password model."
|
||||
// @Failure 412 {object} models.Message "Invalid totp passcode."
|
||||
// @Failure 403 {object} models.Message "Invalid username or password."
|
||||
// @Router /login [post]
|
||||
func Login(c echo.Context) error {
|
||||
|
@ -54,21 +53,6 @@ func Login(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
totpEnabled, err := user2.TOTPEnabledForUser(user)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if totpEnabled {
|
||||
_, err = user2.ValidateTOTPPasscode(&user2.TOTPPasscode{
|
||||
User: user,
|
||||
Passcode: u.TOTPPasscode,
|
||||
})
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Create token
|
||||
t, err := NewUserJWTAuthtoken(user)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -33,7 +34,7 @@ import (
|
|||
// @Produce json
|
||||
// @Param credentials body user.APIUserPassword true "The user credentials"
|
||||
// @Success 200 {object} user.User
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "No or invalid user register object provided / User already exists."
|
||||
// @Failure 400 {object} web.HTTPError "No or invalid user register object provided / User already exists."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /register [post]
|
||||
func RegisterUser(c echo.Context) error {
|
||||
|
|
|
@ -19,6 +19,7 @@ package v1
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -32,7 +33,7 @@ import (
|
|||
// @Produce json
|
||||
// @Param credentials body models.EmailConfirm true "The token."
|
||||
// @Success 200 {object} models.Message
|
||||
// @Failure 412 {object} code.vikunja.io/web.HTTPError "Bad token provided."
|
||||
// @Failure 412 {object} web.HTTPError "Bad token provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /user/confirm [post]
|
||||
func UserConfirmEmail(c echo.Context) error {
|
||||
|
|
|
@ -19,6 +19,7 @@ package v1
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -34,7 +35,7 @@ import (
|
|||
// @Param s query string false "Search for a user by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} user.User "All (found) users."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 400 {object} web.HTTPError "Something's invalid."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /users [get]
|
||||
func UserList(c echo.Context) error {
|
||||
|
@ -62,8 +63,8 @@ func UserList(c echo.Context) error {
|
|||
// @Security JWTKeyAuth
|
||||
// @Param id path int true "List ID"
|
||||
// @Success 200 {array} user.User "All (found) users."
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 401 {object} code.vikunja.io/web.HTTPError "The user does not have the right to see the list."
|
||||
// @Failure 400 {object} web.HTTPError "Something's invalid."
|
||||
// @Failure 401 {object} web.HTTPError "The user does not have the right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /lists/{id}/listusers [get]
|
||||
func ListUsersForList(c echo.Context) error {
|
||||
|
|
|
@ -19,6 +19,7 @@ package v1
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -32,7 +33,7 @@ import (
|
|||
// @Produce json
|
||||
// @Param credentials body models.PasswordReset true "The token with the new password."
|
||||
// @Success 200 {object} models.Message
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Bad token provided."
|
||||
// @Failure 400 {object} web.HTTPError "Bad token provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /user/password/reset [post]
|
||||
func UserResetPassword(c echo.Context) error {
|
||||
|
@ -58,7 +59,7 @@ func UserResetPassword(c echo.Context) error {
|
|||
// @Produce json
|
||||
// @Param credentials body models.PasswordTokenRequest true "The username of the user to request a token for."
|
||||
// @Success 200 {object} models.Message
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "The user does not exist."
|
||||
// @Failure 404 {object} web.HTTPError "The user does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /user/password/token [post]
|
||||
func UserRequestResetPasswordToken(c echo.Context) error {
|
||||
|
|
|
@ -18,6 +18,7 @@ package v1
|
|||
|
||||
import (
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -31,7 +32,7 @@ import (
|
|||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} user.User
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 404 {object} web.HTTPError "User does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user [get]
|
||||
func UserShow(c echo.Context) error {
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
"fmt"
|
||||
"github.com/labstack/echo/v4"
|
||||
"image/jpeg"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UserTOTPEnroll is the handler to enroll a user into totp
|
||||
// @Summary Enroll a user into totp
|
||||
// @Description Creates an initial setup for the user in the db. After this step, the user needs to verify they have a working totp setup with the "enable totp" endpoint.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} user.TOTP
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/totp/enroll [post]
|
||||
func UserTOTPEnroll(c echo.Context) error {
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
t, err := user.EnrollTOTP(u)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, t)
|
||||
}
|
||||
|
||||
// UserTOTPEnable is the handler to enable totp for a user
|
||||
// @Summary Enable a previously enrolled totp setting.
|
||||
// @Description Enables a previously enrolled totp setting by providing a totp passcode.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param totp body user.TOTPPasscode true "The totp passcode."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.Message "Successfully enabled"
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 412 {object} code.vikunja.io/web.HTTPError "TOTP is not enrolled."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/totp/enable [post]
|
||||
func UserTOTPEnable(c echo.Context) error {
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
passcode := &user.TOTPPasscode{
|
||||
User: u,
|
||||
}
|
||||
if err := c.Bind(passcode); err != nil {
|
||||
log.Debugf("Invalid model error. Internal error was: %s", err.Error())
|
||||
if he, is := err.(*echo.HTTPError); is {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided. Error was: %s", he.Message))
|
||||
}
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided."))
|
||||
}
|
||||
|
||||
err = user.EnableTOTP(passcode)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "TOTP was enabled successfully."})
|
||||
}
|
||||
|
||||
// UserTOTPDisable disables totp settings for the current user.
|
||||
// @Summary Disable totp settings
|
||||
// @Description Disables any totp settings for the current user.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Param totp body user.Login true "The current user's password (only password is enough)."
|
||||
// @Success 200 {object} models.Message "Successfully disabled"
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/totp/disable [post]
|
||||
func UserTOTPDisable(c echo.Context) error {
|
||||
login := &user.Login{}
|
||||
if err := c.Bind(login); err != nil {
|
||||
log.Debugf("Invalid model error. Internal error was: %s", err.Error())
|
||||
if he, is := err.(*echo.HTTPError); is {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided. Error was: %s", he.Message))
|
||||
}
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided."))
|
||||
}
|
||||
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
u, err = user.GetUserByID(u.ID)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.CheckUserPassword(u, login.Password)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.DisableTOTP(u)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "TOTP was enabled successfully."})
|
||||
}
|
||||
|
||||
// UserTOTPQrCode is the handler to show a qr code to enroll the user into totp
|
||||
// @Summary Totp QR Code
|
||||
// @Description Returns a qr code for easier setup at end user's devices.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {} string "The qr code as jpeg image"
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/totp/qrcode [get]
|
||||
func UserTOTPQrCode(c echo.Context) error {
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
qrcode, err := user.GetTOTPQrCodeForUser(u)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = jpeg.Encode(buff, qrcode, nil)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.Blob(http.StatusOK, "image/jpeg", buff.Bytes())
|
||||
}
|
||||
|
||||
// UserTOTP returns the current totp implementation if any is enabled.
|
||||
// @Summary Totp setting for the current user
|
||||
// @Description Returns the current user totp setting or an error if it is not enabled.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} user.TOTP "The totp settings."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/totp [get]
|
||||
func UserTOTP(c echo.Context) error {
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
t, err := user.GetTOTPForUser(u)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, t)
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
"fmt"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UpdateUserEmail is the handler to let a user update their email address.
|
||||
// @Summary Update email address
|
||||
// @Description Lets the current user change their email address.
|
||||
// @tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param userEmailUpdate body user.EmailUpdate true "The new email address and current password."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.Message
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/settings/email [post]
|
||||
func UpdateUserEmail(c echo.Context) (err error) {
|
||||
|
||||
var emailUpdate = &user.EmailUpdate{}
|
||||
if err := c.Bind(emailUpdate); err != nil {
|
||||
log.Debugf("Invalid model error. Internal error was: %s", err.Error())
|
||||
if he, is := err.(*echo.HTTPError); is {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided. Error was: %s", he.Message))
|
||||
}
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided."))
|
||||
}
|
||||
|
||||
emailUpdate.User, err = user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
emailUpdate.User, err = user.CheckUserCredentials(&user.Login{
|
||||
Username: emailUpdate.User.Username,
|
||||
Password: emailUpdate.Password,
|
||||
})
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.UpdateEmail(emailUpdate)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "We sent you email with a link to confirm your email address."})
|
||||
}
|
|
@ -19,6 +19,7 @@ package v1
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
_ "code.vikunja.io/web"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
"net/http"
|
||||
|
@ -39,8 +40,8 @@ type UserPassword struct {
|
|||
// @Param userPassword body v1.UserPassword true "The current and new password."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {object} models.Message
|
||||
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
|
||||
// @Failure 400 {object} web.HTTPError "Something's invalid."
|
||||
// @Failure 404 {object} web.HTTPError "User does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/password [post]
|
||||
func UserChangePassword(c echo.Context) error {
|
||||
|
|
|
@ -202,18 +202,10 @@ func registerAPIRoutes(a *echo.Group) {
|
|||
a.POST("/tokenTest", apiv1.CheckToken)
|
||||
|
||||
// User stuff
|
||||
u := a.Group("/user")
|
||||
|
||||
u.GET("", apiv1.UserShow)
|
||||
u.POST("/password", apiv1.UserChangePassword)
|
||||
u.GET("s", apiv1.UserList)
|
||||
u.POST("/token", apiv1.RenewToken)
|
||||
u.POST("/settings/email", apiv1.UpdateUserEmail)
|
||||
u.GET("/settings/totp", apiv1.UserTOTP)
|
||||
u.POST("/settings/totp/enroll", apiv1.UserTOTPEnroll)
|
||||
u.POST("/settings/totp/enable", apiv1.UserTOTPEnable)
|
||||
u.POST("/settings/totp/disable", apiv1.UserTOTPDisable)
|
||||
u.GET("/settings/totp/qrcode", apiv1.UserTOTPQrCode)
|
||||
a.GET("/user", apiv1.UserShow)
|
||||
a.POST("/user/password", apiv1.UserChangePassword)
|
||||
a.GET("/users", apiv1.UserList)
|
||||
a.POST("/user/token", apiv1.RenewToken)
|
||||
|
||||
listHandler := &handler.WebHandler{
|
||||
EmptyStruct: func() handler.CObject {
|
||||
|
@ -246,16 +238,6 @@ func registerAPIRoutes(a *echo.Group) {
|
|||
}
|
||||
a.GET("/lists/:list/tasks", taskCollectionHandler.ReadAllWeb)
|
||||
|
||||
kanbanBucketHandler := &handler.WebHandler{
|
||||
EmptyStruct: func() handler.CObject {
|
||||
return &models.Bucket{}
|
||||
},
|
||||
}
|
||||
a.GET("/lists/:list/buckets", kanbanBucketHandler.ReadAllWeb)
|
||||
a.PUT("/lists/:list/buckets", kanbanBucketHandler.CreateWeb)
|
||||
a.POST("/lists/:list/buckets/:bucket", kanbanBucketHandler.UpdateWeb)
|
||||
a.DELETE("/lists/:list/buckets/:bucket", kanbanBucketHandler.DeleteWeb)
|
||||
|
||||
taskHandler := &handler.WebHandler{
|
||||
EmptyStruct: func() handler.CObject {
|
||||
return &models.Task{}
|
||||
|
|
|
@ -989,7 +989,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "The list ID.",
|
||||
"name": "list_id",
|
||||
"name": "listID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
|
@ -1026,13 +1026,13 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "The start date parameter to filter by. Expects a unix timestamp. If no end date, but a start date is specified, the end date is set to the current time.",
|
||||
"name": "start_date",
|
||||
"name": "startdate",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "The end date parameter to filter by. Expects a unix timestamp. If no start date, but an end date is specified, the start date is set to the current time.",
|
||||
"name": "end_date",
|
||||
"name": "enddate",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
|
@ -1077,14 +1077,14 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "List ID",
|
||||
"name": "list_id",
|
||||
"name": "listID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Team ID",
|
||||
"name": "team_id",
|
||||
"name": "teamID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
|
@ -1144,14 +1144,14 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "List ID",
|
||||
"name": "list_id",
|
||||
"name": "listID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Team ID",
|
||||
"name": "team_id",
|
||||
"name": "teamID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -1206,14 +1206,14 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "List ID",
|
||||
"name": "list_id",
|
||||
"name": "listID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
|
@ -1273,14 +1273,14 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "List ID",
|
||||
"name": "list_id",
|
||||
"name": "listID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -2430,7 +2430,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Team ID",
|
||||
"name": "team_id",
|
||||
"name": "teamID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
|
@ -2497,7 +2497,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "team ID",
|
||||
"name": "team_id",
|
||||
"name": "teamID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -2559,7 +2559,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
|
@ -2626,7 +2626,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "user ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -2796,13 +2796,13 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "The start date parameter to filter by. Expects a unix timestamp. If no end date, but a start date is specified, the end date is set to the current time.",
|
||||
"name": "start_date",
|
||||
"name": "startdate",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "The end date parameter to filter by. Expects a unix timestamp. If no start date, but an end date is specified, the start date is set to the current time.",
|
||||
"name": "end_date",
|
||||
"name": "enddate",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
|
@ -3505,7 +3505,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Assignee user ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -4208,7 +4208,7 @@ var doc = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "user_id",
|
||||
"name": "userID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -4652,7 +4652,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"created_by": {
|
||||
"createdBy": {
|
||||
"description": "The user who initially created the task.",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/user.User"
|
||||
|
@ -4665,19 +4665,19 @@ var doc = `{
|
|||
"description": "Whether a task is done or not.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"done_at": {
|
||||
"doneAt": {
|
||||
"description": "The unix timestamp when a task was marked as done.",
|
||||
"type": "integer"
|
||||
},
|
||||
"due_date": {
|
||||
"dueDate": {
|
||||
"description": "A unix timestamp when the task is due.",
|
||||
"type": "integer"
|
||||
},
|
||||
"end_date": {
|
||||
"endDate": {
|
||||
"description": "When this task ends.",
|
||||
"type": "integer"
|
||||
},
|
||||
"hex_color": {
|
||||
"hexColor": {
|
||||
"description": "The task color in hex",
|
||||
"type": "string",
|
||||
"maxLength": 6
|
||||
|
@ -4701,11 +4701,11 @@ var doc = `{
|
|||
"$ref": "#/definitions/models.Label"
|
||||
}
|
||||
},
|
||||
"list_id": {
|
||||
"listID": {
|
||||
"description": "The list this task belongs to.",
|
||||
"type": "integer"
|
||||
},
|
||||
"percent_done": {
|
||||
"percentDone": {
|
||||
"description": "Determines how far a task is left from being done",
|
||||
"type": "number"
|
||||
},
|
||||
|
@ -4718,18 +4718,18 @@ var doc = `{
|
|||
"type": "object",
|
||||
"$ref": "#/definitions/models.RelatedTaskMap"
|
||||
},
|
||||
"reminder_dates": {
|
||||
"reminderDates": {
|
||||
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"repeat_after": {
|
||||
"repeatAfter": {
|
||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||
"type": "integer"
|
||||
},
|
||||
"start_date": {
|
||||
"startDate": {
|
||||
"description": "When this task starts.",
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -4920,7 +4920,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"user_id": {
|
||||
"userID": {
|
||||
"description": "The username.",
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -4988,7 +4988,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this relation was last updated. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"user_id": {
|
||||
"userID": {
|
||||
"description": "The username.",
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -5079,7 +5079,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"created_by": {
|
||||
"createdBy": {
|
||||
"description": "The user who initially created the task.",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/user.User"
|
||||
|
@ -5092,19 +5092,19 @@ var doc = `{
|
|||
"description": "Whether a task is done or not.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"done_at": {
|
||||
"doneAt": {
|
||||
"description": "The unix timestamp when a task was marked as done.",
|
||||
"type": "integer"
|
||||
},
|
||||
"due_date": {
|
||||
"dueDate": {
|
||||
"description": "A unix timestamp when the task is due.",
|
||||
"type": "integer"
|
||||
},
|
||||
"end_date": {
|
||||
"endDate": {
|
||||
"description": "When this task ends.",
|
||||
"type": "integer"
|
||||
},
|
||||
"hex_color": {
|
||||
"hexColor": {
|
||||
"description": "The task color in hex",
|
||||
"type": "string",
|
||||
"maxLength": 6
|
||||
|
@ -5128,11 +5128,11 @@ var doc = `{
|
|||
"$ref": "#/definitions/models.Label"
|
||||
}
|
||||
},
|
||||
"list_id": {
|
||||
"listID": {
|
||||
"description": "The list this task belongs to.",
|
||||
"type": "integer"
|
||||
},
|
||||
"percent_done": {
|
||||
"percentDone": {
|
||||
"description": "Determines how far a task is left from being done",
|
||||
"type": "number"
|
||||
},
|
||||
|
@ -5145,18 +5145,18 @@ var doc = `{
|
|||
"type": "object",
|
||||
"$ref": "#/definitions/models.RelatedTaskMap"
|
||||
},
|
||||
"reminder_dates": {
|
||||
"reminderDates": {
|
||||
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"repeat_after": {
|
||||
"repeatAfter": {
|
||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||
"type": "integer"
|
||||
},
|
||||
"start_date": {
|
||||
"startDate": {
|
||||
"description": "When this task starts.",
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -5195,7 +5195,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this task was created. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"created_by": {
|
||||
"createdBy": {
|
||||
"description": "The user who initially created the task.",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/user.User"
|
||||
|
@ -5208,19 +5208,19 @@ var doc = `{
|
|||
"description": "Whether a task is done or not.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"done_at": {
|
||||
"doneAt": {
|
||||
"description": "The unix timestamp when a task was marked as done.",
|
||||
"type": "integer"
|
||||
},
|
||||
"due_date": {
|
||||
"dueDate": {
|
||||
"description": "A unix timestamp when the task is due.",
|
||||
"type": "integer"
|
||||
},
|
||||
"end_date": {
|
||||
"endDate": {
|
||||
"description": "When this task ends.",
|
||||
"type": "integer"
|
||||
},
|
||||
"hex_color": {
|
||||
"hexColor": {
|
||||
"description": "The task color in hex",
|
||||
"type": "string",
|
||||
"maxLength": 6
|
||||
|
@ -5244,11 +5244,11 @@ var doc = `{
|
|||
"$ref": "#/definitions/models.Label"
|
||||
}
|
||||
},
|
||||
"list_id": {
|
||||
"listID": {
|
||||
"description": "The list this task belongs to.",
|
||||
"type": "integer"
|
||||
},
|
||||
"percent_done": {
|
||||
"percentDone": {
|
||||
"description": "Determines how far a task is left from being done",
|
||||
"type": "number"
|
||||
},
|
||||
|
@ -5261,18 +5261,18 @@ var doc = `{
|
|||
"type": "object",
|
||||
"$ref": "#/definitions/models.RelatedTaskMap"
|
||||
},
|
||||
"reminder_dates": {
|
||||
"reminderDates": {
|
||||
"description": "An array of unix timestamps when the user wants to be reminded of the task.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"repeat_after": {
|
||||
"repeatAfter": {
|
||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||
"type": "integer"
|
||||
},
|
||||
"start_date": {
|
||||
"startDate": {
|
||||
"description": "When this task starts.",
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -5354,7 +5354,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this relation was created. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"created_by": {
|
||||
"createdBy": {
|
||||
"description": "The user who created this team.",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/user.User"
|
||||
|
@ -5403,7 +5403,7 @@ var doc = `{
|
|||
"default": 0,
|
||||
"maximum": 2
|
||||
},
|
||||
"team_id": {
|
||||
"teamID": {
|
||||
"description": "The team id.",
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -5451,7 +5451,7 @@ var doc = `{
|
|||
"default": 0,
|
||||
"maximum": 2
|
||||
},
|
||||
"team_id": {
|
||||
"teamID": {
|
||||
"description": "The team id.",
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -5504,7 +5504,7 @@ var doc = `{
|
|||
"description": "A unix timestamp when this relation was created. You cannot change this value.",
|
||||
"type": "integer"
|
||||
},
|
||||
"created_by": {
|
||||
"createdBy": {
|
||||
"description": "The user who created this team.",
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/user.User"
|
||||
|
|
|
@ -46,6 +46,5 @@ func InitDB() (err error) {
|
|||
func GetTables() []interface{} {
|
||||
return []interface{}{
|
||||
&User{},
|
||||
&TOTP{},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
|
|||
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
|
||||
}
|
||||
|
||||
// ErrCouldNotGetUserID represents a "ErrCouldNotGetuser_id" kind of error.
|
||||
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
|
||||
type ErrCouldNotGetUserID struct{}
|
||||
|
||||
// IsErrCouldNotGetUserID checks if an error is a ErrCouldNotGetUserID.
|
||||
|
@ -289,80 +289,3 @@ const ErrCodeEmptyOldPassword = 1014
|
|||
func (err ErrEmptyOldPassword) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmptyOldPassword, Message: "Please specify old password."}
|
||||
}
|
||||
|
||||
// ErrTOTPAlreadyEnabled represents a "TOTPAlreadyEnabled" kind of error.
|
||||
type ErrTOTPAlreadyEnabled struct{}
|
||||
|
||||
// IsErrTOTPAlreadyEnabled checks if an error is a ErrTOTPAlreadyEnabled.
|
||||
func IsErrTOTPAlreadyEnabled(err error) bool {
|
||||
_, ok := err.(ErrTOTPAlreadyEnabled)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTOTPAlreadyEnabled) Error() string {
|
||||
return fmt.Sprintf("Totp is already enabled for this user")
|
||||
}
|
||||
|
||||
// ErrCodeTOTPAlreadyEnabled holds the unique world-error code of this error
|
||||
const ErrCodeTOTPAlreadyEnabled = 1015
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTOTPAlreadyEnabled) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusPreconditionFailed,
|
||||
Code: ErrCodeTOTPAlreadyEnabled,
|
||||
Message: "Totp is already enabled for this user, but not activated.",
|
||||
}
|
||||
}
|
||||
|
||||
// ErrTOTPNotEnabled represents a "TOTPNotEnabled" kind of error.
|
||||
type ErrTOTPNotEnabled struct{}
|
||||
|
||||
// IsErrTOTPNotEnabled checks if an error is a ErrTOTPNotEnabled.
|
||||
func IsErrTOTPNotEnabled(err error) bool {
|
||||
_, ok := err.(ErrTOTPNotEnabled)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTOTPNotEnabled) Error() string {
|
||||
return fmt.Sprintf("Totp is not enabled for this user")
|
||||
}
|
||||
|
||||
// ErrCodeTOTPNotEnabled holds the unique world-error code of this error
|
||||
const ErrCodeTOTPNotEnabled = 1016
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTOTPNotEnabled) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusPreconditionFailed,
|
||||
Code: ErrCodeTOTPNotEnabled,
|
||||
Message: "Totp is not enabled for this user.",
|
||||
}
|
||||
}
|
||||
|
||||
// ErrInvalidTOTPPasscode represents a "InvalidTOTPPasscode" kind of error.
|
||||
type ErrInvalidTOTPPasscode struct {
|
||||
Passcode string
|
||||
}
|
||||
|
||||
// IsErrInvalidTOTPPasscode checks if an error is a ErrInvalidTOTPPasscode.
|
||||
func IsErrInvalidTOTPPasscode(err error) bool {
|
||||
_, ok := err.(ErrInvalidTOTPPasscode)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidTOTPPasscode) Error() string {
|
||||
return fmt.Sprintf("Invalid totp passcode")
|
||||
}
|
||||
|
||||
// ErrCodeInvalidTOTPPasscode holds the unique world-error code of this error
|
||||
const ErrCodeInvalidTOTPPasscode = 1017
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidTOTPPasscode) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusPreconditionFailed,
|
||||
Code: ErrCodeInvalidTOTPPasscode,
|
||||
Message: "Invalid totp passcode.",
|
||||
}
|
||||
}
|
||||
|
|
140
pkg/user/totp.go
140
pkg/user/totp.go
|
@ -1,140 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"image"
|
||||
)
|
||||
|
||||
// TOTP holds a user's totp setting in the database.
|
||||
type TOTP struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"-"`
|
||||
UserID int64 `xorm:"int(11) not null" json:"-"`
|
||||
Secret string `xorm:"varchar(20) not null" json:"secret"`
|
||||
// The totp entry will only be enabled after the user verified they have a working totp setup.
|
||||
Enabled bool `xorm:"null" json:"enabled"`
|
||||
// The totp url used to be able to enroll the user later
|
||||
URL string `xorm:"text null" json:"url"`
|
||||
}
|
||||
|
||||
// TableName holds the table name for totp secrets
|
||||
func (T *TOTP) TableName() string {
|
||||
return "totp"
|
||||
}
|
||||
|
||||
// TOTPPasscode is used to validate a users totp passcode
|
||||
type TOTPPasscode struct {
|
||||
User *User `json:"-"`
|
||||
Passcode string `json:"passcode"`
|
||||
}
|
||||
|
||||
// TOTPEnabledForUser checks if totp is enabled for a user - not if it is activated, use GetTOTPForUser to check that.
|
||||
func TOTPEnabledForUser(user *User) (bool, error) {
|
||||
return x.Where("user_id = ?", user.ID).Exist(&TOTP{})
|
||||
}
|
||||
|
||||
// GetTOTPForUser returns the current state of totp settings for the user.
|
||||
func GetTOTPForUser(user *User) (t *TOTP, err error) {
|
||||
t = &TOTP{}
|
||||
exists, err := x.Where("user_id = ?", user.ID).Get(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
return nil, ErrTOTPNotEnabled{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// EnrollTOTP creates a new TOTP entry for the user - it does not enable it yet.
|
||||
func EnrollTOTP(user *User) (t *TOTP, err error) {
|
||||
isEnrolled, err := x.Where("user_id = ?", user.ID).Exist(&TOTP{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isEnrolled {
|
||||
return nil, ErrTOTPAlreadyEnabled{}
|
||||
}
|
||||
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: "Vikunja",
|
||||
AccountName: user.Username,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t = &TOTP{
|
||||
UserID: user.ID,
|
||||
Secret: key.Secret(),
|
||||
Enabled: false,
|
||||
URL: key.URL(),
|
||||
}
|
||||
_, err = x.Insert(t)
|
||||
return
|
||||
}
|
||||
|
||||
// EnableTOTP enables totp for a user. The provided passcode is used to verify the user has a working totp setup.
|
||||
func EnableTOTP(passcode *TOTPPasscode) (err error) {
|
||||
t, err := ValidateTOTPPasscode(passcode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
Where("id = ?", t.ID).
|
||||
Cols("enabled").
|
||||
Update(&TOTP{Enabled: true})
|
||||
return
|
||||
}
|
||||
|
||||
// DisableTOTP removes all totp settings for a user.
|
||||
func DisableTOTP(user *User) (err error) {
|
||||
_, err = x.Where("user_id = ?", user.ID).Delete(&TOTP{})
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateTOTPPasscode validated totp codes of users.
|
||||
func ValidateTOTPPasscode(passcode *TOTPPasscode) (t *TOTP, err error) {
|
||||
t, err = GetTOTPForUser(passcode.User)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !totp.Validate(passcode.Passcode, t.Secret) {
|
||||
return nil, ErrInvalidTOTPPasscode{Passcode: passcode.Passcode}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetTOTPQrCodeForUser returns a qrcode for a user's totp setting
|
||||
func GetTOTPQrCodeForUser(user *User) (qrcode image.Image, err error) {
|
||||
t, err := GetTOTPForUser(user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
key, err := otp.NewKeyFromURL(t.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return key.Image(300, 300)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 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
|
||||
// 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/mail"
|
||||
"code.vikunja.io/api/pkg/utils"
|
||||
)
|
||||
|
||||
// EmailUpdate is the data structure to update a user's email address
|
||||
type EmailUpdate struct {
|
||||
User *User `json:"-"`
|
||||
// The new email address. Needs to be a valid email address.
|
||||
NewEmail string `json:"new_email" valid:"email,length(0|250),required"`
|
||||
// The password of the user for confirmation.
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// UpdateEmail lets a user update their email address
|
||||
func UpdateEmail(update *EmailUpdate) (err error) {
|
||||
|
||||
// Check the email is not already used
|
||||
user := &User{}
|
||||
has, err := x.Where("email = ?", update.NewEmail).Get(user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if has {
|
||||
return ErrUserEmailExists{UserID: user.ID, Email: update.NewEmail}
|
||||
}
|
||||
|
||||
// Set the user as unconfirmed and the new email address
|
||||
update.User, err = GetUserWithEmail(&User{ID: update.User.ID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
update.User.IsActive = false
|
||||
update.User.Email = update.NewEmail
|
||||
update.User.EmailConfirmToken = utils.MakeRandomString(64)
|
||||
_, err = x.
|
||||
Where("id = ?", update.User.ID).
|
||||
Cols("email", "is_active", "email_confirm_token").
|
||||
Update(update.User)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Send the confirmation mail
|
||||
if !config.MailerEnabled.GetBool() {
|
||||
return
|
||||
}
|
||||
|
||||
// Send the user a mail with a link to confirm the mail
|
||||
data := map[string]interface{}{
|
||||
"User": update.User,
|
||||
"IsNew": false,
|
||||
}
|
||||
|
||||
mail.SendMailWithTemplate(update.User.Email, update.User.Username+", please confirm your email address at Vikunja", "confirm-email", data)
|
||||
|
||||
return
|
||||
}
|
|
@ -37,8 +37,6 @@ type Login struct {
|
|||
Username string `json:"username"`
|
||||
// The password for the user.
|
||||
Password string `json:"password"`
|
||||
// The totp passcode of a user. Only needs to be provided when enabled.
|
||||
TOTPPasscode string `json:"totp_passcode"`
|
||||
}
|
||||
|
||||
// User holds information about an user
|
||||
|
@ -162,7 +160,7 @@ func CheckUserCredentials(u *Login) (*User, error) {
|
|||
user, err := GetUserByUsername(u.Username)
|
||||
if err != nil {
|
||||
// hashing the password takes a long time, so we hash something to not make it clear if the username was wrong
|
||||
_, _ = bcrypt.GenerateFromPassword([]byte(u.Username), 14)
|
||||
bcrypt.GenerateFromPassword([]byte(u.Username), 14)
|
||||
return &User{}, ErrWrongUsernameOrPassword{}
|
||||
}
|
||||
|
||||
|
@ -172,27 +170,17 @@ func CheckUserCredentials(u *Login) (*User, error) {
|
|||
}
|
||||
|
||||
// Check the users password
|
||||
err = CheckUserPassword(user, u.Password)
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password))
|
||||
if err != nil {
|
||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||
return &User{}, ErrWrongUsernameOrPassword{}
|
||||
}
|
||||
return &User{}, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// CheckUserPassword checks and verifies a user's password. The user object needs to contain the hashed password from the database.
|
||||
func CheckUserPassword(user *User, password string) error {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
|
||||
if err != nil {
|
||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||
return ErrWrongUsernameOrPassword{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrentUser returns the current user based on its jwt token
|
||||
func GetCurrentUser(c echo.Context) (user *User, err error) {
|
||||
jwtinf := c.Get("user").(*jwt.Token)
|
||||
|
@ -264,7 +252,7 @@ func CreateUser(user *User) (newUser *User, err error) {
|
|||
// The new user should not be activated until it confirms his mail address
|
||||
newUser.IsActive = false
|
||||
// Generate a confirm token
|
||||
newUser.EmailConfirmToken = utils.MakeRandomString(60)
|
||||
newUser.EmailConfirmToken = utils.MakeRandomString(400)
|
||||
}
|
||||
|
||||
// Insert it
|
||||
|
@ -289,8 +277,7 @@ func CreateUser(user *User) (newUser *User, err error) {
|
|||
|
||||
// Send the user a mail with a link to confirm the mail
|
||||
data := map[string]interface{}{
|
||||
"User": newUserOut,
|
||||
"IsNew": true,
|
||||
"User": newUserOut,
|
||||
}
|
||||
|
||||
mail.SendMailWithTemplate(user.Email, newUserOut.Username+" + Vikunja = <3", "confirm-email", data)
|
||||
|
|
|
@ -147,36 +147,36 @@ func TestGetUser(t *testing.T) {
|
|||
func TestCheckUserCredentials(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Username: "user1", Password: "1234"})
|
||||
_, err := CheckUserCredentials(&Login{"user1", "1234"})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("unverified email", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Username: "user5", Password: "1234"})
|
||||
_, err := CheckUserCredentials(&Login{"user5", "1234"})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrEmailNotConfirmed(err))
|
||||
})
|
||||
t.Run("wrong password", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Username: "user1", Password: "12345"})
|
||||
_, err := CheckUserCredentials(&Login{"user1", "12345"})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWrongUsernameOrPassword(err))
|
||||
})
|
||||
t.Run("nonexistant user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Username: "dfstestuu", Password: "1234"})
|
||||
_, err := CheckUserCredentials(&Login{"dfstestuu", "1234"})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWrongUsernameOrPassword(err))
|
||||
})
|
||||
t.Run("empty password", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Username: "user1"})
|
||||
_, err := CheckUserCredentials(&Login{"user1", ""})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNoUsernamePassword(err))
|
||||
})
|
||||
t.Run("empty username", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, err := CheckUserCredentials(&Login{Password: "1234"})
|
||||
_, err := CheckUserCredentials(&Login{"", "1234"})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNoUsernamePassword(err))
|
||||
})
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5" // #nosec
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Md5String generates an md5 hash from a string
|
||||
func Md5String(in string) string {
|
||||
// #nosec
|
||||
h := md5.New()
|
||||
_, _ = io.WriteString(h, in)
|
||||
io.WriteString(h, in)
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
{{template "mail-header.tmpl" .}}
|
||||
<p>
|
||||
Hi {{.User.Username}},<br>
|
||||
{{if .IsNew}}
|
||||
<br>
|
||||
Welcome to Vikunja!
|
||||
{{end}}
|
||||
<br>
|
||||
Welcome to Vikunja!
|
||||
<br/>
|
||||
To confirm your email address, click the link below:
|
||||
To confirm you email address, click the link below:
|
||||
</p>
|
||||
<a href="{{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}}" title="Confirm your email address" style="background: rgb(20, 131, 175); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; border: 1px solid rgb(16, 106, 140); border-bottom-width: 3px; color: rgb(255, 255, 255); font-weight: 700; font-size: 13px; margin: 10px auto; padding: 5px 10px; text-decoration: none; text-align: center; text-rendering: optimizelegibility; text-transform: uppercase; display: block; width: 200px;">
|
||||
Confirm your email address
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
Hi {{.User.Username}},
|
||||
|
||||
{{if .IsNew}}
|
||||
Welcome to Vikunja!
|
||||
Welcome to Vikunja!
|
||||
|
||||
{{end}}
|
||||
To confirm your email address, copy the link below and paste it in your browser:
|
||||
To confirm you email address, click the link below:
|
||||
|
||||
{{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}}
|
10
vendor/gitea.com/xorm/xorm-redis-cache/go.mod
generated
vendored
10
vendor/gitea.com/xorm/xorm-redis-cache/go.mod
generated
vendored
|
@ -1,10 +0,0 @@
|
|||
module gitea.com/xorm/xorm-redis-cache
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
gitea.com/xorm/tests v0.7.0
|
||||
github.com/garyburd/redigo v1.6.0
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
xorm.io/xorm v1.0.1
|
||||
)
|
173
vendor/gitea.com/xorm/xorm-redis-cache/go.sum
generated
vendored
173
vendor/gitea.com/xorm/xorm-redis-cache/go.sum
generated
vendored
|
@ -1,173 +0,0 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitea.com/xorm/tests v0.7.0 h1:pFcaxTGGAWw3rDuVfhBdyr+mX1uzdTtncyAKxkCQ/IE=
|
||||
gitea.com/xorm/tests v0.7.0/go.mod h1:ngmhQrSBgihBbOqw1hdReSQJAnTlbStYTn0vruUFwDc=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-oci8 v0.0.0-20191108001511-cbd8d5bc1da0/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/shopspring/decimal v0.0.0-20191009025716-f1972eb1d1f5/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
|
||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
|
||||
xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=
|
13
vendor/gitea.com/xorm/xorm-redis-cache/redis_cacher.go
generated
vendored
13
vendor/gitea.com/xorm/xorm-redis-cache/redis_cacher.go
generated
vendored
|
@ -10,8 +10,7 @@ import (
|
|||
"unsafe"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"xorm.io/xorm/caches"
|
||||
"xorm.io/xorm/log"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,7 +25,7 @@ type RedisCacher struct {
|
|||
pool *redis.Pool
|
||||
defaultExpiration time.Duration
|
||||
|
||||
Logger log.ContextLogger
|
||||
Logger core.ILogger
|
||||
}
|
||||
|
||||
// NewRedisCacher creates a Redis Cacher, host as IP endpoint, i.e., localhost:6379, provide empty string or nil if Redis server doesn't
|
||||
|
@ -39,7 +38,7 @@ type RedisCacher struct {
|
|||
//
|
||||
// engine.MapCacher(&user, xormrediscache.NewRedisCacher("localhost:6379", "", xormrediscache.DEFAULT_EXPIRATION, engine.Logger))
|
||||
//
|
||||
func NewRedisCacher(host string, password string, defaultExpiration time.Duration, logger log.ContextLogger) *RedisCacher {
|
||||
func NewRedisCacher(host string, password string, defaultExpiration time.Duration, logger core.ILogger) *RedisCacher {
|
||||
var pool = &redis.Pool{
|
||||
MaxIdle: 5,
|
||||
IdleTimeout: 240 * time.Second,
|
||||
|
@ -75,7 +74,7 @@ func NewRedisCacher(host string, password string, defaultExpiration time.Duratio
|
|||
}
|
||||
|
||||
// MakeRedisCacher build a cacher based on redis.Pool
|
||||
func MakeRedisCacher(pool *redis.Pool, defaultExpiration time.Duration, logger log.ContextLogger) *RedisCacher {
|
||||
func MakeRedisCacher(pool *redis.Pool, defaultExpiration time.Duration, logger core.ILogger) *RedisCacher {
|
||||
return &RedisCacher{pool: pool, defaultExpiration: defaultExpiration, Logger: logger}
|
||||
}
|
||||
|
||||
|
@ -167,8 +166,8 @@ func (c *RedisCacher) delObject(key string) error {
|
|||
conn := c.pool.Get()
|
||||
defer conn.Close()
|
||||
if !exists(conn, key) {
|
||||
c.logErrf("delObject key:[%s] err: %v", key, caches.ErrCacheMiss)
|
||||
return caches.ErrCacheMiss
|
||||
c.logErrf("delObject key:[%s] err: %v", key, core.ErrCacheMiss)
|
||||
return core.ErrCacheMiss
|
||||
}
|
||||
_, err := conn.Do("DEL", key)
|
||||
return err
|
||||
|
|
1
vendor/github.com/boombuler/barcode/.gitignore
generated
vendored
1
vendor/github.com/boombuler/barcode/.gitignore
generated
vendored
|
@ -1 +0,0 @@
|
|||
.vscode/
|
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
21
vendor/github.com/boombuler/barcode/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Florian Sundermann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
53
vendor/github.com/boombuler/barcode/README.md
generated
vendored
53
vendor/github.com/boombuler/barcode/README.md
generated
vendored
|
@ -1,53 +0,0 @@
|
|||
[![Join the chat at https://gitter.im/golang-barcode/Lobby](https://badges.gitter.im/golang-barcode/Lobby.svg)](https://gitter.im/golang-barcode/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
## Introduction ##
|
||||
|
||||
This is a package for GO which can be used to create different types of barcodes.
|
||||
|
||||
## Supported Barcode Types ##
|
||||
* 2 of 5
|
||||
* Aztec Code
|
||||
* Codabar
|
||||
* Code 128
|
||||
* Code 39
|
||||
* Code 93
|
||||
* Datamatrix
|
||||
* EAN 13
|
||||
* EAN 8
|
||||
* PDF 417
|
||||
* QR Code
|
||||
|
||||
## Example ##
|
||||
|
||||
This is a simple example on how to create a QR-Code and write it to a png-file
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/png"
|
||||
"os"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/qr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create the barcode
|
||||
qrCode, _ := qr.Encode("Hello World", qr.M, qr.Auto)
|
||||
|
||||
// Scale the barcode to 200x200 pixels
|
||||
qrCode, _ = barcode.Scale(qrCode, 200, 200)
|
||||
|
||||
// create the output file
|
||||
file, _ := os.Create("qrcode.png")
|
||||
defer file.Close()
|
||||
|
||||
// encode the barcode as png
|
||||
png.Encode(file, qrCode)
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation ##
|
||||
See [GoDoc](https://godoc.org/github.com/boombuler/barcode)
|
||||
|
||||
To create a barcode use the Encode function from one of the subpackages.
|
42
vendor/github.com/boombuler/barcode/barcode.go
generated
vendored
42
vendor/github.com/boombuler/barcode/barcode.go
generated
vendored
|
@ -1,42 +0,0 @@
|
|||
package barcode
|
||||
|
||||
import "image"
|
||||
|
||||
const (
|
||||
TypeAztec = "Aztec"
|
||||
TypeCodabar = "Codabar"
|
||||
TypeCode128 = "Code 128"
|
||||
TypeCode39 = "Code 39"
|
||||
TypeCode93 = "Code 93"
|
||||
TypeDataMatrix = "DataMatrix"
|
||||
TypeEAN8 = "EAN 8"
|
||||
TypeEAN13 = "EAN 13"
|
||||
TypePDF = "PDF417"
|
||||
TypeQR = "QR Code"
|
||||
Type2of5 = "2 of 5"
|
||||
Type2of5Interleaved = "2 of 5 (interleaved)"
|
||||
)
|
||||
|
||||
// Contains some meta information about a barcode
|
||||
type Metadata struct {
|
||||
// the name of the barcode kind
|
||||
CodeKind string
|
||||
// contains 1 for 1D barcodes or 2 for 2D barcodes
|
||||
Dimensions byte
|
||||
}
|
||||
|
||||
// a rendered and encoded barcode
|
||||
type Barcode interface {
|
||||
image.Image
|
||||
// returns some meta information about the barcode
|
||||
Metadata() Metadata
|
||||
// the data that was encoded in this barcode
|
||||
Content() string
|
||||
}
|
||||
|
||||
// Additional interface that some barcodes might implement to provide
|
||||
// the value of its checksum.
|
||||
type BarcodeIntCS interface {
|
||||
Barcode
|
||||
CheckSum() int
|
||||
}
|
1
vendor/github.com/boombuler/barcode/go.mod
generated
vendored
1
vendor/github.com/boombuler/barcode/go.mod
generated
vendored
|
@ -1 +0,0 @@
|
|||
module github.com/boombuler/barcode
|
66
vendor/github.com/boombuler/barcode/qr/alphanumeric.go
generated
vendored
66
vendor/github.com/boombuler/barcode/qr/alphanumeric.go
generated
vendored
|
@ -1,66 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
const charSet string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
||||
|
||||
func stringToAlphaIdx(content string) <-chan int {
|
||||
result := make(chan int)
|
||||
go func() {
|
||||
for _, r := range content {
|
||||
idx := strings.IndexRune(charSet, r)
|
||||
result <- idx
|
||||
if idx < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(result)
|
||||
}()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func encodeAlphaNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
|
||||
contentLenIsOdd := len(content)%2 == 1
|
||||
contentBitCount := (len(content) / 2) * 11
|
||||
if contentLenIsOdd {
|
||||
contentBitCount += 6
|
||||
}
|
||||
vi := findSmallestVersionInfo(ecl, alphaNumericMode, contentBitCount)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(alphaNumericMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(alphaNumericMode))
|
||||
|
||||
encoder := stringToAlphaIdx(content)
|
||||
|
||||
for idx := 0; idx < len(content)/2; idx++ {
|
||||
c1 := <-encoder
|
||||
c2 := <-encoder
|
||||
if c1 < 0 || c2 < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
|
||||
}
|
||||
res.AddBits(c1*45+c2, 11)
|
||||
}
|
||||
if contentLenIsOdd {
|
||||
c := <-encoder
|
||||
if c < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
|
||||
}
|
||||
res.AddBits(c, 6)
|
||||
}
|
||||
|
||||
addPaddingAndTerminator(res, vi)
|
||||
|
||||
return res, vi, nil
|
||||
}
|
23
vendor/github.com/boombuler/barcode/qr/automatic.go
generated
vendored
23
vendor/github.com/boombuler/barcode/qr/automatic.go
generated
vendored
|
@ -1,23 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeAuto(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
bits, vi, _ := Numeric.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
bits, vi, _ = AlphaNumeric.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
bits, vi, _ = Unicode.getEncoder()(content, ecl)
|
||||
if bits != nil && vi != nil {
|
||||
return bits, vi, nil
|
||||
}
|
||||
return nil, nil, fmt.Errorf("No encoding found to encode \"%s\"", content)
|
||||
}
|
59
vendor/github.com/boombuler/barcode/qr/blocks.go
generated
vendored
59
vendor/github.com/boombuler/barcode/qr/blocks.go
generated
vendored
|
@ -1,59 +0,0 @@
|
|||
package qr
|
||||
|
||||
type block struct {
|
||||
data []byte
|
||||
ecc []byte
|
||||
}
|
||||
type blockList []*block
|
||||
|
||||
func splitToBlocks(data <-chan byte, vi *versionInfo) blockList {
|
||||
result := make(blockList, vi.NumberOfBlocksInGroup1+vi.NumberOfBlocksInGroup2)
|
||||
|
||||
for b := 0; b < int(vi.NumberOfBlocksInGroup1); b++ {
|
||||
blk := new(block)
|
||||
blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup1)
|
||||
for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup1); cw++ {
|
||||
blk.data[cw] = <-data
|
||||
}
|
||||
blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
|
||||
result[b] = blk
|
||||
}
|
||||
|
||||
for b := 0; b < int(vi.NumberOfBlocksInGroup2); b++ {
|
||||
blk := new(block)
|
||||
blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup2)
|
||||
for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup2); cw++ {
|
||||
blk.data[cw] = <-data
|
||||
}
|
||||
blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
|
||||
result[int(vi.NumberOfBlocksInGroup1)+b] = blk
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (bl blockList) interleave(vi *versionInfo) []byte {
|
||||
var maxCodewordCount int
|
||||
if vi.DataCodeWordsPerBlockInGroup1 > vi.DataCodeWordsPerBlockInGroup2 {
|
||||
maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup1)
|
||||
} else {
|
||||
maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup2)
|
||||
}
|
||||
resultLen := (vi.DataCodeWordsPerBlockInGroup1+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup1 +
|
||||
(vi.DataCodeWordsPerBlockInGroup2+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup2
|
||||
|
||||
result := make([]byte, 0, resultLen)
|
||||
for i := 0; i < maxCodewordCount; i++ {
|
||||
for b := 0; b < len(bl); b++ {
|
||||
if len(bl[b].data) > i {
|
||||
result = append(result, bl[b].data[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < int(vi.ErrorCorrectionCodewordsPerBlock); i++ {
|
||||
for b := 0; b < len(bl); b++ {
|
||||
result = append(result, bl[b].ecc[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
416
vendor/github.com/boombuler/barcode/qr/encoder.go
generated
vendored
416
vendor/github.com/boombuler/barcode/qr/encoder.go
generated
vendored
|
@ -1,416 +0,0 @@
|
|||
// Package qr can be used to create QR barcodes.
|
||||
package qr
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
|
||||
|
||||
// Encoding mode for QR Codes.
|
||||
type Encoding byte
|
||||
|
||||
const (
|
||||
// Auto will choose ths best matching encoding
|
||||
Auto Encoding = iota
|
||||
// Numeric encoding only encodes numbers [0-9]
|
||||
Numeric
|
||||
// AlphaNumeric encoding only encodes uppercase letters, numbers and [Space], $, %, *, +, -, ., /, :
|
||||
AlphaNumeric
|
||||
// Unicode encoding encodes the string as utf-8
|
||||
Unicode
|
||||
// only for testing purpose
|
||||
unknownEncoding
|
||||
)
|
||||
|
||||
func (e Encoding) getEncoder() encodeFn {
|
||||
switch e {
|
||||
case Auto:
|
||||
return encodeAuto
|
||||
case Numeric:
|
||||
return encodeNumeric
|
||||
case AlphaNumeric:
|
||||
return encodeAlphaNumeric
|
||||
case Unicode:
|
||||
return encodeUnicode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Encoding) String() string {
|
||||
switch e {
|
||||
case Auto:
|
||||
return "Auto"
|
||||
case Numeric:
|
||||
return "Numeric"
|
||||
case AlphaNumeric:
|
||||
return "AlphaNumeric"
|
||||
case Unicode:
|
||||
return "Unicode"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Encode returns a QR barcode with the given content, error correction level and uses the given encoding
|
||||
func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
|
||||
bits, vi, err := mode.getEncoder()(content, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocks := splitToBlocks(bits.IterateBytes(), vi)
|
||||
data := blocks.interleave(vi)
|
||||
result := render(data, vi)
|
||||
result.content = content
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func render(data []byte, vi *versionInfo) *qrcode {
|
||||
dim := vi.modulWidth()
|
||||
results := make([]*qrcode, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
results[i] = newBarcode(dim)
|
||||
}
|
||||
|
||||
occupied := newBarcode(dim)
|
||||
|
||||
setAll := func(x int, y int, val bool) {
|
||||
occupied.Set(x, y, true)
|
||||
for i := 0; i < 8; i++ {
|
||||
results[i].Set(x, y, val)
|
||||
}
|
||||
}
|
||||
|
||||
drawFinderPatterns(vi, setAll)
|
||||
drawAlignmentPatterns(occupied, vi, setAll)
|
||||
|
||||
//Timing Pattern:
|
||||
var i int
|
||||
for i = 0; i < dim; i++ {
|
||||
if !occupied.Get(i, 6) {
|
||||
setAll(i, 6, i%2 == 0)
|
||||
}
|
||||
if !occupied.Get(6, i) {
|
||||
setAll(6, i, i%2 == 0)
|
||||
}
|
||||
}
|
||||
// Dark Module
|
||||
setAll(8, dim-8, true)
|
||||
|
||||
drawVersionInfo(vi, setAll)
|
||||
drawFormatInfo(vi, -1, occupied.Set)
|
||||
for i := 0; i < 8; i++ {
|
||||
drawFormatInfo(vi, i, results[i].Set)
|
||||
}
|
||||
|
||||
// Write the data
|
||||
var curBitNo int
|
||||
|
||||
for pos := range iterateModules(occupied) {
|
||||
var curBit bool
|
||||
if curBitNo < len(data)*8 {
|
||||
curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
|
||||
} else {
|
||||
curBit = false
|
||||
}
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
|
||||
}
|
||||
curBitNo++
|
||||
}
|
||||
|
||||
lowestPenalty := ^uint(0)
|
||||
lowestPenaltyIdx := -1
|
||||
for i := 0; i < 8; i++ {
|
||||
p := results[i].calcPenalty()
|
||||
if p < lowestPenalty {
|
||||
lowestPenalty = p
|
||||
lowestPenaltyIdx = i
|
||||
}
|
||||
}
|
||||
return results[lowestPenaltyIdx]
|
||||
}
|
||||
|
||||
func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
|
||||
switch mask {
|
||||
case 0:
|
||||
val = val != (((y + x) % 2) == 0)
|
||||
break
|
||||
case 1:
|
||||
val = val != ((y % 2) == 0)
|
||||
break
|
||||
case 2:
|
||||
val = val != ((x % 3) == 0)
|
||||
break
|
||||
case 3:
|
||||
val = val != (((y + x) % 3) == 0)
|
||||
break
|
||||
case 4:
|
||||
val = val != (((y/2 + x/3) % 2) == 0)
|
||||
break
|
||||
case 5:
|
||||
val = val != (((y*x)%2)+((y*x)%3) == 0)
|
||||
break
|
||||
case 6:
|
||||
val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
|
||||
break
|
||||
case 7:
|
||||
val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
|
||||
}
|
||||
set(x, y, val)
|
||||
}
|
||||
|
||||
func iterateModules(occupied *qrcode) <-chan image.Point {
|
||||
result := make(chan image.Point)
|
||||
allPoints := make(chan image.Point)
|
||||
go func() {
|
||||
curX := occupied.dimension - 1
|
||||
curY := occupied.dimension - 1
|
||||
isUpward := true
|
||||
|
||||
for true {
|
||||
if isUpward {
|
||||
allPoints <- image.Pt(curX, curY)
|
||||
allPoints <- image.Pt(curX-1, curY)
|
||||
curY--
|
||||
if curY < 0 {
|
||||
curY = 0
|
||||
curX -= 2
|
||||
if curX == 6 {
|
||||
curX--
|
||||
}
|
||||
if curX < 0 {
|
||||
break
|
||||
}
|
||||
isUpward = false
|
||||
}
|
||||
} else {
|
||||
allPoints <- image.Pt(curX, curY)
|
||||
allPoints <- image.Pt(curX-1, curY)
|
||||
curY++
|
||||
if curY >= occupied.dimension {
|
||||
curY = occupied.dimension - 1
|
||||
curX -= 2
|
||||
if curX == 6 {
|
||||
curX--
|
||||
}
|
||||
isUpward = true
|
||||
if curX < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(allPoints)
|
||||
}()
|
||||
go func() {
|
||||
for pt := range allPoints {
|
||||
if !occupied.Get(pt.X, pt.Y) {
|
||||
result <- pt
|
||||
}
|
||||
}
|
||||
close(result)
|
||||
}()
|
||||
return result
|
||||
}
|
||||
|
||||
func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
|
||||
dim := vi.modulWidth()
|
||||
drawPattern := func(xoff int, yoff int) {
|
||||
for x := -1; x < 8; x++ {
|
||||
for y := -1; y < 8; y++ {
|
||||
val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0)
|
||||
|
||||
if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
|
||||
set(x+xoff, y+yoff, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drawPattern(0, 0)
|
||||
drawPattern(0, dim-7)
|
||||
drawPattern(dim-7, 0)
|
||||
}
|
||||
|
||||
func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
|
||||
drawPattern := func(xoff int, yoff int) {
|
||||
for x := -2; x <= 2; x++ {
|
||||
for y := -2; y <= 2; y++ {
|
||||
val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
|
||||
set(x+xoff, y+yoff, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
positions := vi.alignmentPatternPlacements()
|
||||
|
||||
for _, x := range positions {
|
||||
for _, y := range positions {
|
||||
if occupied.Get(x, y) {
|
||||
continue
|
||||
}
|
||||
drawPattern(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
|
||||
L: {
|
||||
0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
|
||||
1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
|
||||
2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
|
||||
3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
|
||||
4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
|
||||
5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
|
||||
6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
|
||||
7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
|
||||
},
|
||||
M: {
|
||||
0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
|
||||
1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
|
||||
2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
|
||||
3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
|
||||
4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
|
||||
5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
|
||||
6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
|
||||
7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
|
||||
},
|
||||
Q: {
|
||||
0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
|
||||
1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
|
||||
2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
|
||||
3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
|
||||
4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
|
||||
5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
|
||||
6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
|
||||
7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
|
||||
},
|
||||
H: {
|
||||
0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
|
||||
1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
|
||||
2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
|
||||
3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
|
||||
4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
|
||||
5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
|
||||
6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
|
||||
7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
|
||||
},
|
||||
}
|
||||
|
||||
func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
|
||||
var formatInfo []bool
|
||||
|
||||
if usedMask == -1 {
|
||||
formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true} // Set all to true cause -1 --> occupied mask.
|
||||
} else {
|
||||
formatInfo = formatInfos[vi.Level][usedMask]
|
||||
}
|
||||
|
||||
if len(formatInfo) == 15 {
|
||||
dim := vi.modulWidth()
|
||||
set(0, 8, formatInfo[0])
|
||||
set(1, 8, formatInfo[1])
|
||||
set(2, 8, formatInfo[2])
|
||||
set(3, 8, formatInfo[3])
|
||||
set(4, 8, formatInfo[4])
|
||||
set(5, 8, formatInfo[5])
|
||||
set(7, 8, formatInfo[6])
|
||||
set(8, 8, formatInfo[7])
|
||||
set(8, 7, formatInfo[8])
|
||||
set(8, 5, formatInfo[9])
|
||||
set(8, 4, formatInfo[10])
|
||||
set(8, 3, formatInfo[11])
|
||||
set(8, 2, formatInfo[12])
|
||||
set(8, 1, formatInfo[13])
|
||||
set(8, 0, formatInfo[14])
|
||||
|
||||
set(8, dim-1, formatInfo[0])
|
||||
set(8, dim-2, formatInfo[1])
|
||||
set(8, dim-3, formatInfo[2])
|
||||
set(8, dim-4, formatInfo[3])
|
||||
set(8, dim-5, formatInfo[4])
|
||||
set(8, dim-6, formatInfo[5])
|
||||
set(8, dim-7, formatInfo[6])
|
||||
set(dim-8, 8, formatInfo[7])
|
||||
set(dim-7, 8, formatInfo[8])
|
||||
set(dim-6, 8, formatInfo[9])
|
||||
set(dim-5, 8, formatInfo[10])
|
||||
set(dim-4, 8, formatInfo[11])
|
||||
set(dim-3, 8, formatInfo[12])
|
||||
set(dim-2, 8, formatInfo[13])
|
||||
set(dim-1, 8, formatInfo[14])
|
||||
}
|
||||
}
|
||||
|
||||
var versionInfoBitsByVersion = map[byte][]bool{
|
||||
7: []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
|
||||
8: []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
|
||||
9: []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
|
||||
10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
|
||||
11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
|
||||
12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
|
||||
13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
|
||||
14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
|
||||
15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
|
||||
16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
|
||||
17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
|
||||
18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
|
||||
19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
|
||||
20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
|
||||
21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
|
||||
22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
|
||||
23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
|
||||
24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
|
||||
25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
|
||||
26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
|
||||
27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
|
||||
28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
|
||||
29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
|
||||
30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
|
||||
31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
|
||||
32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
|
||||
33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
|
||||
34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
|
||||
35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
|
||||
36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
|
||||
37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
|
||||
38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
|
||||
39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
|
||||
40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
|
||||
}
|
||||
|
||||
func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
|
||||
versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
|
||||
|
||||
if ok && len(versionInfoBits) > 0 {
|
||||
for i := 0; i < len(versionInfoBits); i++ {
|
||||
x := (vi.modulWidth() - 11) + i%3
|
||||
y := i / 3
|
||||
set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
|
||||
set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
|
||||
for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
|
||||
bl.AddBit(false)
|
||||
}
|
||||
|
||||
for bl.Len()%8 != 0 {
|
||||
bl.AddBit(false)
|
||||
}
|
||||
|
||||
for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
|
||||
if i%2 == 0 {
|
||||
bl.AddByte(236)
|
||||
} else {
|
||||
bl.AddByte(17)
|
||||
}
|
||||
}
|
||||
}
|
29
vendor/github.com/boombuler/barcode/qr/errorcorrection.go
generated
vendored
29
vendor/github.com/boombuler/barcode/qr/errorcorrection.go
generated
vendored
|
@ -1,29 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type errorCorrection struct {
|
||||
rs *utils.ReedSolomonEncoder
|
||||
}
|
||||
|
||||
var ec = newErrorCorrection()
|
||||
|
||||
func newErrorCorrection() *errorCorrection {
|
||||
fld := utils.NewGaloisField(285, 256, 0)
|
||||
return &errorCorrection{utils.NewReedSolomonEncoder(fld)}
|
||||
}
|
||||
|
||||
func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
|
||||
dataInts := make([]int, len(data))
|
||||
for i := 0; i < len(data); i++ {
|
||||
dataInts[i] = int(data[i])
|
||||
}
|
||||
res := ec.rs.Encode(dataInts, int(eccCount))
|
||||
result := make([]byte, len(res))
|
||||
for i := 0; i < len(res); i++ {
|
||||
result[i] = byte(res[i])
|
||||
}
|
||||
return result
|
||||
}
|
56
vendor/github.com/boombuler/barcode/qr/numeric.go
generated
vendored
56
vendor/github.com/boombuler/barcode/qr/numeric.go
generated
vendored
|
@ -1,56 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
contentBitCount := (len(content) / 3) * 10
|
||||
switch len(content) % 3 {
|
||||
case 1:
|
||||
contentBitCount += 4
|
||||
case 2:
|
||||
contentBitCount += 7
|
||||
}
|
||||
vi := findSmallestVersionInfo(ecl, numericMode, contentBitCount)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(numericMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(numericMode))
|
||||
|
||||
for pos := 0; pos < len(content); pos += 3 {
|
||||
var curStr string
|
||||
if pos+3 <= len(content) {
|
||||
curStr = content[pos : pos+3]
|
||||
} else {
|
||||
curStr = content[pos:]
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(curStr)
|
||||
if err != nil || i < 0 {
|
||||
return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, Numeric)
|
||||
}
|
||||
var bitCnt byte
|
||||
switch len(curStr) % 3 {
|
||||
case 0:
|
||||
bitCnt = 10
|
||||
case 1:
|
||||
bitCnt = 4
|
||||
break
|
||||
case 2:
|
||||
bitCnt = 7
|
||||
break
|
||||
}
|
||||
|
||||
res.AddBits(i, bitCnt)
|
||||
}
|
||||
|
||||
addPaddingAndTerminator(res, vi)
|
||||
return res, vi, nil
|
||||
}
|
166
vendor/github.com/boombuler/barcode/qr/qrcode.go
generated
vendored
166
vendor/github.com/boombuler/barcode/qr/qrcode.go
generated
vendored
|
@ -1,166 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
type qrcode struct {
|
||||
dimension int
|
||||
data *utils.BitList
|
||||
content string
|
||||
}
|
||||
|
||||
func (qr *qrcode) Content() string {
|
||||
return qr.content
|
||||
}
|
||||
|
||||
func (qr *qrcode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{barcode.TypeQR, 2}
|
||||
}
|
||||
|
||||
func (qr *qrcode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (qr *qrcode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, qr.dimension, qr.dimension)
|
||||
}
|
||||
|
||||
func (qr *qrcode) At(x, y int) color.Color {
|
||||
if qr.Get(x, y) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (qr *qrcode) Get(x, y int) bool {
|
||||
return qr.data.GetBit(x*qr.dimension + y)
|
||||
}
|
||||
|
||||
func (qr *qrcode) Set(x, y int, val bool) {
|
||||
qr.data.SetBit(x*qr.dimension+y, val)
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenalty() uint {
|
||||
return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule1() uint {
|
||||
var result uint
|
||||
for x := 0; x < qr.dimension; x++ {
|
||||
checkForX := false
|
||||
var cntX uint
|
||||
checkForY := false
|
||||
var cntY uint
|
||||
|
||||
for y := 0; y < qr.dimension; y++ {
|
||||
if qr.Get(x, y) == checkForX {
|
||||
cntX++
|
||||
} else {
|
||||
checkForX = !checkForX
|
||||
if cntX >= 5 {
|
||||
result += cntX - 2
|
||||
}
|
||||
cntX = 1
|
||||
}
|
||||
|
||||
if qr.Get(y, x) == checkForY {
|
||||
cntY++
|
||||
} else {
|
||||
checkForY = !checkForY
|
||||
if cntY >= 5 {
|
||||
result += cntY - 2
|
||||
}
|
||||
cntY = 1
|
||||
}
|
||||
}
|
||||
|
||||
if cntX >= 5 {
|
||||
result += cntX - 2
|
||||
}
|
||||
if cntY >= 5 {
|
||||
result += cntY - 2
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule2() uint {
|
||||
var result uint
|
||||
for x := 0; x < qr.dimension-1; x++ {
|
||||
for y := 0; y < qr.dimension-1; y++ {
|
||||
check := qr.Get(x, y)
|
||||
if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
|
||||
result += 3
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule3() uint {
|
||||
pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
|
||||
pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
|
||||
|
||||
var result uint
|
||||
for x := 0; x <= qr.dimension-len(pattern1); x++ {
|
||||
for y := 0; y < qr.dimension; y++ {
|
||||
pattern1XFound := true
|
||||
pattern2XFound := true
|
||||
pattern1YFound := true
|
||||
pattern2YFound := true
|
||||
|
||||
for i := 0; i < len(pattern1); i++ {
|
||||
iv := qr.Get(x+i, y)
|
||||
if iv != pattern1[i] {
|
||||
pattern1XFound = false
|
||||
}
|
||||
if iv != pattern2[i] {
|
||||
pattern2XFound = false
|
||||
}
|
||||
iv = qr.Get(y, x+i)
|
||||
if iv != pattern1[i] {
|
||||
pattern1YFound = false
|
||||
}
|
||||
if iv != pattern2[i] {
|
||||
pattern2YFound = false
|
||||
}
|
||||
}
|
||||
if pattern1XFound || pattern2XFound {
|
||||
result += 40
|
||||
}
|
||||
if pattern1YFound || pattern2YFound {
|
||||
result += 40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (qr *qrcode) calcPenaltyRule4() uint {
|
||||
totalNum := qr.data.Len()
|
||||
trueCnt := 0
|
||||
for i := 0; i < totalNum; i++ {
|
||||
if qr.data.GetBit(i) {
|
||||
trueCnt++
|
||||
}
|
||||
}
|
||||
percDark := float64(trueCnt) * 100 / float64(totalNum)
|
||||
floor := math.Abs(math.Floor(percDark/5) - 10)
|
||||
ceil := math.Abs(math.Ceil(percDark/5) - 10)
|
||||
return uint(math.Min(floor, ceil) * 10)
|
||||
}
|
||||
|
||||
func newBarcode(dim int) *qrcode {
|
||||
res := new(qrcode)
|
||||
res.dimension = dim
|
||||
res.data = utils.NewBitList(dim * dim)
|
||||
return res
|
||||
}
|
27
vendor/github.com/boombuler/barcode/qr/unicode.go
generated
vendored
27
vendor/github.com/boombuler/barcode/qr/unicode.go
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
package qr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/boombuler/barcode/utils"
|
||||
)
|
||||
|
||||
func encodeUnicode(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
|
||||
data := []byte(content)
|
||||
|
||||
vi := findSmallestVersionInfo(ecl, byteMode, len(data)*8)
|
||||
if vi == nil {
|
||||
return nil, nil, errors.New("To much data to encode")
|
||||
}
|
||||
|
||||
// It's not correct to add the unicode bytes to the result directly but most readers can't handle the
|
||||
// required ECI header...
|
||||
res := new(utils.BitList)
|
||||
res.AddBits(int(byteMode), 4)
|
||||
res.AddBits(len(content), vi.charCountBits(byteMode))
|
||||
for _, b := range data {
|
||||
res.AddByte(b)
|
||||
}
|
||||
addPaddingAndTerminator(res, vi)
|
||||
return res, vi, nil
|
||||
}
|
310
vendor/github.com/boombuler/barcode/qr/versioninfo.go
generated
vendored
310
vendor/github.com/boombuler/barcode/qr/versioninfo.go
generated
vendored
|
@ -1,310 +0,0 @@
|
|||
package qr
|
||||
|
||||
import "math"
|
||||
|
||||
// ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code
|
||||
type ErrorCorrectionLevel byte
|
||||
|
||||
const (
|
||||
// L recovers 7% of data
|
||||
L ErrorCorrectionLevel = iota
|
||||
// M recovers 15% of data
|
||||
M
|
||||
// Q recovers 25% of data
|
||||
Q
|
||||
// H recovers 30% of data
|
||||
H
|
||||
)
|
||||
|
||||
func (ecl ErrorCorrectionLevel) String() string {
|
||||
switch ecl {
|
||||
case L:
|
||||
return "L"
|
||||
case M:
|
||||
return "M"
|
||||
case Q:
|
||||
return "Q"
|
||||
case H:
|
||||
return "H"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
type encodingMode byte
|
||||
|
||||
const (
|
||||
numericMode encodingMode = 1
|
||||
alphaNumericMode encodingMode = 2
|
||||
byteMode encodingMode = 4
|
||||
kanjiMode encodingMode = 8
|
||||
)
|
||||
|
||||
type versionInfo struct {
|
||||
Version byte
|
||||
Level ErrorCorrectionLevel
|
||||
ErrorCorrectionCodewordsPerBlock byte
|
||||
NumberOfBlocksInGroup1 byte
|
||||
DataCodeWordsPerBlockInGroup1 byte
|
||||
NumberOfBlocksInGroup2 byte
|
||||
DataCodeWordsPerBlockInGroup2 byte
|
||||
}
|
||||
|
||||
var versionInfos = []*versionInfo{
|
||||
&versionInfo{1, L, 7, 1, 19, 0, 0},
|
||||
&versionInfo{1, M, 10, 1, 16, 0, 0},
|
||||
&versionInfo{1, Q, 13, 1, 13, 0, 0},
|
||||
&versionInfo{1, H, 17, 1, 9, 0, 0},
|
||||
&versionInfo{2, L, 10, 1, 34, 0, 0},
|
||||
&versionInfo{2, M, 16, 1, 28, 0, 0},
|
||||
&versionInfo{2, Q, 22, 1, 22, 0, 0},
|
||||
&versionInfo{2, H, 28, 1, 16, 0, 0},
|
||||
&versionInfo{3, L, 15, 1, 55, 0, 0},
|
||||
&versionInfo{3, M, 26, 1, 44, 0, 0},
|
||||
&versionInfo{3, Q, 18, 2, 17, 0, 0},
|
||||
&versionInfo{3, H, 22, 2, 13, 0, 0},
|
||||
&versionInfo{4, L, 20, 1, 80, 0, 0},
|
||||
&versionInfo{4, M, 18, 2, 32, 0, 0},
|
||||
&versionInfo{4, Q, 26, 2, 24, 0, 0},
|
||||
&versionInfo{4, H, 16, 4, 9, 0, 0},
|
||||
&versionInfo{5, L, 26, 1, 108, 0, 0},
|
||||
&versionInfo{5, M, 24, 2, 43, 0, 0},
|
||||
&versionInfo{5, Q, 18, 2, 15, 2, 16},
|
||||
&versionInfo{5, H, 22, 2, 11, 2, 12},
|
||||
&versionInfo{6, L, 18, 2, 68, 0, 0},
|
||||
&versionInfo{6, M, 16, 4, 27, 0, 0},
|
||||
&versionInfo{6, Q, 24, 4, 19, 0, 0},
|
||||
&versionInfo{6, H, 28, 4, 15, 0, 0},
|
||||
&versionInfo{7, L, 20, 2, 78, 0, 0},
|
||||
&versionInfo{7, M, 18, 4, 31, 0, 0},
|
||||
&versionInfo{7, Q, 18, 2, 14, 4, 15},
|
||||
&versionInfo{7, H, 26, 4, 13, 1, 14},
|
||||
&versionInfo{8, L, 24, 2, 97, 0, 0},
|
||||
&versionInfo{8, M, 22, 2, 38, 2, 39},
|
||||
&versionInfo{8, Q, 22, 4, 18, 2, 19},
|
||||
&versionInfo{8, H, 26, 4, 14, 2, 15},
|
||||
&versionInfo{9, L, 30, 2, 116, 0, 0},
|
||||
&versionInfo{9, M, 22, 3, 36, 2, 37},
|
||||
&versionInfo{9, Q, 20, 4, 16, 4, 17},
|
||||
&versionInfo{9, H, 24, 4, 12, 4, 13},
|
||||
&versionInfo{10, L, 18, 2, 68, 2, 69},
|
||||
&versionInfo{10, M, 26, 4, 43, 1, 44},
|
||||
&versionInfo{10, Q, 24, 6, 19, 2, 20},
|
||||
&versionInfo{10, H, 28, 6, 15, 2, 16},
|
||||
&versionInfo{11, L, 20, 4, 81, 0, 0},
|
||||
&versionInfo{11, M, 30, 1, 50, 4, 51},
|
||||
&versionInfo{11, Q, 28, 4, 22, 4, 23},
|
||||
&versionInfo{11, H, 24, 3, 12, 8, 13},
|
||||
&versionInfo{12, L, 24, 2, 92, 2, 93},
|
||||
&versionInfo{12, M, 22, 6, 36, 2, 37},
|
||||
&versionInfo{12, Q, 26, 4, 20, 6, 21},
|
||||
&versionInfo{12, H, 28, 7, 14, 4, 15},
|
||||
&versionInfo{13, L, 26, 4, 107, 0, 0},
|
||||
&versionInfo{13, M, 22, 8, 37, 1, 38},
|
||||
&versionInfo{13, Q, 24, 8, 20, 4, 21},
|
||||
&versionInfo{13, H, 22, 12, 11, 4, 12},
|
||||
&versionInfo{14, L, 30, 3, 115, 1, 116},
|
||||
&versionInfo{14, M, 24, 4, 40, 5, 41},
|
||||
&versionInfo{14, Q, 20, 11, 16, 5, 17},
|
||||
&versionInfo{14, H, 24, 11, 12, 5, 13},
|
||||
&versionInfo{15, L, 22, 5, 87, 1, 88},
|
||||
&versionInfo{15, M, 24, 5, 41, 5, 42},
|
||||
&versionInfo{15, Q, 30, 5, 24, 7, 25},
|
||||
&versionInfo{15, H, 24, 11, 12, 7, 13},
|
||||
&versionInfo{16, L, 24, 5, 98, 1, 99},
|
||||
&versionInfo{16, M, 28, 7, 45, 3, 46},
|
||||
&versionInfo{16, Q, 24, 15, 19, 2, 20},
|
||||
&versionInfo{16, H, 30, 3, 15, 13, 16},
|
||||
&versionInfo{17, L, 28, 1, 107, 5, 108},
|
||||
&versionInfo{17, M, 28, 10, 46, 1, 47},
|
||||
&versionInfo{17, Q, 28, 1, 22, 15, 23},
|
||||
&versionInfo{17, H, 28, 2, 14, 17, 15},
|
||||
&versionInfo{18, L, 30, 5, 120, 1, 121},
|
||||
&versionInfo{18, M, 26, 9, 43, 4, 44},
|
||||
&versionInfo{18, Q, 28, 17, 22, 1, 23},
|
||||
&versionInfo{18, H, 28, 2, 14, 19, 15},
|
||||
&versionInfo{19, L, 28, 3, 113, 4, 114},
|
||||
&versionInfo{19, M, 26, 3, 44, 11, 45},
|
||||
&versionInfo{19, Q, 26, 17, 21, 4, 22},
|
||||
&versionInfo{19, H, 26, 9, 13, 16, 14},
|
||||
&versionInfo{20, L, 28, 3, 107, 5, 108},
|
||||
&versionInfo{20, M, 26, 3, 41, 13, 42},
|
||||
&versionInfo{20, Q, 30, 15, 24, 5, 25},
|
||||
&versionInfo{20, H, 28, 15, 15, 10, 16},
|
||||
&versionInfo{21, L, 28, 4, 116, 4, 117},
|
||||
&versionInfo{21, M, 26, 17, 42, 0, 0},
|
||||
&versionInfo{21, Q, 28, 17, 22, 6, 23},
|
||||
&versionInfo{21, H, 30, 19, 16, 6, 17},
|
||||
&versionInfo{22, L, 28, 2, 111, 7, 112},
|
||||
&versionInfo{22, M, 28, 17, 46, 0, 0},
|
||||
&versionInfo{22, Q, 30, 7, 24, 16, 25},
|
||||
&versionInfo{22, H, 24, 34, 13, 0, 0},
|
||||
&versionInfo{23, L, 30, 4, 121, 5, 122},
|
||||
&versionInfo{23, M, 28, 4, 47, 14, 48},
|
||||
&versionInfo{23, Q, 30, 11, 24, 14, 25},
|
||||
&versionInfo{23, H, 30, 16, 15, 14, 16},
|
||||
&versionInfo{24, L, 30, 6, 117, 4, 118},
|
||||
&versionInfo{24, M, 28, 6, 45, 14, 46},
|
||||
&versionInfo{24, Q, 30, 11, 24, 16, 25},
|
||||
&versionInfo{24, H, 30, 30, 16, 2, 17},
|
||||
&versionInfo{25, L, 26, 8, 106, 4, 107},
|
||||
&versionInfo{25, M, 28, 8, 47, 13, 48},
|
||||
&versionInfo{25, Q, 30, 7, 24, 22, 25},
|
||||
&versionInfo{25, H, 30, 22, 15, 13, 16},
|
||||
&versionInfo{26, L, 28, 10, 114, 2, 115},
|
||||
&versionInfo{26, M, 28, 19, 46, 4, 47},
|
||||
&versionInfo{26, Q, 28, 28, 22, 6, 23},
|
||||
&versionInfo{26, H, 30, 33, 16, 4, 17},
|
||||
&versionInfo{27, L, 30, 8, 122, 4, 123},
|
||||
&versionInfo{27, M, 28, 22, 45, 3, 46},
|
||||
&versionInfo{27, Q, 30, 8, 23, 26, 24},
|
||||
&versionInfo{27, H, 30, 12, 15, 28, 16},
|
||||
&versionInfo{28, L, 30, 3, 117, 10, 118},
|
||||
&versionInfo{28, M, 28, 3, 45, 23, 46},
|
||||
&versionInfo{28, Q, 30, 4, 24, 31, 25},
|
||||
&versionInfo{28, H, 30, 11, 15, 31, 16},
|
||||
&versionInfo{29, L, 30, 7, 116, 7, 117},
|
||||
&versionInfo{29, M, 28, 21, 45, 7, 46},
|
||||
&versionInfo{29, Q, 30, 1, 23, 37, 24},
|
||||
&versionInfo{29, H, 30, 19, 15, 26, 16},
|
||||
&versionInfo{30, L, 30, 5, 115, 10, 116},
|
||||
&versionInfo{30, M, 28, 19, 47, 10, 48},
|
||||
&versionInfo{30, Q, 30, 15, 24, 25, 25},
|
||||
&versionInfo{30, H, 30, 23, 15, 25, 16},
|
||||
&versionInfo{31, L, 30, 13, 115, 3, 116},
|
||||
&versionInfo{31, M, 28, 2, 46, 29, 47},
|
||||
&versionInfo{31, Q, 30, 42, 24, 1, 25},
|
||||
&versionInfo{31, H, 30, 23, 15, 28, 16},
|
||||
&versionInfo{32, L, 30, 17, 115, 0, 0},
|
||||
&versionInfo{32, M, 28, 10, 46, 23, 47},
|
||||
&versionInfo{32, Q, 30, 10, 24, 35, 25},
|
||||
&versionInfo{32, H, 30, 19, 15, 35, 16},
|
||||
&versionInfo{33, L, 30, 17, 115, 1, 116},
|
||||
&versionInfo{33, M, 28, 14, 46, 21, 47},
|
||||
&versionInfo{33, Q, 30, 29, 24, 19, 25},
|
||||
&versionInfo{33, H, 30, 11, 15, 46, 16},
|
||||
&versionInfo{34, L, 30, 13, 115, 6, 116},
|
||||
&versionInfo{34, M, 28, 14, 46, 23, 47},
|
||||
&versionInfo{34, Q, 30, 44, 24, 7, 25},
|
||||
&versionInfo{34, H, 30, 59, 16, 1, 17},
|
||||
&versionInfo{35, L, 30, 12, 121, 7, 122},
|
||||
&versionInfo{35, M, 28, 12, 47, 26, 48},
|
||||
&versionInfo{35, Q, 30, 39, 24, 14, 25},
|
||||
&versionInfo{35, H, 30, 22, 15, 41, 16},
|
||||
&versionInfo{36, L, 30, 6, 121, 14, 122},
|
||||
&versionInfo{36, M, 28, 6, 47, 34, 48},
|
||||
&versionInfo{36, Q, 30, 46, 24, 10, 25},
|
||||
&versionInfo{36, H, 30, 2, 15, 64, 16},
|
||||
&versionInfo{37, L, 30, 17, 122, 4, 123},
|
||||
&versionInfo{37, M, 28, 29, 46, 14, 47},
|
||||
&versionInfo{37, Q, 30, 49, 24, 10, 25},
|
||||
&versionInfo{37, H, 30, 24, 15, 46, 16},
|
||||
&versionInfo{38, L, 30, 4, 122, 18, 123},
|
||||
&versionInfo{38, M, 28, 13, 46, 32, 47},
|
||||
&versionInfo{38, Q, 30, 48, 24, 14, 25},
|
||||
&versionInfo{38, H, 30, 42, 15, 32, 16},
|
||||
&versionInfo{39, L, 30, 20, 117, 4, 118},
|
||||
&versionInfo{39, M, 28, 40, 47, 7, 48},
|
||||
&versionInfo{39, Q, 30, 43, 24, 22, 25},
|
||||
&versionInfo{39, H, 30, 10, 15, 67, 16},
|
||||
&versionInfo{40, L, 30, 19, 118, 6, 119},
|
||||
&versionInfo{40, M, 28, 18, 47, 31, 48},
|
||||
&versionInfo{40, Q, 30, 34, 24, 34, 25},
|
||||
&versionInfo{40, H, 30, 20, 15, 61, 16},
|
||||
}
|
||||
|
||||
func (vi *versionInfo) totalDataBytes() int {
|
||||
g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1)
|
||||
g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2)
|
||||
return (g1Data + g2Data)
|
||||
}
|
||||
|
||||
func (vi *versionInfo) charCountBits(m encodingMode) byte {
|
||||
switch m {
|
||||
case numericMode:
|
||||
if vi.Version < 10 {
|
||||
return 10
|
||||
} else if vi.Version < 27 {
|
||||
return 12
|
||||
}
|
||||
return 14
|
||||
|
||||
case alphaNumericMode:
|
||||
if vi.Version < 10 {
|
||||
return 9
|
||||
} else if vi.Version < 27 {
|
||||
return 11
|
||||
}
|
||||
return 13
|
||||
|
||||
case byteMode:
|
||||
if vi.Version < 10 {
|
||||
return 8
|
||||
}
|
||||
return 16
|
||||
|
||||
case kanjiMode:
|
||||
if vi.Version < 10 {
|
||||
return 8
|
||||
} else if vi.Version < 27 {
|
||||
return 10
|
||||
}
|
||||
return 12
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (vi *versionInfo) modulWidth() int {
|
||||
return ((int(vi.Version) - 1) * 4) + 21
|
||||
}
|
||||
|
||||
func (vi *versionInfo) alignmentPatternPlacements() []int {
|
||||
if vi.Version == 1 {
|
||||
return make([]int, 0)
|
||||
}
|
||||
|
||||
first := 6
|
||||
last := vi.modulWidth() - 7
|
||||
space := float64(last - first)
|
||||
count := int(math.Ceil(space/28)) + 1
|
||||
|
||||
result := make([]int, count)
|
||||
result[0] = first
|
||||
result[len(result)-1] = last
|
||||
if count > 2 {
|
||||
step := int(math.Ceil(float64(last-first) / float64(count-1)))
|
||||
if step%2 == 1 {
|
||||
frac := float64(last-first) / float64(count-1)
|
||||
_, x := math.Modf(frac)
|
||||
if x >= 0.5 {
|
||||
frac = math.Ceil(frac)
|
||||
} else {
|
||||
frac = math.Floor(frac)
|
||||
}
|
||||
|
||||
if int(frac)%2 == 0 {
|
||||
step--
|
||||
} else {
|
||||
step++
|
||||
}
|
||||
}
|
||||
|
||||
for i := 1; i <= count-2; i++ {
|
||||
result[i] = last - (step * (count - 1 - i))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo {
|
||||
dataBits = dataBits + 4 // mode indicator
|
||||
for _, vi := range versionInfos {
|
||||
if vi.Level == ecl {
|
||||
if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) {
|
||||
return vi
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
134
vendor/github.com/boombuler/barcode/scaledbarcode.go
generated
vendored
134
vendor/github.com/boombuler/barcode/scaledbarcode.go
generated
vendored
|
@ -1,134 +0,0 @@
|
|||
package barcode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
type wrapFunc func(x, y int) color.Color
|
||||
|
||||
type scaledBarcode struct {
|
||||
wrapped Barcode
|
||||
wrapperFunc wrapFunc
|
||||
rect image.Rectangle
|
||||
}
|
||||
|
||||
type intCSscaledBC struct {
|
||||
scaledBarcode
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Content() string {
|
||||
return bc.wrapped.Content()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Metadata() Metadata {
|
||||
return bc.wrapped.Metadata()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) ColorModel() color.Model {
|
||||
return bc.wrapped.ColorModel()
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) Bounds() image.Rectangle {
|
||||
return bc.rect
|
||||
}
|
||||
|
||||
func (bc *scaledBarcode) At(x, y int) color.Color {
|
||||
return bc.wrapperFunc(x, y)
|
||||
}
|
||||
|
||||
func (bc *intCSscaledBC) CheckSum() int {
|
||||
if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
|
||||
return cs.CheckSum()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Scale returns a resized barcode with the given width and height.
|
||||
func Scale(bc Barcode, width, height int) (Barcode, error) {
|
||||
switch bc.Metadata().Dimensions {
|
||||
case 1:
|
||||
return scale1DCode(bc, width, height)
|
||||
case 2:
|
||||
return scale2DCode(bc, width, height)
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported barcode format")
|
||||
}
|
||||
|
||||
func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
|
||||
result := &scaledBarcode{
|
||||
wrapped: wrapped,
|
||||
wrapperFunc: wrapperFunc,
|
||||
rect: rect,
|
||||
}
|
||||
|
||||
if _, ok := wrapped.(BarcodeIntCS); ok {
|
||||
return &intCSscaledBC{*result}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
|
||||
orgBounds := bc.Bounds()
|
||||
orgWidth := orgBounds.Max.X - orgBounds.Min.X
|
||||
orgHeight := orgBounds.Max.Y - orgBounds.Min.Y
|
||||
|
||||
factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
|
||||
if factor <= 0 {
|
||||
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
|
||||
}
|
||||
|
||||
offsetX := (width - (orgWidth * factor)) / 2
|
||||
offsetY := (height - (orgHeight * factor)) / 2
|
||||
|
||||
wrap := func(x, y int) color.Color {
|
||||
if x < offsetX || y < offsetY {
|
||||
return color.White
|
||||
}
|
||||
x = (x - offsetX) / factor
|
||||
y = (y - offsetY) / factor
|
||||
if x >= orgWidth || y >= orgHeight {
|
||||
return color.White
|
||||
}
|
||||
return bc.At(x, y)
|
||||
}
|
||||
|
||||
return newScaledBC(
|
||||
bc,
|
||||
wrap,
|
||||
image.Rect(0, 0, width, height),
|
||||
), nil
|
||||
}
|
||||
|
||||
func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
|
||||
orgBounds := bc.Bounds()
|
||||
orgWidth := orgBounds.Max.X - orgBounds.Min.X
|
||||
factor := int(float64(width) / float64(orgWidth))
|
||||
|
||||
if factor <= 0 {
|
||||
return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
|
||||
}
|
||||
offsetX := (width - (orgWidth * factor)) / 2
|
||||
|
||||
wrap := func(x, y int) color.Color {
|
||||
if x < offsetX {
|
||||
return color.White
|
||||
}
|
||||
x = (x - offsetX) / factor
|
||||
|
||||
if x >= orgWidth {
|
||||
return color.White
|
||||
}
|
||||
return bc.At(x, 0)
|
||||
}
|
||||
|
||||
return newScaledBC(
|
||||
bc,
|
||||
wrap,
|
||||
image.Rect(0, 0, width, height),
|
||||
), nil
|
||||
}
|
57
vendor/github.com/boombuler/barcode/utils/base1dcode.go
generated
vendored
57
vendor/github.com/boombuler/barcode/utils/base1dcode.go
generated
vendored
|
@ -1,57 +0,0 @@
|
|||
// Package utils contain some utilities which are needed to create barcodes
|
||||
package utils
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
)
|
||||
|
||||
type base1DCode struct {
|
||||
*BitList
|
||||
kind string
|
||||
content string
|
||||
}
|
||||
|
||||
type base1DCodeIntCS struct {
|
||||
base1DCode
|
||||
checksum int
|
||||
}
|
||||
|
||||
func (c *base1DCode) Content() string {
|
||||
return c.content
|
||||
}
|
||||
|
||||
func (c *base1DCode) Metadata() barcode.Metadata {
|
||||
return barcode.Metadata{c.kind, 1}
|
||||
}
|
||||
|
||||
func (c *base1DCode) ColorModel() color.Model {
|
||||
return color.Gray16Model
|
||||
}
|
||||
|
||||
func (c *base1DCode) Bounds() image.Rectangle {
|
||||
return image.Rect(0, 0, c.Len(), 1)
|
||||
}
|
||||
|
||||
func (c *base1DCode) At(x, y int) color.Color {
|
||||
if c.GetBit(x) {
|
||||
return color.Black
|
||||
}
|
||||
return color.White
|
||||
}
|
||||
|
||||
func (c *base1DCodeIntCS) CheckSum() int {
|
||||
return c.checksum
|
||||
}
|
||||
|
||||
// New1DCodeIntCheckSum creates a new 1D barcode where the bars are represented by the bits in the bars BitList
|
||||
func New1DCodeIntCheckSum(codeKind, content string, bars *BitList, checksum int) barcode.BarcodeIntCS {
|
||||
return &base1DCodeIntCS{base1DCode{bars, codeKind, content}, checksum}
|
||||
}
|
||||
|
||||
// New1DCode creates a new 1D barcode where the bars are represented by the bits in the bars BitList
|
||||
func New1DCode(codeKind, content string, bars *BitList) barcode.Barcode {
|
||||
return &base1DCode{bars, codeKind, content}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user