Migrate all timestamps to real iso dates (#594)
Fix query param name Add option to include null results when filtering Always set db time to gmt Fix null filter Fix timezone setting for todoist parsing Fix timezone setting for wunderlist parsing Fix import Fix caldav reminder parsing Use timezone from config Add error and test for invalid filter values Fix integration tests Remove task collection date hack Fix task filter Fix lint Fix tests and fixtures for date timezone stuff Properly set timezone Change fixtures time zone to gmt Set db timezone Set created and updated timestamps for all fixtures Fix lint Fix test fixtures Fix misspell Fix test fixtures Partially fix tests Remove timeutil package Remove adding _unix suffix hack Remove _unix suffix Move all timeutil.TimeStamp to time.Time Remove all Unix suffixes in field names Add better error messages when running migrations Make sure to not migrate 0 unix timestamps to 1970 iso dates Add migration script for sqlite Add converting sqlite values Convert 0 unix timestamps to null in postgres Convert 0 to null in timestamps Automatically rename _unix suffix Add all tables and columns for migration Fix sql migration query for mysql Fail with an error if trying to use an unsupported dbms Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/594
This commit is contained in:
parent
e17cac854a
commit
08205008e7
83 changed files with 2208 additions and 1257 deletions
2
Makefile
2
Makefile
|
@ -219,7 +219,7 @@ gocyclo-check:
|
||||||
go get -u github.com/fzipp/gocyclo; \
|
go get -u github.com/fzipp/gocyclo; \
|
||||||
go install $(GOFLAGS) github.com/fzipp/gocyclo; \
|
go install $(GOFLAGS) github.com/fzipp/gocyclo; \
|
||||||
fi
|
fi
|
||||||
for S in $(GOFILES); do gocyclo -over 29 $$S || exit 1; done;
|
for S in $(GOFILES); do gocyclo -over 33 $$S || exit 1; done;
|
||||||
|
|
||||||
.PHONY: static-check
|
.PHONY: static-check
|
||||||
static-check:
|
static-check:
|
||||||
|
|
|
@ -78,6 +78,7 @@ This document describes the different errors Vikunja can return.
|
||||||
| 4016 | 403 | Invalid task field. |
|
| 4016 | 403 | Invalid task field. |
|
||||||
| 4017 | 403 | Invalid task filter comparator. |
|
| 4017 | 403 | Invalid task filter comparator. |
|
||||||
| 4018 | 403 | Invalid task filter concatinator. |
|
| 4018 | 403 | Invalid task filter concatinator. |
|
||||||
|
| 4019 | 403 | Invalid task filter value. |
|
||||||
|
|
||||||
### Namespace
|
### Namespace
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -46,6 +46,7 @@ require (
|
||||||
github.com/labstack/gommon v0.3.0
|
github.com/labstack/gommon v0.3.0
|
||||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
||||||
github.com/lib/pq v1.7.0
|
github.com/lib/pq v1.7.0
|
||||||
|
github.com/magiconair/properties v1.8.1
|
||||||
github.com/mailru/easyjson v0.7.0 // indirect
|
github.com/mailru/easyjson v0.7.0 // indirect
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
package caldav
|
package caldav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,37 +36,37 @@ type Event struct {
|
||||||
UID string
|
UID string
|
||||||
Alarms []Alarm
|
Alarms []Alarm
|
||||||
|
|
||||||
Timestamp timeutil.TimeStamp
|
Timestamp time.Time
|
||||||
Start timeutil.TimeStamp
|
Start time.Time
|
||||||
End timeutil.TimeStamp
|
End time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo holds a single VTODO
|
// Todo holds a single VTODO
|
||||||
type Todo struct {
|
type Todo struct {
|
||||||
// Required
|
// Required
|
||||||
Timestamp timeutil.TimeStamp
|
Timestamp time.Time
|
||||||
UID string
|
UID string
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
Summary string
|
Summary string
|
||||||
Description string
|
Description string
|
||||||
Completed timeutil.TimeStamp
|
Completed time.Time
|
||||||
Organizer *user.User
|
Organizer *user.User
|
||||||
Priority int64 // 0-9, 1 is highest
|
Priority int64 // 0-9, 1 is highest
|
||||||
RelatedToUID string
|
RelatedToUID string
|
||||||
|
|
||||||
Start timeutil.TimeStamp
|
Start time.Time
|
||||||
End timeutil.TimeStamp
|
End time.Time
|
||||||
DueDate timeutil.TimeStamp
|
DueDate time.Time
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
|
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp // last-mod
|
Updated time.Time // last-mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alarm holds infos about an alarm from a caldav event
|
// Alarm holds infos about an alarm from a caldav event
|
||||||
type Alarm struct {
|
type Alarm struct {
|
||||||
Time timeutil.TimeStamp
|
Time time.Time
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,11 +142,11 @@ UID:` + t.UID + `
|
||||||
DTSTAMP:` + makeCalDavTimeFromTimeStamp(t.Timestamp) + `
|
DTSTAMP:` + makeCalDavTimeFromTimeStamp(t.Timestamp) + `
|
||||||
SUMMARY:` + t.Summary
|
SUMMARY:` + t.Summary
|
||||||
|
|
||||||
if t.Start != 0 {
|
if t.Start.Unix() > 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
DTSTART: ` + makeCalDavTimeFromTimeStamp(t.Start)
|
DTSTART: ` + makeCalDavTimeFromTimeStamp(t.Start)
|
||||||
}
|
}
|
||||||
if t.End != 0 {
|
if t.End.Unix() > 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
DTEND: ` + makeCalDavTimeFromTimeStamp(t.End)
|
DTEND: ` + makeCalDavTimeFromTimeStamp(t.End)
|
||||||
}
|
}
|
||||||
|
@ -153,7 +154,7 @@ DTEND: ` + makeCalDavTimeFromTimeStamp(t.End)
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
DESCRIPTION:` + t.Description
|
DESCRIPTION:` + t.Description
|
||||||
}
|
}
|
||||||
if t.Completed != 0 {
|
if t.Completed.Unix() > 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
COMPLETED: ` + makeCalDavTimeFromTimeStamp(t.Completed)
|
COMPLETED: ` + makeCalDavTimeFromTimeStamp(t.Completed)
|
||||||
}
|
}
|
||||||
|
@ -167,12 +168,12 @@ ORGANIZER;CN=:` + t.Organizer.Username
|
||||||
RELATED-TO:` + t.RelatedToUID
|
RELATED-TO:` + t.RelatedToUID
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.DueDate != 0 {
|
if t.DueDate.Unix() > 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
DUE:` + makeCalDavTimeFromTimeStamp(t.DueDate)
|
DUE:` + makeCalDavTimeFromTimeStamp(t.DueDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Created != 0 {
|
if t.Created.Unix() > 0 {
|
||||||
caldavtodos += `
|
caldavtodos += `
|
||||||
CREATED:` + makeCalDavTimeFromTimeStamp(t.Created)
|
CREATED:` + makeCalDavTimeFromTimeStamp(t.Created)
|
||||||
}
|
}
|
||||||
|
@ -200,20 +201,19 @@ END:VCALENDAR` // Need a line break
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCalDavTimeFromTimeStamp(ts timeutil.TimeStamp) (caldavtime string) {
|
func makeCalDavTimeFromTimeStamp(ts time.Time) (caldavtime string) {
|
||||||
tz, _ := time.LoadLocation("UTC")
|
return ts.In(config.GetTimeZone()).Format(DateFormat)
|
||||||
return ts.ToTime().In(tz).Format(DateFormat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcAlarmDateFromReminder(eventStartUnix, reminderUnix timeutil.TimeStamp) (alarmTime string) {
|
func calcAlarmDateFromReminder(eventStart, reminder time.Time) (alarmTime string) {
|
||||||
if eventStartUnix > reminderUnix {
|
diff := reminder.Sub(eventStart)
|
||||||
|
diffStr := strings.ToUpper(diff.String())
|
||||||
|
if diff < 0 {
|
||||||
alarmTime += `-`
|
alarmTime += `-`
|
||||||
|
// We append the - at the beginning of the caldav flag, that would get in the way if the minutes
|
||||||
|
// themselves are also containing it
|
||||||
|
diffStr = diffStr[1:]
|
||||||
}
|
}
|
||||||
alarmTime += `PT`
|
alarmTime += `PT` + diffStr
|
||||||
diff := eventStartUnix - reminderUnix
|
|
||||||
if diff < 0 { // Make it positive
|
|
||||||
diff = diff * -1
|
|
||||||
}
|
|
||||||
alarmTime += strconv.Itoa(int(diff/60)) + "M"
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,12 @@
|
||||||
|
|
||||||
package caldav
|
package caldav
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func TestParseEvents(t *testing.T) {
|
func TestParseEvents(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
|
@ -40,23 +45,23 @@ func TestParseEvents(t *testing.T) {
|
||||||
Summary: "Event #1",
|
Summary: "Event #1",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
UID: "randommduid",
|
UID: "randommduid",
|
||||||
Timestamp: 1543626724,
|
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543626724,
|
Start: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||||
End: 1543627824,
|
End: time.Unix(1543627824, 0).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Summary: "Event #2",
|
Summary: "Event #2",
|
||||||
UID: "randommduidd",
|
UID: "randommduidd",
|
||||||
Timestamp: 1543726724,
|
Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543726724,
|
Start: time.Unix(1543726724, 0).In(config.GetTimeZone()),
|
||||||
End: 1543738724,
|
End: time.Unix(1543738724, 0).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Summary: "Event #3 with empty uid",
|
Summary: "Event #3 with empty uid",
|
||||||
UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83",
|
UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83",
|
||||||
Timestamp: 1543726824,
|
Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543726824,
|
Start: time.Unix(1543726824, 0).In(config.GetTimeZone()),
|
||||||
End: 1543727000,
|
End: time.Unix(1543727000, 0).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -104,44 +109,44 @@ END:VCALENDAR`,
|
||||||
Summary: "Event #1",
|
Summary: "Event #1",
|
||||||
Description: "Lorem Ipsum",
|
Description: "Lorem Ipsum",
|
||||||
UID: "randommduid",
|
UID: "randommduid",
|
||||||
Timestamp: 1543626724,
|
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543626724,
|
Start: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||||
End: 1543627824,
|
End: time.Unix(1543627824, 0).In(config.GetTimeZone()),
|
||||||
Alarms: []Alarm{
|
Alarms: []Alarm{
|
||||||
{Time: 1543626524},
|
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626224},
|
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626024},
|
{Time: time.Unix(1543626024, 0)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Summary: "Event #2",
|
Summary: "Event #2",
|
||||||
UID: "randommduidd",
|
UID: "randommduidd",
|
||||||
Timestamp: 1543726724,
|
Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543726724,
|
Start: time.Unix(1543726724, 0).In(config.GetTimeZone()),
|
||||||
End: 1543738724,
|
End: time.Unix(1543738724, 0).In(config.GetTimeZone()),
|
||||||
Alarms: []Alarm{
|
Alarms: []Alarm{
|
||||||
{Time: 1543626524},
|
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626224},
|
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626024},
|
{Time: time.Unix(1543626024, 0).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Summary: "Event #3 with empty uid",
|
Summary: "Event #3 with empty uid",
|
||||||
Timestamp: 1543726824,
|
Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()),
|
||||||
Start: 1543726824,
|
Start: time.Unix(1543726824, 0).In(config.GetTimeZone()),
|
||||||
End: 1543727000,
|
End: time.Unix(1543727000, 0).In(config.GetTimeZone()),
|
||||||
Alarms: []Alarm{
|
Alarms: []Alarm{
|
||||||
{Time: 1543626524},
|
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626224},
|
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543626024},
|
{Time: time.Unix(1543626024, 0).In(config.GetTimeZone())},
|
||||||
{Time: 1543826824},
|
{Time: time.Unix(1543826824, 0).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Summary: "Event #4 without any",
|
Summary: "Event #4 without any",
|
||||||
Timestamp: 1543726824,
|
Timestamp: time.Unix(1543726824, 0),
|
||||||
Start: 1543726824,
|
Start: time.Unix(1543726824, 0),
|
||||||
End: 1543727000,
|
End: time.Unix(1543727000, 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -159,17 +164,17 @@ DTSTAMP:20181201T011204
|
||||||
DTSTART:20181201T011204
|
DTSTART:20181201T011204
|
||||||
DTEND:20181201T013024
|
DTEND:20181201T013024
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT3M
|
TRIGGER:-PT3M20S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #1
|
DESCRIPTION:Event #1
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT8M
|
TRIGGER:-PT8M20S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #1
|
DESCRIPTION:Event #1
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT11M
|
TRIGGER:-PT11M40S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #1
|
DESCRIPTION:Event #1
|
||||||
END:VALARM
|
END:VALARM
|
||||||
|
@ -182,17 +187,17 @@ DTSTAMP:20181202T045844
|
||||||
DTSTART:20181202T045844
|
DTSTART:20181202T045844
|
||||||
DTEND:20181202T081844
|
DTEND:20181202T081844
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1670M
|
TRIGGER:-PT27H50M0S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #2
|
DESCRIPTION:Event #2
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1675M
|
TRIGGER:-PT27H55M0S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #2
|
DESCRIPTION:Event #2
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1678M
|
TRIGGER:-PT27H58M20S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #2
|
DESCRIPTION:Event #2
|
||||||
END:VALARM
|
END:VALARM
|
||||||
|
@ -205,22 +210,22 @@ DTSTAMP:20181202T050024
|
||||||
DTSTART:20181202T050024
|
DTSTART:20181202T050024
|
||||||
DTEND:20181202T050320
|
DTEND:20181202T050320
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1671M
|
TRIGGER:-PT27H51M40S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #3 with empty uid
|
DESCRIPTION:Event #3 with empty uid
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1676M
|
TRIGGER:-PT27H56M40S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #3 with empty uid
|
DESCRIPTION:Event #3 with empty uid
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:-PT1680M
|
TRIGGER:-PT28H0M0S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #3 with empty uid
|
DESCRIPTION:Event #3 with empty uid
|
||||||
END:VALARM
|
END:VALARM
|
||||||
BEGIN:VALARM
|
BEGIN:VALARM
|
||||||
TRIGGER:PT1666M
|
TRIGGER:PT27H46M40S
|
||||||
ACTION:DISPLAY
|
ACTION:DISPLAY
|
||||||
DESCRIPTION:Event #3 with empty uid
|
DESCRIPTION:Event #3 with empty uid
|
||||||
END:VALARM
|
END:VALARM
|
||||||
|
@ -238,9 +243,8 @@ END:VCALENDAR`,
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if gotCaldavevents := ParseEvents(tt.args.config, tt.args.events); gotCaldavevents != tt.wantCaldavevents {
|
gotCaldavevents := ParseEvents(tt.args.config, tt.args.events)
|
||||||
t.Errorf("ParseEvents() = %v, want %v", gotCaldavevents, tt.wantCaldavevents)
|
assert.Equal(t, gotCaldavevents, tt.wantCaldavevents)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,23 @@ func (k Key) GetStringSlice() []string {
|
||||||
return viper.GetStringSlice(string(k))
|
return viper.GetStringSlice(string(k))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var timezone *time.Location
|
||||||
|
|
||||||
|
// GetTimeZone returns the time zone configured for vikunja
|
||||||
|
// It is a separate function and not done through viper because that makes handling
|
||||||
|
// it way easier, especially when testing.
|
||||||
|
func GetTimeZone() *time.Location {
|
||||||
|
if timezone == nil {
|
||||||
|
loc, err := time.LoadLocation(ServiceTimeZone.GetString())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error parsing time zone: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
timezone = loc
|
||||||
|
}
|
||||||
|
return timezone
|
||||||
|
}
|
||||||
|
|
||||||
// Set sets a value
|
// Set sets a value
|
||||||
func (k Key) Set(i interface{}) {
|
func (k Key) Set(i interface{}) {
|
||||||
viper.Set(string(k), i)
|
viper.Set(string(k), i)
|
||||||
|
|
10
pkg/db/db.go
10
pkg/db/db.go
|
@ -62,14 +62,22 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else if config.DatabaseType.GetString() == "sqlite" {
|
||||||
// Otherwise use sqlite
|
// Otherwise use sqlite
|
||||||
engine, err = initSqliteEngine()
|
engine, err = initSqliteEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Fatalf("Unknown database type %s", config.DatabaseType.GetString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
engine.SetTZLocation(config.GetTimeZone()) // Vikunja's timezone
|
||||||
|
loc, err := time.LoadLocation("GMT") // The db data timezone
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error parsing time zone: %s", err)
|
||||||
|
}
|
||||||
|
engine.SetTZDatabase(loc)
|
||||||
engine.SetMapper(core.GonicMapper{})
|
engine.SetMapper(core.GonicMapper{})
|
||||||
logger := log.NewXormLogger("")
|
logger := log.NewXormLogger("")
|
||||||
engine.SetLogger(logger)
|
engine.SetLogger(logger)
|
||||||
|
|
|
@ -2,206 +2,206 @@
|
||||||
title: testbucket1
|
title: testbucket1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 2
|
- id: 2
|
||||||
title: testbucket2
|
title: testbucket2
|
||||||
list_id: 1
|
list_id: 1
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 3
|
- id: 3
|
||||||
title: testbucket3
|
title: testbucket3
|
||||||
list_id: 1
|
list_id: 1
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 4
|
- id: 4
|
||||||
title: testbucket4 - other list
|
title: testbucket4 - other list
|
||||||
list_id: 2
|
list_id: 2
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
# The following are not or only partly owned by user 1
|
# The following are not or only partly owned by user 1
|
||||||
- id: 5
|
- id: 5
|
||||||
title: testbucket5
|
title: testbucket5
|
||||||
list_id: 20
|
list_id: 20
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 6
|
- id: 6
|
||||||
title: testbucket6
|
title: testbucket6
|
||||||
list_id: 6
|
list_id: 6
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 7
|
- id: 7
|
||||||
title: testbucket7
|
title: testbucket7
|
||||||
list_id: 7
|
list_id: 7
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 8
|
- id: 8
|
||||||
title: testbucket8
|
title: testbucket8
|
||||||
list_id: 8
|
list_id: 8
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 9
|
- id: 9
|
||||||
title: testbucket9
|
title: testbucket9
|
||||||
list_id: 9
|
list_id: 9
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 10
|
- id: 10
|
||||||
title: testbucket10
|
title: testbucket10
|
||||||
list_id: 10
|
list_id: 10
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 11
|
- id: 11
|
||||||
title: testbucket11
|
title: testbucket11
|
||||||
list_id: 11
|
list_id: 11
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 12
|
- id: 12
|
||||||
title: testbucket13
|
title: testbucket13
|
||||||
list_id: 12
|
list_id: 12
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 13
|
- id: 13
|
||||||
title: testbucket13
|
title: testbucket13
|
||||||
list_id: 13
|
list_id: 13
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 14
|
- id: 14
|
||||||
title: testbucket14
|
title: testbucket14
|
||||||
list_id: 14
|
list_id: 14
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 15
|
- id: 15
|
||||||
title: testbucket15
|
title: testbucket15
|
||||||
list_id: 15
|
list_id: 15
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 16
|
- id: 16
|
||||||
title: testbucket16
|
title: testbucket16
|
||||||
list_id: 16
|
list_id: 16
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 17
|
- id: 17
|
||||||
title: testbucket17
|
title: testbucket17
|
||||||
list_id: 17
|
list_id: 17
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 18
|
- id: 18
|
||||||
title: testbucket18
|
title: testbucket18
|
||||||
list_id: 5
|
list_id: 5
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 19
|
- id: 19
|
||||||
title: testbucket19
|
title: testbucket19
|
||||||
list_id: 21
|
list_id: 21
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 20
|
- id: 20
|
||||||
title: testbucket20
|
title: testbucket20
|
||||||
list_id: 22
|
list_id: 22
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 21
|
- id: 21
|
||||||
title: testbucket21
|
title: testbucket21
|
||||||
list_id: 3
|
list_id: 3
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
# Duplicate buckets to make deletion of one of them possible
|
# Duplicate buckets to make deletion of one of them possible
|
||||||
- id: 22
|
- id: 22
|
||||||
title: testbucket22
|
title: testbucket22
|
||||||
list_id: 6
|
list_id: 6
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 23
|
- id: 23
|
||||||
title: testbucket23
|
title: testbucket23
|
||||||
list_id: 7
|
list_id: 7
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 24
|
- id: 24
|
||||||
title: testbucket24
|
title: testbucket24
|
||||||
list_id: 8
|
list_id: 8
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 25
|
- id: 25
|
||||||
title: testbucket25
|
title: testbucket25
|
||||||
list_id: 9
|
list_id: 9
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 26
|
- id: 26
|
||||||
title: testbucket26
|
title: testbucket26
|
||||||
list_id: 10
|
list_id: 10
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 27
|
- id: 27
|
||||||
title: testbucket27
|
title: testbucket27
|
||||||
list_id: 11
|
list_id: 11
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 28
|
- id: 28
|
||||||
title: testbucket28
|
title: testbucket28
|
||||||
list_id: 12
|
list_id: 12
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 29
|
- id: 29
|
||||||
title: testbucket29
|
title: testbucket29
|
||||||
list_id: 13
|
list_id: 13
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 30
|
- id: 30
|
||||||
title: testbucket30
|
title: testbucket30
|
||||||
list_id: 14
|
list_id: 14
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 31
|
- id: 31
|
||||||
title: testbucket31
|
title: testbucket31
|
||||||
list_id: 15
|
list_id: 15
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 32
|
- id: 32
|
||||||
title: testbucket32
|
title: testbucket32
|
||||||
list_id: 16
|
list_id: 16
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
- id: 33
|
- id: 33
|
||||||
title: testbucket33
|
title: testbucket33
|
||||||
list_id: 17
|
list_id: 17
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
# This bucket is the last one in its list
|
# This bucket is the last one in its list
|
||||||
- id: 34
|
- id: 34
|
||||||
title: testbucket34
|
title: testbucket34
|
||||||
list_id: 18
|
list_id: 18
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 1587244432
|
created: 2020-04-18 21:13:52
|
||||||
updated: 1587244432
|
updated: 2020-04-18 21:13:52
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
name: test
|
name: test
|
||||||
size: 100
|
size: 100
|
||||||
created_unix: 1570998791
|
created: 2019-10-13 20:33:11
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
task_id: 1
|
task_id: 1
|
||||||
label_id: 4
|
label_id: 4
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
task_id: 2
|
task_id: 2
|
||||||
label_id: 4
|
label_id: 4
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
task_id: 35
|
task_id: 35
|
||||||
label_id: 4
|
label_id: 4
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
task_id: 36
|
task_id: 36
|
||||||
label_id: 4
|
label_id: 4
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
title: 'Label #1'
|
title: 'Label #1'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
title: 'Label #2'
|
title: 'Label #2'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
title: 'Label #3 - other user'
|
title: 'Label #3 - other user'
|
||||||
created_by_id: 2
|
created_by_id: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
title: 'Label #4 - visible via other task'
|
title: 'Label #4 - visible via other task'
|
||||||
created_by_id: 2
|
created_by_id: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
|
@ -4,21 +4,21 @@
|
||||||
right: 0
|
right: 0
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
hash: test2
|
hash: test2
|
||||||
list_id: 2
|
list_id: 2
|
||||||
right: 1
|
right: 1
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
hash: test3
|
hash: test3
|
||||||
list_id: 3
|
list_id: 3
|
||||||
right: 2
|
right: 2
|
||||||
sharing_type: 1
|
sharing_type: 1
|
||||||
shared_by_id: 1
|
shared_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
identifier: test1
|
identifier: test1
|
||||||
owner_id: 1
|
owner_id: 1
|
||||||
namespace_id: 1
|
namespace_id: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 2
|
id: 2
|
||||||
title: Test2
|
title: Test2
|
||||||
|
@ -14,8 +14,8 @@
|
||||||
identifier: test2
|
identifier: test2
|
||||||
owner_id: 3
|
owner_id: 3
|
||||||
namespace_id: 1
|
namespace_id: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 3
|
id: 3
|
||||||
title: Test3
|
title: Test3
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
identifier: test3
|
identifier: test3
|
||||||
owner_id: 3
|
owner_id: 3
|
||||||
namespace_id: 2
|
namespace_id: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 4
|
id: 4
|
||||||
title: Test4
|
title: Test4
|
||||||
|
@ -32,8 +32,8 @@
|
||||||
identifier: test4
|
identifier: test4
|
||||||
owner_id: 3
|
owner_id: 3
|
||||||
namespace_id: 3
|
namespace_id: 3
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 5
|
id: 5
|
||||||
title: Test5
|
title: Test5
|
||||||
|
@ -41,8 +41,8 @@
|
||||||
identifier: test5
|
identifier: test5
|
||||||
owner_id: 5
|
owner_id: 5
|
||||||
namespace_id: 5
|
namespace_id: 5
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 6
|
id: 6
|
||||||
title: Test6
|
title: Test6
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
identifier: test6
|
identifier: test6
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 7
|
id: 7
|
||||||
title: Test7
|
title: Test7
|
||||||
|
@ -59,8 +59,8 @@
|
||||||
identifier: test7
|
identifier: test7
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 8
|
id: 8
|
||||||
title: Test8
|
title: Test8
|
||||||
|
@ -68,8 +68,8 @@
|
||||||
identifier: test8
|
identifier: test8
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 9
|
id: 9
|
||||||
title: Test9
|
title: Test9
|
||||||
|
@ -77,8 +77,8 @@
|
||||||
identifier: test9
|
identifier: test9
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 10
|
id: 10
|
||||||
title: Test10
|
title: Test10
|
||||||
|
@ -86,8 +86,8 @@
|
||||||
identifier: test10
|
identifier: test10
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 11
|
id: 11
|
||||||
title: Test11
|
title: Test11
|
||||||
|
@ -95,8 +95,8 @@
|
||||||
identifier: test11
|
identifier: test11
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 6
|
namespace_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 12
|
id: 12
|
||||||
title: Test12
|
title: Test12
|
||||||
|
@ -104,8 +104,8 @@
|
||||||
identifier: test12
|
identifier: test12
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 7
|
namespace_id: 7
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 13
|
id: 13
|
||||||
title: Test13
|
title: Test13
|
||||||
|
@ -113,8 +113,8 @@
|
||||||
identifier: test13
|
identifier: test13
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 8
|
namespace_id: 8
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 14
|
id: 14
|
||||||
title: Test14
|
title: Test14
|
||||||
|
@ -122,8 +122,8 @@
|
||||||
identifier: test14
|
identifier: test14
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 9
|
namespace_id: 9
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 15
|
id: 15
|
||||||
title: Test15
|
title: Test15
|
||||||
|
@ -131,8 +131,8 @@
|
||||||
identifier: test15
|
identifier: test15
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 10
|
namespace_id: 10
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 16
|
id: 16
|
||||||
title: Test16
|
title: Test16
|
||||||
|
@ -140,8 +140,8 @@
|
||||||
identifier: test16
|
identifier: test16
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 11
|
namespace_id: 11
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 17
|
id: 17
|
||||||
title: Test17
|
title: Test17
|
||||||
|
@ -149,8 +149,8 @@
|
||||||
identifier: test17
|
identifier: test17
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
namespace_id: 12
|
namespace_id: 12
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# This list is owned by user 7, and several other users have access to it via different methods.
|
# This list is owned by user 7, and several other users have access to it via different methods.
|
||||||
# It is used to test the listUsers method.
|
# It is used to test the listUsers method.
|
||||||
-
|
-
|
||||||
|
@ -160,8 +160,8 @@
|
||||||
identifier: test18
|
identifier: test18
|
||||||
owner_id: 7
|
owner_id: 7
|
||||||
namespace_id: 13
|
namespace_id: 13
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 19
|
id: 19
|
||||||
title: Test19
|
title: Test19
|
||||||
|
@ -169,8 +169,8 @@
|
||||||
identifier: test19
|
identifier: test19
|
||||||
owner_id: 7
|
owner_id: 7
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 20
|
id: 20
|
||||||
title: Test20
|
title: Test20
|
||||||
|
@ -178,8 +178,8 @@
|
||||||
identifier: test20
|
identifier: test20
|
||||||
owner_id: 13
|
owner_id: 13
|
||||||
namespace_id: 15
|
namespace_id: 15
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 21
|
id: 21
|
||||||
title: Test21 archived through namespace
|
title: Test21 archived through namespace
|
||||||
|
@ -187,8 +187,8 @@
|
||||||
identifier: test21
|
identifier: test21
|
||||||
owner_id: 1
|
owner_id: 1
|
||||||
namespace_id: 16
|
namespace_id: 16
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 22
|
id: 22
|
||||||
title: Test22 archived individually
|
title: Test22 archived individually
|
||||||
|
@ -197,5 +197,5 @@
|
||||||
owner_id: 1
|
owner_id: 1
|
||||||
namespace_id: 1
|
namespace_id: 1
|
||||||
is_archived: 1
|
is_archived: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,83 +2,83 @@
|
||||||
title: testnamespace
|
title: testnamespace
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 1
|
owner_id: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
title: testnamespace2
|
title: testnamespace2
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 2
|
owner_id: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
title: testnamespace3
|
title: testnamespace3
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 3
|
owner_id: 3
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
title: testnamespace6
|
title: testnamespace6
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
title: testnamespace7
|
title: testnamespace7
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
title: testnamespace8
|
title: testnamespace8
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 9
|
- id: 9
|
||||||
title: testnamespace9
|
title: testnamespace9
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 10
|
- id: 10
|
||||||
title: testnamespace10
|
title: testnamespace10
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 11
|
- id: 11
|
||||||
title: testnamespace11
|
title: testnamespace11
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 12
|
- id: 12
|
||||||
title: testnamespace12
|
title: testnamespace12
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 6
|
owner_id: 6
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 13
|
- id: 13
|
||||||
title: testnamespace13
|
title: testnamespace13
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 7
|
owner_id: 7
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 14
|
- id: 14
|
||||||
title: testnamespace14
|
title: testnamespace14
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 7
|
owner_id: 7
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 15
|
- id: 15
|
||||||
title: testnamespace15
|
title: testnamespace15
|
||||||
description: Lorem Ipsum
|
description: Lorem Ipsum
|
||||||
owner_id: 13
|
owner_id: 13
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 16
|
- id: 16
|
||||||
title: Archived testnamespace16
|
title: Archived testnamespace16
|
||||||
owner_id: 1
|
owner_id: 1
|
||||||
is_archived: 1
|
is_archived: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
task_id: 30
|
task_id: 30
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
task_id: 30
|
task_id: 30
|
||||||
user_id: 2
|
user_id: 2
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
task_id: 35
|
task_id: 35
|
||||||
user_id: 2
|
user_id: 2
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
task_id: 36
|
task_id: 36
|
||||||
user_id: 2
|
user_id: 2
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
task_id: 1
|
task_id: 1
|
||||||
file_id: 1
|
file_id: 1
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# The file for this attachment does not exist
|
# The file for this attachment does not exist
|
||||||
- id: 2
|
- id: 2
|
||||||
task_id: 1
|
task_id: 1
|
||||||
file_id: 9999
|
file_id: 9999
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,95 +2,95 @@
|
||||||
comment: Lorem Ipsum Dolor Sit Amet
|
comment: Lorem Ipsum Dolor Sit Amet
|
||||||
author_id: 1
|
author_id: 1
|
||||||
task_id: 1
|
task_id: 1
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 2
|
- id: 2
|
||||||
comment: comment 2
|
comment: comment 2
|
||||||
author_id: 5
|
author_id: 5
|
||||||
task_id: 14
|
task_id: 14
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 3
|
- id: 3
|
||||||
comment: comment 3
|
comment: comment 3
|
||||||
author_id: 5
|
author_id: 5
|
||||||
task_id: 15
|
task_id: 15
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 4
|
- id: 4
|
||||||
comment: comment 4
|
comment: comment 4
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 16
|
task_id: 16
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 5
|
- id: 5
|
||||||
comment: comment 5
|
comment: comment 5
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 17
|
task_id: 17
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 6
|
- id: 6
|
||||||
comment: comment 6
|
comment: comment 6
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 18
|
task_id: 18
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 7
|
- id: 7
|
||||||
comment: comment 7
|
comment: comment 7
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 19
|
task_id: 19
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 8
|
- id: 8
|
||||||
comment: comment 8
|
comment: comment 8
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 20
|
task_id: 20
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 9
|
- id: 9
|
||||||
comment: comment 9
|
comment: comment 9
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 21
|
task_id: 21
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 10
|
- id: 10
|
||||||
comment: comment 10
|
comment: comment 10
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 22
|
task_id: 22
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 11
|
- id: 11
|
||||||
comment: comment 11
|
comment: comment 11
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 23
|
task_id: 23
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 12
|
- id: 12
|
||||||
comment: comment 12
|
comment: comment 12
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 24
|
task_id: 24
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 13
|
- id: 13
|
||||||
comment: comment 13
|
comment: comment 13
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 25
|
task_id: 25
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 14
|
- id: 14
|
||||||
comment: comment 14
|
comment: comment 14
|
||||||
author_id: 6
|
author_id: 6
|
||||||
task_id: 26
|
task_id: 26
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 15
|
- id: 15
|
||||||
comment: comment 15
|
comment: comment 15
|
||||||
author_id: 1
|
author_id: 1
|
||||||
task_id: 35
|
task_id: 35
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
- id: 16
|
- id: 16
|
||||||
comment: comment 16
|
comment: comment 16
|
||||||
author_id: 1
|
author_id: 1
|
||||||
task_id: 36
|
task_id: 36
|
||||||
created: 1582135626
|
created: 2020-02-19 18:07:06
|
||||||
updated: 1582135626
|
updated: 2020-02-19 18:07:06
|
||||||
|
|
|
@ -3,34 +3,34 @@
|
||||||
other_task_id: 29
|
other_task_id: 29
|
||||||
relation_kind: 'subtask'
|
relation_kind: 'subtask'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
task_id: 29
|
task_id: 29
|
||||||
other_task_id: 1
|
other_task_id: 1
|
||||||
relation_kind: 'parenttask'
|
relation_kind: 'parenttask'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
task_id: 35
|
task_id: 35
|
||||||
other_task_id: 1
|
other_task_id: 1
|
||||||
relation_kind: 'related'
|
relation_kind: 'related'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
task_id: 35
|
task_id: 35
|
||||||
other_task_id: 1
|
other_task_id: 1
|
||||||
relation_kind: 'related'
|
relation_kind: 'related'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 5
|
- id: 5
|
||||||
task_id: 36
|
task_id: 36
|
||||||
other_task_id: 1
|
other_task_id: 1
|
||||||
relation_kind: 'related'
|
relation_kind: 'related'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
task_id: 36
|
task_id: 36
|
||||||
other_task_id: 1
|
other_task_id: 1
|
||||||
relation_kind: 'related'
|
relation_kind: 'related'
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
task_id: 27
|
task_id: 27
|
||||||
reminder_unix: 1543626724
|
reminder: 2018-12-01 01:12:04
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
- id: 2
|
- id: 2
|
||||||
task_id: 27
|
task_id: 27
|
||||||
reminder_unix: 1543626824
|
reminder: 2018-12-01 01:13:44
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 1
|
index: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
- id: 2
|
- id: 2
|
||||||
title: 'task #2 done'
|
title: 'task #2 done'
|
||||||
|
@ -14,8 +14,8 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 2
|
index: 2
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
- id: 3
|
- id: 3
|
||||||
title: 'task #3 high prio'
|
title: 'task #3 high prio'
|
||||||
|
@ -23,8 +23,8 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 3
|
index: 3
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
priority: 100
|
priority: 100
|
||||||
bucket_id: 2
|
bucket_id: 2
|
||||||
- id: 4
|
- id: 4
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 4
|
index: 4
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
priority: 1
|
priority: 1
|
||||||
bucket_id: 2
|
bucket_id: 2
|
||||||
- id: 5
|
- id: 5
|
||||||
|
@ -43,9 +43,9 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 5
|
index: 5
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
due_date_unix: 1543636724
|
due_date: 2018-12-01 03:58:44
|
||||||
bucket_id: 2
|
bucket_id: 2
|
||||||
- id: 6
|
- id: 6
|
||||||
title: 'task #6 lower due date'
|
title: 'task #6 lower due date'
|
||||||
|
@ -53,9 +53,9 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 6
|
index: 6
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
due_date_unix: 1543616724
|
due_date: 2018-11-30 22:25:24
|
||||||
bucket_id: 3
|
bucket_id: 3
|
||||||
- id: 7
|
- id: 7
|
||||||
title: 'task #7 with start date'
|
title: 'task #7 with start date'
|
||||||
|
@ -63,9 +63,9 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 7
|
index: 7
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
start_date_unix: 1544600000
|
start_date: 2018-12-12 07:33:20
|
||||||
bucket_id: 3
|
bucket_id: 3
|
||||||
- id: 8
|
- id: 8
|
||||||
title: 'task #8 with end date'
|
title: 'task #8 with end date'
|
||||||
|
@ -73,9 +73,9 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 8
|
index: 8
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
end_date_unix: 1544700000
|
end_date: 2018-12-13 11:20:00
|
||||||
bucket_id: 3
|
bucket_id: 3
|
||||||
- id: 9
|
- id: 9
|
||||||
title: 'task #9 with start and end date'
|
title: 'task #9 with start and end date'
|
||||||
|
@ -83,10 +83,10 @@
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 9
|
index: 9
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
start_date_unix: 1544600000
|
start_date: 2018-12-12 07:33:20
|
||||||
end_date_unix: 1544700000
|
end_date: 2018-12-13 11:20:00
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
- id: 10
|
- id: 10
|
||||||
title: 'task #10 basic'
|
title: 'task #10 basic'
|
||||||
|
@ -95,8 +95,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 10
|
index: 10
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 11
|
- id: 11
|
||||||
title: 'task #11 basic'
|
title: 'task #11 basic'
|
||||||
done: false
|
done: false
|
||||||
|
@ -104,8 +104,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 11
|
index: 11
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 12
|
- id: 12
|
||||||
title: 'task #12 basic'
|
title: 'task #12 basic'
|
||||||
done: false
|
done: false
|
||||||
|
@ -113,8 +113,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 12
|
index: 12
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 13
|
- id: 13
|
||||||
title: 'task #13 basic other list'
|
title: 'task #13 basic other list'
|
||||||
done: false
|
done: false
|
||||||
|
@ -122,8 +122,8 @@
|
||||||
list_id: 2
|
list_id: 2
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 4
|
bucket_id: 4
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 14
|
- id: 14
|
||||||
title: 'task #14 basic other list'
|
title: 'task #14 basic other list'
|
||||||
done: false
|
done: false
|
||||||
|
@ -131,8 +131,8 @@
|
||||||
list_id: 5
|
list_id: 5
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 18
|
bucket_id: 18
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 15
|
- id: 15
|
||||||
title: 'task #15'
|
title: 'task #15'
|
||||||
done: false
|
done: false
|
||||||
|
@ -140,8 +140,8 @@
|
||||||
list_id: 6
|
list_id: 6
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 6
|
bucket_id: 6
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 16
|
- id: 16
|
||||||
title: 'task #16'
|
title: 'task #16'
|
||||||
done: false
|
done: false
|
||||||
|
@ -149,8 +149,8 @@
|
||||||
list_id: 7
|
list_id: 7
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 7
|
bucket_id: 7
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 17
|
- id: 17
|
||||||
title: 'task #17'
|
title: 'task #17'
|
||||||
done: false
|
done: false
|
||||||
|
@ -158,8 +158,8 @@
|
||||||
list_id: 8
|
list_id: 8
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 8
|
bucket_id: 8
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 18
|
- id: 18
|
||||||
title: 'task #18'
|
title: 'task #18'
|
||||||
done: false
|
done: false
|
||||||
|
@ -167,8 +167,8 @@
|
||||||
list_id: 9
|
list_id: 9
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 9
|
bucket_id: 9
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 19
|
- id: 19
|
||||||
title: 'task #19'
|
title: 'task #19'
|
||||||
done: false
|
done: false
|
||||||
|
@ -176,8 +176,8 @@
|
||||||
list_id: 10
|
list_id: 10
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 10
|
bucket_id: 10
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 20
|
- id: 20
|
||||||
title: 'task #20'
|
title: 'task #20'
|
||||||
done: false
|
done: false
|
||||||
|
@ -185,8 +185,8 @@
|
||||||
list_id: 11
|
list_id: 11
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 11
|
bucket_id: 11
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 21
|
- id: 21
|
||||||
title: 'task #21'
|
title: 'task #21'
|
||||||
done: false
|
done: false
|
||||||
|
@ -194,8 +194,8 @@
|
||||||
list_id: 12
|
list_id: 12
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 12
|
bucket_id: 12
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 22
|
- id: 22
|
||||||
title: 'task #22'
|
title: 'task #22'
|
||||||
done: false
|
done: false
|
||||||
|
@ -203,8 +203,8 @@
|
||||||
list_id: 13
|
list_id: 13
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 13
|
bucket_id: 13
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 23
|
- id: 23
|
||||||
title: 'task #23'
|
title: 'task #23'
|
||||||
done: false
|
done: false
|
||||||
|
@ -212,8 +212,8 @@
|
||||||
list_id: 14
|
list_id: 14
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 14
|
bucket_id: 14
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 24
|
- id: 24
|
||||||
title: 'task #24'
|
title: 'task #24'
|
||||||
done: false
|
done: false
|
||||||
|
@ -221,8 +221,8 @@
|
||||||
list_id: 15
|
list_id: 15
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 15
|
bucket_id: 15
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 25
|
- id: 25
|
||||||
title: 'task #25'
|
title: 'task #25'
|
||||||
done: false
|
done: false
|
||||||
|
@ -230,8 +230,8 @@
|
||||||
list_id: 16
|
list_id: 16
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 16
|
bucket_id: 16
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 26
|
- id: 26
|
||||||
title: 'task #26'
|
title: 'task #26'
|
||||||
done: false
|
done: false
|
||||||
|
@ -239,8 +239,8 @@
|
||||||
list_id: 17
|
list_id: 17
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 17
|
bucket_id: 17
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 27
|
- id: 27
|
||||||
title: 'task #27 with reminders'
|
title: 'task #27 with reminders'
|
||||||
done: false
|
done: false
|
||||||
|
@ -248,8 +248,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 12
|
index: 12
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 28
|
- id: 28
|
||||||
title: 'task #28 with repeat after'
|
title: 'task #28 with repeat after'
|
||||||
done: false
|
done: false
|
||||||
|
@ -258,8 +258,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 13
|
index: 13
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 29
|
- id: 29
|
||||||
title: 'task #29 with parent task (1)'
|
title: 'task #29 with parent task (1)'
|
||||||
done: false
|
done: false
|
||||||
|
@ -267,8 +267,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 14
|
index: 14
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 30
|
- id: 30
|
||||||
title: 'task #30 with assignees'
|
title: 'task #30 with assignees'
|
||||||
done: false
|
done: false
|
||||||
|
@ -276,8 +276,8 @@
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 15
|
index: 15
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 31
|
- id: 31
|
||||||
title: 'task #31 with color'
|
title: 'task #31 with color'
|
||||||
done: false
|
done: false
|
||||||
|
@ -286,8 +286,8 @@
|
||||||
index: 16
|
index: 16
|
||||||
hex_color: f0f0f0
|
hex_color: f0f0f0
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 32
|
- id: 32
|
||||||
title: 'task #32'
|
title: 'task #32'
|
||||||
done: false
|
done: false
|
||||||
|
@ -295,8 +295,8 @@
|
||||||
list_id: 3
|
list_id: 3
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 21
|
bucket_id: 21
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 33
|
- id: 33
|
||||||
title: 'task #33 with percent done'
|
title: 'task #33 with percent done'
|
||||||
done: false
|
done: false
|
||||||
|
@ -305,8 +305,8 @@
|
||||||
index: 17
|
index: 17
|
||||||
percent_done: 0.5
|
percent_done: 0.5
|
||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
# This task is forbidden for user1
|
# This task is forbidden for user1
|
||||||
- id: 34
|
- id: 34
|
||||||
title: 'task #34'
|
title: 'task #34'
|
||||||
|
@ -315,8 +315,8 @@
|
||||||
list_id: 20
|
list_id: 20
|
||||||
index: 20
|
index: 20
|
||||||
bucket_id: 5
|
bucket_id: 5
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 35
|
- id: 35
|
||||||
title: 'task #35'
|
title: 'task #35'
|
||||||
done: false
|
done: false
|
||||||
|
@ -324,8 +324,8 @@
|
||||||
list_id: 21
|
list_id: 21
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 19
|
bucket_id: 19
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 36
|
- id: 36
|
||||||
title: 'task #36'
|
title: 'task #36'
|
||||||
done: false
|
done: false
|
||||||
|
@ -333,7 +333,7 @@
|
||||||
list_id: 22
|
list_id: 22
|
||||||
index: 1
|
index: 1
|
||||||
bucket_id: 20
|
bucket_id: 20
|
||||||
created: 1543626724
|
created: 2018-12-01 01:12:04
|
||||||
updated: 1543626724
|
updated: 2018-12-01 01:12:04
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,47 +2,47 @@
|
||||||
team_id: 1
|
team_id: 1
|
||||||
list_id: 3
|
list_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# This team has read only access on list 6
|
# This team has read only access on list 6
|
||||||
- id: 2
|
- id: 2
|
||||||
team_id: 2
|
team_id: 2
|
||||||
list_id: 6
|
list_id: 6
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# This team has write access on list 7
|
# This team has write access on list 7
|
||||||
- id: 3
|
- id: 3
|
||||||
team_id: 3
|
team_id: 3
|
||||||
list_id: 7
|
list_id: 7
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# This team has admin access on list 8
|
# This team has admin access on list 8
|
||||||
- id: 4
|
- id: 4
|
||||||
team_id: 4
|
team_id: 4
|
||||||
list_id: 8
|
list_id: 8
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# Readonly acces on list 19
|
# Readonly acces on list 19
|
||||||
- id: 5
|
- id: 5
|
||||||
team_id: 8
|
team_id: 8
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# Write acces on list 19
|
# Write acces on list 19
|
||||||
- id: 6
|
- id: 6
|
||||||
team_id: 9
|
team_id: 9
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# Admin acces on list 19
|
# Admin acces on list 19
|
||||||
- id: 7
|
- id: 7
|
||||||
team_id: 10
|
team_id: 10
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
|
@ -2,56 +2,56 @@
|
||||||
team_id: 1
|
team_id: 1
|
||||||
user_id: 1
|
user_id: 1
|
||||||
admin: true
|
admin: true
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 1
|
team_id: 1
|
||||||
user_id: 2
|
user_id: 2
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 2
|
team_id: 2
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 3
|
team_id: 3
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 4
|
team_id: 4
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 5
|
team_id: 5
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 6
|
team_id: 6
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 7
|
team_id: 7
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 8
|
team_id: 8
|
||||||
user_id: 1
|
user_id: 1
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 9
|
team_id: 9
|
||||||
user_id: 2
|
user_id: 2
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 10
|
team_id: 10
|
||||||
user_id: 3
|
user_id: 3
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 11
|
team_id: 11
|
||||||
user_id: 8
|
user_id: 8
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 12
|
team_id: 12
|
||||||
user_id: 9
|
user_id: 9
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
team_id: 13
|
team_id: 13
|
||||||
user_id: 10
|
user_id: 10
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,51 +2,51 @@
|
||||||
team_id: 1
|
team_id: 1
|
||||||
namespace_id: 3
|
namespace_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 2
|
- id: 2
|
||||||
team_id: 2
|
team_id: 2
|
||||||
namespace_id: 3
|
namespace_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 3
|
- id: 3
|
||||||
team_id: 5
|
team_id: 5
|
||||||
namespace_id: 7
|
namespace_id: 7
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 4
|
- id: 4
|
||||||
team_id: 6
|
team_id: 6
|
||||||
namespace_id: 8
|
namespace_id: 8
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 5
|
- id: 5
|
||||||
team_id: 7
|
team_id: 7
|
||||||
namespace_id: 9
|
namespace_id: 9
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
team_id: 11
|
team_id: 11
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
team_id: 12
|
team_id: 12
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
team_id: 13
|
team_id: 13
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -4,31 +4,31 @@
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user1@example.com'
|
email: 'user1@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 2
|
id: 2
|
||||||
username: 'user2'
|
username: 'user2'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user2@example.com'
|
email: 'user2@example.com'
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 3
|
id: 3
|
||||||
username: 'user3'
|
username: 'user3'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user3@example.com'
|
email: 'user3@example.com'
|
||||||
password_reset_token: passwordresettesttoken
|
password_reset_token: passwordresettesttoken
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 4
|
id: 4
|
||||||
username: 'user4'
|
username: 'user4'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user4@example.com'
|
email: 'user4@example.com'
|
||||||
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
|
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
-
|
-
|
||||||
id: 5
|
id: 5
|
||||||
username: 'user5'
|
username: 'user5'
|
||||||
|
@ -36,62 +36,62 @@
|
||||||
email: 'user5@example.com'
|
email: 'user5@example.com'
|
||||||
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
|
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
|
||||||
is_active: false
|
is_active: false
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
# This use is used to create a whole bunch of lists which are then shared directly with a user
|
# This use is used to create a whole bunch of lists which are then shared directly with a user
|
||||||
- id: 6
|
- id: 6
|
||||||
username: 'user6'
|
username: 'user6'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user6@example.com'
|
email: 'user6@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
username: 'user7'
|
username: 'user7'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user7@example.com'
|
email: 'user7@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
username: 'user8'
|
username: 'user8'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user8@example.com'
|
email: 'user8@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 9
|
- id: 9
|
||||||
username: 'user9'
|
username: 'user9'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user9@example.com'
|
email: 'user9@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 10
|
- id: 10
|
||||||
username: 'user10'
|
username: 'user10'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user10@example.com'
|
email: 'user10@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 11
|
- id: 11
|
||||||
username: 'user11'
|
username: 'user11'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user11@example.com'
|
email: 'user11@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 12
|
- id: 12
|
||||||
username: 'user12'
|
username: 'user12'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user12@example.com'
|
email: 'user12@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 13
|
- id: 13
|
||||||
username: 'user13'
|
username: 'user13'
|
||||||
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
|
||||||
email: 'user14@example.com'
|
email: 'user14@example.com'
|
||||||
is_active: true
|
is_active: true
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,47 +2,47 @@
|
||||||
user_id: 1
|
user_id: 1
|
||||||
list_id: 3
|
list_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 2
|
- id: 2
|
||||||
user_id: 2
|
user_id: 2
|
||||||
list_id: 3
|
list_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 3
|
- id: 3
|
||||||
user_id: 1
|
user_id: 1
|
||||||
list_id: 9
|
list_id: 9
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 4
|
- id: 4
|
||||||
user_id: 1
|
user_id: 1
|
||||||
list_id: 10
|
list_id: 10
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 5
|
- id: 5
|
||||||
user_id: 1
|
user_id: 1
|
||||||
list_id: 11
|
list_id: 11
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
user_id: 4
|
user_id: 4
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
user_id: 5
|
user_id: 5
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
user_id: 6
|
user_id: 6
|
||||||
list_id: 19
|
list_id: 19
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -2,51 +2,51 @@
|
||||||
user_id: 1
|
user_id: 1
|
||||||
namespace_id: 3
|
namespace_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 2
|
- id: 2
|
||||||
user_id: 2
|
user_id: 2
|
||||||
namespace_id: 3
|
namespace_id: 3
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 3
|
- id: 3
|
||||||
user_id: 1
|
user_id: 1
|
||||||
namespace_id: 10
|
namespace_id: 10
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 4
|
- id: 4
|
||||||
user_id: 1
|
user_id: 1
|
||||||
namespace_id: 11
|
namespace_id: 11
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
||||||
- id: 5
|
- id: 5
|
||||||
user_id: 1
|
user_id: 1
|
||||||
namespace_id: 12
|
namespace_id: 12
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 6
|
- id: 6
|
||||||
user_id: 11
|
user_id: 11
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 0
|
right: 0
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 7
|
- id: 7
|
||||||
user_id: 12
|
user_id: 12
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 1
|
right: 1
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
- id: 8
|
- id: 8
|
||||||
user_id: 13
|
user_id: 13
|
||||||
namespace_id: 14
|
namespace_id: 14
|
||||||
right: 2
|
right: 2
|
||||||
updated: 0
|
updated: 2018-12-02 15:13:12
|
||||||
created: 0
|
created: 2018-12-01 15:13:12
|
||||||
|
|
|
@ -49,6 +49,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) {
|
||||||
logger := log.NewXormLogger("DEBUG")
|
logger := log.NewXormLogger("DEBUG")
|
||||||
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
||||||
engine.SetLogger(logger)
|
engine.SetLogger(logger)
|
||||||
|
engine.SetTZLocation(config.GetTimeZone())
|
||||||
x = engine
|
x = engine
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ func InitFixtures(tablenames ...string) (err error) {
|
||||||
testfixtures.Database(x.DB().DB),
|
testfixtures.Database(x.DB().DB),
|
||||||
testfixtures.Dialect(config.DatabaseType.GetString()),
|
testfixtures.Dialect(config.DatabaseType.GetString()),
|
||||||
testfixtures.DangerousSkipTestDatabaseCheck(),
|
testfixtures.DangerousSkipTestDatabaseCheck(),
|
||||||
|
testfixtures.Location(config.GetTimeZone()),
|
||||||
testfiles,
|
testfiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"github.com/c2h5oh/datasize"
|
"github.com/c2h5oh/datasize"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
@ -34,10 +33,8 @@ type File struct {
|
||||||
Mime string `xorm:"text null" json:"mime"`
|
Mime string `xorm:"text null" json:"mime"`
|
||||||
Size uint64 `xorm:"int(11) not null" json:"size"`
|
Size uint64 `xorm:"int(11) not null" json:"size"`
|
||||||
|
|
||||||
Created time.Time `xorm:"-" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
|
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"created" json:"-"`
|
|
||||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
|
||||||
|
|
||||||
File afero.File `xorm:"-" json:"-"`
|
File afero.File `xorm:"-" json:"-"`
|
||||||
// This ReadCloser is only used for migration purposes. Use with care!
|
// This ReadCloser is only used for migration purposes. Use with care!
|
||||||
|
@ -66,7 +63,6 @@ func (f *File) LoadFileMetaByID() (err error) {
|
||||||
if !exists {
|
if !exists {
|
||||||
return ErrFileDoesNotExist{FileID: f.ID}
|
return ErrFileDoesNotExist{FileID: f.ID}
|
||||||
}
|
}
|
||||||
f.Created = f.CreatedUnix.ToTime()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,49 +113,49 @@ func TestTaskCollection(t *testing.T) {
|
||||||
t.Run("by priority", func(t *testing.T) {
|
t.Run("by priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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":1,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-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":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
t.Run("by priority desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority asc", func(t *testing.T) {
|
t.Run("by priority asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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":1,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-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":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// should equal duedate asc
|
||||||
t.Run("by due_date", func(t *testing.T) {
|
t.Run("by due_date", 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,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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}}]`)
|
|
||||||
})
|
|
||||||
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,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date`)
|
|
||||||
})
|
|
||||||
// Due date without unix suffix
|
|
||||||
t.Run("by duedate asc without _unix suffix", func(t *testing.T) {
|
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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}}]`)
|
|
||||||
})
|
|
||||||
t.Run("by due_date without _unix suffix", func(t *testing.T) {
|
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc without _unix suffix", func(t *testing.T) {
|
t.Run("by duedate desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
|
})
|
||||||
|
// Due date without unix suffix
|
||||||
|
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
|
})
|
||||||
|
t.Run("by due_date without suffix", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, urlParams)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
|
})
|
||||||
|
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, urlParams)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
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)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
t.Run("invalid sort parameter", func(t *testing.T) {
|
||||||
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
_, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"loremipsum"}}, urlParams)
|
||||||
|
@ -177,71 +177,85 @@ func TestTaskCollection(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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}}]`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Date range", func(t *testing.T) {
|
t.Run("Filter", func(t *testing.T) {
|
||||||
t.Run("start and end date", func(t *testing.T) {
|
t.Run("Date range", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
t.Run("start and end date", func(t *testing.T) {
|
||||||
url.Values{
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
"filter_by": []string{"start_date", "end_date", "due_date"},
|
url.Values{
|
||||||
"filter_value": []string{"1544500000", "1544700001", "1543500000"},
|
"filter_by": []string{"start_date", "end_date", "due_date"},
|
||||||
"filter_comparator": []string{"greater", "less", "greater"},
|
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
|
||||||
},
|
"filter_comparator": []string{"greater", "less", "greater"},
|
||||||
urlParams,
|
},
|
||||||
)
|
urlParams,
|
||||||
assert.NoError(t, err)
|
)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #3`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #5`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #6`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #7`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #8`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.Contains(t, rec.Body.String(), `task #9`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #13`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("start date only", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
|
url.Values{
|
||||||
|
"filter_by": []string{"start_date"},
|
||||||
|
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
|
||||||
|
"filter_comparator": []string{"greater"},
|
||||||
|
},
|
||||||
|
urlParams,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #3`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #5`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #6`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #7`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #8`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #9`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("end date only", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
|
url.Values{
|
||||||
|
"filter_by": []string{"end_date"},
|
||||||
|
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
|
||||||
|
"filter_comparator": []string{"greater"},
|
||||||
|
},
|
||||||
|
urlParams,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// If no start date but an end date is specified, this should be null
|
||||||
|
// since we don't have any tasks in the fixtures with an end date >
|
||||||
|
// the current date.
|
||||||
|
assert.Equal(t, "[]\n", rec.Body.String())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
t.Run("start date only", func(t *testing.T) {
|
t.Run("invalid date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
_, err := testHandler.testReadAllWithUser(
|
||||||
url.Values{
|
url.Values{
|
||||||
"filter_by": []string{"start_date"},
|
"filter_by": []string{"due_date"},
|
||||||
"filter_value": []string{"1540000000"},
|
"filter_value": []string{"1540000000"},
|
||||||
"filter_comparator": []string{"greater"},
|
"filter_comparator": []string{"greater"},
|
||||||
},
|
},
|
||||||
urlParams,
|
nil,
|
||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.Error(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #6`)
|
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #13`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
|
||||||
})
|
|
||||||
t.Run("end date only", func(t *testing.T) {
|
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
|
||||||
url.Values{
|
|
||||||
"filter_by": []string{"end_date"},
|
|
||||||
"filter_value": []string{"1544700001"},
|
|
||||||
"filter_comparator": []string{"greater"},
|
|
||||||
},
|
|
||||||
urlParams,
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// If no start date but an end date is specified, this should be null
|
|
||||||
// since we don't have any tasks in the fixtures with an end date >
|
|
||||||
// the current date.
|
|
||||||
assert.Equal(t, "[]\n", rec.Body.String())
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -304,33 +318,33 @@ func TestTaskCollection(t *testing.T) {
|
||||||
t.Run("by priority", func(t *testing.T) {
|
t.Run("by priority", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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":1,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-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":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
t.Run("by priority desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1`)
|
assert.Contains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1`)
|
||||||
})
|
})
|
||||||
t.Run("by priority asc", func(t *testing.T) {
|
t.Run("by priority asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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":1,"position":0,"created_by":{"id":1,"username":"user1","created":null,"updated":null}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #3 high prio","description":"","done":false,"done_at":null,"due_date":null,"reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":1,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-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":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":3,"title":"task #3 high prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// should equal duedate asc
|
||||||
t.Run("by due_date", func(t *testing.T) {
|
t.Run("by due_date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date_unix"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
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)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date`)
|
assert.Contains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
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)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `{"id":6,"title":"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,"repeat_from_current_date":false,"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,"title":"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,"repeat_from_current_date":false,"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,"title":"task #6 lower due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"list_id":1,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"created_by":{"id":1,"username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("invalid parameter", func(t *testing.T) {
|
t.Run("invalid parameter", func(t *testing.T) {
|
||||||
// Invalid parameter should not sort at all
|
// Invalid parameter should not sort at all
|
||||||
|
@ -342,71 +356,85 @@ func TestTaskCollection(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"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}}]`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Date range", func(t *testing.T) {
|
t.Run("Filter", func(t *testing.T) {
|
||||||
t.Run("start and end date", func(t *testing.T) {
|
t.Run("Date range", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
t.Run("start and end date", func(t *testing.T) {
|
||||||
url.Values{
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
"filter_by": []string{"start_date", "end_date", "due_date"},
|
url.Values{
|
||||||
"filter_value": []string{"1544500000", "1544700001", "1543500000"},
|
"filter_by": []string{"start_date", "end_date", "due_date"},
|
||||||
"filter_comparator": []string{"greater", "less", "greater"},
|
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
|
||||||
},
|
"filter_comparator": []string{"greater", "less", "greater"},
|
||||||
nil,
|
},
|
||||||
)
|
nil,
|
||||||
assert.NoError(t, err)
|
)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #3`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #5`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #6`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #7`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #8`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.Contains(t, rec.Body.String(), `task #9`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #13`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("start date only", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
|
url.Values{
|
||||||
|
"filter_by": []string{"start_date"},
|
||||||
|
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
|
||||||
|
"filter_comparator": []string{"greater"},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #2`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #3`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #4`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #5`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #6`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #7`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #8`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `task #9`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #13`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `task #14`)
|
||||||
|
})
|
||||||
|
t.Run("end date only", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testReadAllWithUser(
|
||||||
|
url.Values{
|
||||||
|
"filter_by": []string{"end_date"},
|
||||||
|
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
|
||||||
|
"filter_comparator": []string{"greater"},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// If no start date but an end date is specified, this should be null
|
||||||
|
// since we don't have any tasks in the fixtures with an end date >
|
||||||
|
// the current date.
|
||||||
|
assert.Equal(t, "[]\n", rec.Body.String())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
t.Run("start date only", func(t *testing.T) {
|
t.Run("invalid date", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
_, err := testHandler.testReadAllWithUser(
|
||||||
url.Values{
|
url.Values{
|
||||||
"filter_by": []string{"start_date"},
|
"filter_by": []string{"due_date"},
|
||||||
"filter_value": []string{"1540000000"},
|
"filter_value": []string{"1540000000"},
|
||||||
"filter_comparator": []string{"greater"},
|
"filter_comparator": []string{"greater"},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.Error(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #6`)
|
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #13`)
|
|
||||||
assert.NotContains(t, rec.Body.String(), `task #14`)
|
|
||||||
})
|
|
||||||
t.Run("end date only", func(t *testing.T) {
|
|
||||||
rec, err := testHandler.testReadAllWithUser(
|
|
||||||
url.Values{
|
|
||||||
"filter_by": []string{"end_date"},
|
|
||||||
"filter_value": []string{"1544700001"},
|
|
||||||
"filter_comparator": []string{"greater"},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// If no start date but an end date is specified, this should be null
|
|
||||||
// since we don't have any tasks in the fixtures with an end date >
|
|
||||||
// the current date.
|
|
||||||
assert.Equal(t, "[]\n", rec.Body.String())
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -75,7 +75,7 @@ func TestTask(t *testing.T) {
|
||||||
t.Run("Due date unset", func(t *testing.T) {
|
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"}, `{"due_date": null}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"due_date":null`)
|
assert.Contains(t, rec.Body.String(), `"due_date":"0001-01-01T00:00:00Z"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"due_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders", func(t *testing.T) {
|
t.Run("Reminders", func(t *testing.T) {
|
||||||
|
@ -151,9 +151,9 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `"start_date":0`)
|
assert.NotContains(t, rec.Body.String(), `"start_date":0`)
|
||||||
})
|
})
|
||||||
t.Run("Start date unset", func(t *testing.T) {
|
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"}, `{"start_date":"0001-01-01T00:00:00Z"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"start_date":null`)
|
assert.Contains(t, rec.Body.String(), `"start_date":"0001-01-01T00:00:00Z"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"start_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("End date", func(t *testing.T) {
|
t.Run("End date", func(t *testing.T) {
|
||||||
|
@ -163,9 +163,9 @@ func TestTask(t *testing.T) {
|
||||||
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
assert.NotContains(t, rec.Body.String(), `"end_date":""`)
|
||||||
})
|
})
|
||||||
t.Run("End date unset", func(t *testing.T) {
|
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"}, `{"end_date":"0001-01-01T00:00:00Z"}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"end_date":null`)
|
assert.Contains(t, rec.Body.String(), `"end_date":"0001-01-01T00:00:00Z"`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
assert.NotContains(t, rec.Body.String(), `"end_date":"2020-02-10T10:00:00Z"`)
|
||||||
})
|
})
|
||||||
t.Run("Color", func(t *testing.T) {
|
t.Run("Color", func(t *testing.T) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"src.techknowlogick.com/xormigrate"
|
"src.techknowlogick.com/xormigrate"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
@ -28,8 +27,8 @@ type taskComments20200219183248 struct {
|
||||||
AuthorID int64 `xorm:"not null" json:"-"`
|
AuthorID int64 `xorm:"not null" json:"-"`
|
||||||
TaskID int64 `xorm:"not null" json:"-" param:"task"`
|
TaskID int64 `xorm:"not null" json:"-" param:"task"`
|
||||||
|
|
||||||
Created timeutil.TimeStamp `xorm:"created"`
|
Created int64 `xorm:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
Updated int64 `xorm:"updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s taskComments20200219183248) TableName() string {
|
func (s taskComments20200219183248) TableName() string {
|
||||||
|
|
|
@ -17,18 +17,17 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"src.techknowlogick.com/xormigrate"
|
"src.techknowlogick.com/xormigrate"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bucket20200418230605 struct {
|
type bucket20200418230605 struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk"`
|
||||||
Title string `xorm:"text not null"`
|
Title string `xorm:"text not null"`
|
||||||
ListID int64 `xorm:"int(11) not null"`
|
ListID int64 `xorm:"int(11) not null"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null"`
|
Created int64 `xorm:"created not null"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null"`
|
Updated int64 `xorm:"updated not null"`
|
||||||
CreatedByID int64 `xorm:"int(11) not null"`
|
CreatedByID int64 `xorm:"int(11) not null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bucket20200418230605) TableName() string {
|
func (b *bucket20200418230605) TableName() string {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"src.techknowlogick.com/xormigrate"
|
"src.techknowlogick.com/xormigrate"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
@ -32,10 +31,10 @@ func (l *list20200425182634) TableName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type task20200425182634 struct {
|
type task20200425182634 struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
||||||
ListID int64 `xorm:"int(11) INDEX not null" json:"list_id" param:"list"`
|
ListID int64 `xorm:"int(11) INDEX not null" json:"list_id" param:"list"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||||
BucketID int64 `xorm:"int(11) null" json:"bucket_id"`
|
BucketID int64 `xorm:"int(11) null" json:"bucket_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *task20200425182634) TableName() string {
|
func (t *task20200425182634) TableName() string {
|
||||||
|
@ -43,12 +42,12 @@ func (t *task20200425182634) TableName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type bucket20200425182634 struct {
|
type bucket20200425182634 struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"bucket"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"bucket"`
|
||||||
Title string `xorm:"text not null" valid:"required" minLength:"1" json:"title"`
|
Title string `xorm:"text not null" valid:"required" minLength:"1" json:"title"`
|
||||||
ListID int64 `xorm:"int(11) not null" json:"list_id" param:"list"`
|
ListID int64 `xorm:"int(11) not null" json:"list_id" param:"list"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created int64 `xorm:"created not null" json:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bucket20200425182634) TableName() string {
|
func (b *bucket20200425182634) TableName() string {
|
||||||
|
|
|
@ -17,23 +17,22 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"src.techknowlogick.com/xormigrate"
|
"src.techknowlogick.com/xormigrate"
|
||||||
"strings"
|
"strings"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type list20200516123847 struct {
|
type list20200516123847 struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"list"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"list"`
|
||||||
Title string `xorm:"varchar(250) not null" json:"title" valid:"required,runelength(3|250)" minLength:"3" maxLength:"250"`
|
Title string `xorm:"varchar(250) not null" json:"title" valid:"required,runelength(3|250)" minLength:"3" maxLength:"250"`
|
||||||
Description string `xorm:"longtext null" json:"description"`
|
Description string `xorm:"longtext null" json:"description"`
|
||||||
Identifier string `xorm:"varchar(10) null" json:"identifier" valid:"runelength(0|10)" minLength:"0" maxLength:"10"`
|
Identifier string `xorm:"varchar(10) null" json:"identifier" valid:"runelength(0|10)" minLength:"0" maxLength:"10"`
|
||||||
HexColor string `xorm:"varchar(6) null" json:"hex_color" valid:"runelength(0|6)" maxLength:"6"`
|
HexColor string `xorm:"varchar(6) null" json:"hex_color" valid:"runelength(0|6)" maxLength:"6"`
|
||||||
OwnerID int64 `xorm:"int(11) INDEX not null" json:"-"`
|
OwnerID int64 `xorm:"int(11) INDEX not null" json:"-"`
|
||||||
NamespaceID int64 `xorm:"int(11) INDEX not null" json:"-" param:"namespace"`
|
NamespaceID int64 `xorm:"int(11) INDEX not null" json:"-" param:"namespace"`
|
||||||
IsArchived bool `xorm:"not null default false" json:"is_archived" query:"is_archived"`
|
IsArchived bool `xorm:"not null default false" json:"is_archived" query:"is_archived"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created int64 `xorm:"created not null" json:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated int64 `xorm:"updated not null" json:"updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *list20200516123847) TableName() string {
|
func (l *list20200516123847) TableName() string {
|
||||||
|
|
791
pkg/migration/20200621214452.go
Normal file
791
pkg/migration/20200621214452.go
Normal file
|
@ -0,0 +1,791 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"src.techknowlogick.com/xormigrate"
|
||||||
|
"strings"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations = append(migrations, &xormigrate.Migration{
|
||||||
|
ID: "20200621214452",
|
||||||
|
Description: "Make all dates to iso time",
|
||||||
|
Migrate: func(tx *xorm.Engine) error {
|
||||||
|
|
||||||
|
// Big query for sqlite goes here
|
||||||
|
// SQLite is not capable of modifying columns directly like mysql or postgres, so we need to add
|
||||||
|
// all the sql we need manually here.
|
||||||
|
if tx.Dialect().URI().DBType == schemas.SQLITE {
|
||||||
|
sql := `
|
||||||
|
--- Buckets
|
||||||
|
create table buckets_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
title TEXT not null,
|
||||||
|
list_id INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null,
|
||||||
|
created_by_id INTEGER not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into buckets_dg_tmp(id, title, list_id, created, updated, created_by_id) select id, title, list_id, created, updated, created_by_id from buckets;
|
||||||
|
|
||||||
|
drop table buckets;
|
||||||
|
|
||||||
|
alter table buckets_dg_tmp rename to buckets;
|
||||||
|
|
||||||
|
create unique index UQE_buckets_id
|
||||||
|
on buckets (id);
|
||||||
|
|
||||||
|
--- files
|
||||||
|
|
||||||
|
create table files_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
name TEXT not null,
|
||||||
|
mime TEXT,
|
||||||
|
size INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
created_by_id INTEGER not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into files_dg_tmp(id, name, mime, size, created, created_by_id) select id, name, mime, size, created_unix, created_by_id from files;
|
||||||
|
|
||||||
|
drop table files;
|
||||||
|
|
||||||
|
alter table files_dg_tmp rename to files;
|
||||||
|
|
||||||
|
create unique index UQE_files_id
|
||||||
|
on files (id);
|
||||||
|
|
||||||
|
--- label_task
|
||||||
|
create table label_task_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
label_id INTEGER not null,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into label_task_dg_tmp(id, task_id, label_id, created) select id, task_id, label_id, created from label_task;
|
||||||
|
|
||||||
|
drop table label_task;
|
||||||
|
|
||||||
|
alter table label_task_dg_tmp rename to label_task;
|
||||||
|
|
||||||
|
create index IDX_label_task_label_id
|
||||||
|
on label_task (label_id);
|
||||||
|
|
||||||
|
create index IDX_label_task_task_id
|
||||||
|
on label_task (task_id);
|
||||||
|
|
||||||
|
create unique index UQE_label_task_id
|
||||||
|
on label_task (id);
|
||||||
|
|
||||||
|
--- labels
|
||||||
|
create table labels_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
title TEXT not null,
|
||||||
|
description TEXT,
|
||||||
|
hex_color TEXT,
|
||||||
|
created_by_id INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into labels_dg_tmp(id, title, description, hex_color, created_by_id, created, updated) select id, title, description, hex_color, created_by_id, created, updated from labels;
|
||||||
|
|
||||||
|
drop table labels;
|
||||||
|
|
||||||
|
alter table labels_dg_tmp rename to labels;
|
||||||
|
|
||||||
|
create unique index UQE_labels_id
|
||||||
|
on labels (id);
|
||||||
|
|
||||||
|
--- link_sharing
|
||||||
|
create table link_sharing_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
hash TEXT not null,
|
||||||
|
list_id INTEGER not null,
|
||||||
|
"right" INTEGER default 0 not null,
|
||||||
|
sharing_type INTEGER default 0 not null,
|
||||||
|
shared_by_id INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into link_sharing_dg_tmp(id, hash, list_id, "right", sharing_type, shared_by_id, created, updated) select id, hash, list_id, "right", sharing_type, shared_by_id, created, updated from link_sharing;
|
||||||
|
|
||||||
|
drop table link_sharing;
|
||||||
|
|
||||||
|
alter table link_sharing_dg_tmp rename to link_sharing;
|
||||||
|
|
||||||
|
create index IDX_link_sharing_right
|
||||||
|
on link_sharing ("right");
|
||||||
|
|
||||||
|
create index IDX_link_sharing_shared_by_id
|
||||||
|
on link_sharing (shared_by_id);
|
||||||
|
|
||||||
|
create index IDX_link_sharing_sharing_type
|
||||||
|
on link_sharing (sharing_type);
|
||||||
|
|
||||||
|
create unique index UQE_link_sharing_hash
|
||||||
|
on link_sharing (hash);
|
||||||
|
|
||||||
|
create unique index UQE_link_sharing_id
|
||||||
|
on link_sharing (id);
|
||||||
|
|
||||||
|
--- list
|
||||||
|
create table list_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
title TEXT not null,
|
||||||
|
description TEXT,
|
||||||
|
identifier TEXT,
|
||||||
|
hex_color TEXT,
|
||||||
|
owner_id INTEGER not null,
|
||||||
|
namespace_id INTEGER not null,
|
||||||
|
is_archived INTEGER default 0 not null,
|
||||||
|
background_file_id INTEGER,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into list_dg_tmp(id, title, description, identifier, hex_color, owner_id, namespace_id, is_archived, background_file_id, created, updated) select id, title, description, identifier, hex_color, owner_id, namespace_id, is_archived, background_file_id, created, updated from list;
|
||||||
|
|
||||||
|
drop table list;
|
||||||
|
|
||||||
|
alter table list_dg_tmp rename to list;
|
||||||
|
|
||||||
|
create index IDX_list_namespace_id
|
||||||
|
on list (namespace_id);
|
||||||
|
|
||||||
|
create index IDX_list_owner_id
|
||||||
|
on list (owner_id);
|
||||||
|
|
||||||
|
create unique index UQE_list_id
|
||||||
|
on list (id);
|
||||||
|
|
||||||
|
--- migration_status
|
||||||
|
create table migration_status_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
user_id INTEGER not null,
|
||||||
|
migrator_name TEXT,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into migration_status_dg_tmp(id, user_id, migrator_name, created) select id, user_id, migrator_name, created_unix from migration_status;
|
||||||
|
|
||||||
|
drop table migration_status;
|
||||||
|
|
||||||
|
alter table migration_status_dg_tmp rename to migration_status;
|
||||||
|
|
||||||
|
create unique index UQE_migration_status_id
|
||||||
|
on migration_status (id);
|
||||||
|
|
||||||
|
--- namespaces
|
||||||
|
create table namespaces_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
title TEXT not null,
|
||||||
|
description TEXT,
|
||||||
|
owner_id INTEGER not null,
|
||||||
|
hex_color TEXT,
|
||||||
|
is_archived INTEGER default 0 not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into namespaces_dg_tmp(id, title, description, owner_id, hex_color, is_archived, created, updated) select id, title, description, owner_id, hex_color, is_archived, created, updated from namespaces;
|
||||||
|
|
||||||
|
drop table namespaces;
|
||||||
|
|
||||||
|
alter table namespaces_dg_tmp rename to namespaces;
|
||||||
|
|
||||||
|
create index IDX_namespaces_owner_id
|
||||||
|
on namespaces (owner_id);
|
||||||
|
|
||||||
|
create unique index UQE_namespaces_id
|
||||||
|
on namespaces (id);
|
||||||
|
|
||||||
|
--- task_assignees
|
||||||
|
create table task_assignees_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
user_id INTEGER not null,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into task_assignees_dg_tmp(id, task_id, user_id, created) select id, task_id, user_id, created from task_assignees;
|
||||||
|
|
||||||
|
drop table task_assignees;
|
||||||
|
|
||||||
|
alter table task_assignees_dg_tmp rename to task_assignees;
|
||||||
|
|
||||||
|
create index IDX_task_assignees_task_id
|
||||||
|
on task_assignees (task_id);
|
||||||
|
|
||||||
|
create index IDX_task_assignees_user_id
|
||||||
|
on task_assignees (user_id);
|
||||||
|
|
||||||
|
create unique index UQE_task_assignees_id
|
||||||
|
on task_assignees (id);
|
||||||
|
|
||||||
|
--- task_attachments
|
||||||
|
create table task_attachments_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
file_id INTEGER not null,
|
||||||
|
created_by_id INTEGER not null,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into task_attachments_dg_tmp(id, task_id, file_id, created_by_id, created) select id, task_id, file_id, created_by_id, created from task_attachments;
|
||||||
|
|
||||||
|
drop table task_attachments;
|
||||||
|
|
||||||
|
alter table task_attachments_dg_tmp rename to task_attachments;
|
||||||
|
|
||||||
|
create unique index UQE_task_attachments_id
|
||||||
|
on task_attachments (id);
|
||||||
|
|
||||||
|
--- task_comments
|
||||||
|
create table task_comments_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
comment TEXT not null,
|
||||||
|
author_id INTEGER not null,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into task_comments_dg_tmp(id, comment, author_id, task_id, created, updated) select id, comment, author_id, task_id, created, updated from task_comments;
|
||||||
|
|
||||||
|
drop table task_comments;
|
||||||
|
|
||||||
|
alter table task_comments_dg_tmp rename to task_comments;
|
||||||
|
|
||||||
|
create unique index UQE_task_comments_id
|
||||||
|
on task_comments (id);
|
||||||
|
|
||||||
|
--- task_relations
|
||||||
|
create table task_relations_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
other_task_id INTEGER not null,
|
||||||
|
relation_kind TEXT not null,
|
||||||
|
created_by_id INTEGER not null,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into task_relations_dg_tmp(id, task_id, other_task_id, relation_kind, created_by_id, created) select id, task_id, other_task_id, relation_kind, created_by_id, created from task_relations;
|
||||||
|
|
||||||
|
drop table task_relations;
|
||||||
|
|
||||||
|
alter table task_relations_dg_tmp rename to task_relations;
|
||||||
|
|
||||||
|
create unique index UQE_task_relations_id
|
||||||
|
on task_relations (id);
|
||||||
|
|
||||||
|
--- task_reminders
|
||||||
|
create table task_reminders_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
task_id INTEGER not null,
|
||||||
|
reminder datetime not null,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into task_reminders_dg_tmp(id, task_id, reminder, created) select id, task_id, reminder_unix, created from task_reminders;
|
||||||
|
|
||||||
|
drop table task_reminders;
|
||||||
|
|
||||||
|
alter table task_reminders_dg_tmp rename to task_reminders;
|
||||||
|
|
||||||
|
create index IDX_task_reminders_reminder_unix
|
||||||
|
on task_reminders (reminder);
|
||||||
|
|
||||||
|
create index IDX_task_reminders_task_id
|
||||||
|
on task_reminders (task_id);
|
||||||
|
|
||||||
|
create unique index UQE_task_reminders_id
|
||||||
|
on task_reminders (id);
|
||||||
|
|
||||||
|
--- tasks
|
||||||
|
create table tasks_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
title TEXT not null,
|
||||||
|
description TEXT,
|
||||||
|
done INTEGER,
|
||||||
|
done_at datetime,
|
||||||
|
due_date datetime,
|
||||||
|
created_by_id INTEGER not null,
|
||||||
|
list_id INTEGER not null,
|
||||||
|
repeat_after INTEGER,
|
||||||
|
repeat_from_current_date INTEGER,
|
||||||
|
priority INTEGER,
|
||||||
|
start_date datetime,
|
||||||
|
end_date datetime,
|
||||||
|
hex_color TEXT,
|
||||||
|
percent_done REAL,
|
||||||
|
"index" INTEGER default 0 not null,
|
||||||
|
uid TEXT,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null,
|
||||||
|
bucket_id INTEGER,
|
||||||
|
position REAL
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into tasks_dg_tmp(id, title, description, done, done_at, due_date, created_by_id, list_id, repeat_after, repeat_from_current_date, priority, start_date, end_date, hex_color, percent_done, "index", uid, created, updated, bucket_id, position) select id, title, description, done, done_at_unix, due_date_unix, created_by_id, list_id, repeat_after, repeat_from_current_date, priority, start_date_unix, end_date_unix, hex_color, percent_done, "index", uid, created, updated, bucket_id, position from tasks;
|
||||||
|
|
||||||
|
drop table tasks;
|
||||||
|
|
||||||
|
alter table tasks_dg_tmp rename to tasks;
|
||||||
|
|
||||||
|
create index IDX_tasks_done
|
||||||
|
on tasks (done);
|
||||||
|
|
||||||
|
create index IDX_tasks_done_at_unix
|
||||||
|
on tasks (done_at);
|
||||||
|
|
||||||
|
create index IDX_tasks_due_date_unix
|
||||||
|
on tasks (due_date);
|
||||||
|
|
||||||
|
create index IDX_tasks_end_date_unix
|
||||||
|
on tasks (end_date);
|
||||||
|
|
||||||
|
create index IDX_tasks_list_id
|
||||||
|
on tasks (list_id);
|
||||||
|
|
||||||
|
create index IDX_tasks_repeat_after
|
||||||
|
on tasks (repeat_after);
|
||||||
|
|
||||||
|
create index IDX_tasks_start_date_unix
|
||||||
|
on tasks (start_date);
|
||||||
|
|
||||||
|
create unique index UQE_tasks_id
|
||||||
|
on tasks (id);
|
||||||
|
|
||||||
|
--- team_list
|
||||||
|
create table team_list_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
team_id INTEGER not null,
|
||||||
|
list_id INTEGER not null,
|
||||||
|
"right" INTEGER default 0 not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into team_list_dg_tmp(id, team_id, list_id, "right", created, updated) select id, team_id, list_id, "right", created, updated from team_list;
|
||||||
|
|
||||||
|
drop table team_list;
|
||||||
|
|
||||||
|
alter table team_list_dg_tmp rename to team_list;
|
||||||
|
|
||||||
|
create index IDX_team_list_list_id
|
||||||
|
on team_list (list_id);
|
||||||
|
|
||||||
|
create index IDX_team_list_right
|
||||||
|
on team_list ("right");
|
||||||
|
|
||||||
|
create index IDX_team_list_team_id
|
||||||
|
on team_list (team_id);
|
||||||
|
|
||||||
|
create unique index UQE_team_list_id
|
||||||
|
on team_list (id);
|
||||||
|
|
||||||
|
--- team_members
|
||||||
|
create table team_members_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
team_id INTEGER not null,
|
||||||
|
user_id INTEGER not null,
|
||||||
|
admin INTEGER,
|
||||||
|
created datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into team_members_dg_tmp(id, team_id, user_id, admin, created) select id, team_id, user_id, admin, created from team_members;
|
||||||
|
|
||||||
|
drop table team_members;
|
||||||
|
|
||||||
|
alter table team_members_dg_tmp rename to team_members;
|
||||||
|
|
||||||
|
create index IDX_team_members_team_id
|
||||||
|
on team_members (team_id);
|
||||||
|
|
||||||
|
create index IDX_team_members_user_id
|
||||||
|
on team_members (user_id);
|
||||||
|
|
||||||
|
create unique index UQE_team_members_id
|
||||||
|
on team_members (id);
|
||||||
|
|
||||||
|
--- team_namespaces
|
||||||
|
create table team_namespaces_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
team_id INTEGER not null,
|
||||||
|
namespace_id INTEGER not null,
|
||||||
|
"right" INTEGER default 0 not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into team_namespaces_dg_tmp(id, team_id, namespace_id, "right", created, updated) select id, team_id, namespace_id, "right", created, updated from team_namespaces;
|
||||||
|
|
||||||
|
drop table team_namespaces;
|
||||||
|
|
||||||
|
alter table team_namespaces_dg_tmp rename to team_namespaces;
|
||||||
|
|
||||||
|
create index IDX_team_namespaces_namespace_id
|
||||||
|
on team_namespaces (namespace_id);
|
||||||
|
|
||||||
|
create index IDX_team_namespaces_right
|
||||||
|
on team_namespaces ("right");
|
||||||
|
|
||||||
|
create index IDX_team_namespaces_team_id
|
||||||
|
on team_namespaces (team_id);
|
||||||
|
|
||||||
|
create unique index UQE_team_namespaces_id
|
||||||
|
on team_namespaces (id);
|
||||||
|
|
||||||
|
--- teams
|
||||||
|
create table teams_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
name TEXT not null,
|
||||||
|
description TEXT,
|
||||||
|
created_by_id INTEGER not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into teams_dg_tmp(id, name, description, created_by_id, created, updated) select id, name, description, created_by_id, created, updated from teams;
|
||||||
|
|
||||||
|
drop table teams;
|
||||||
|
|
||||||
|
alter table teams_dg_tmp rename to teams;
|
||||||
|
|
||||||
|
create index IDX_teams_created_by_id
|
||||||
|
on teams (created_by_id);
|
||||||
|
|
||||||
|
create unique index UQE_teams_id
|
||||||
|
on teams (id);
|
||||||
|
|
||||||
|
--- users
|
||||||
|
create table users_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
username TEXT not null,
|
||||||
|
password TEXT not null,
|
||||||
|
email TEXT,
|
||||||
|
is_active INTEGER,
|
||||||
|
password_reset_token TEXT,
|
||||||
|
email_confirm_token TEXT,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into users_dg_tmp(id, username, password, email, is_active, password_reset_token, email_confirm_token, created, updated) select id, username, password, email, is_active, password_reset_token, email_confirm_token, created, updated from users;
|
||||||
|
|
||||||
|
drop table users;
|
||||||
|
|
||||||
|
alter table users_dg_tmp rename to users;
|
||||||
|
|
||||||
|
create unique index UQE_users_id
|
||||||
|
on users (id);
|
||||||
|
|
||||||
|
create unique index UQE_users_username
|
||||||
|
on users (username);
|
||||||
|
|
||||||
|
--- users_list
|
||||||
|
create table users_list_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
user_id INTEGER not null,
|
||||||
|
list_id INTEGER not null,
|
||||||
|
"right" INTEGER default 0 not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into users_list_dg_tmp(id, user_id, list_id, "right", created, updated) select id, user_id, list_id, "right", created, updated from users_list;
|
||||||
|
|
||||||
|
drop table users_list;
|
||||||
|
|
||||||
|
alter table users_list_dg_tmp rename to users_list;
|
||||||
|
|
||||||
|
create index IDX_users_list_list_id
|
||||||
|
on users_list (list_id);
|
||||||
|
|
||||||
|
create index IDX_users_list_right
|
||||||
|
on users_list ("right");
|
||||||
|
|
||||||
|
create index IDX_users_list_user_id
|
||||||
|
on users_list (user_id);
|
||||||
|
|
||||||
|
create unique index UQE_users_list_id
|
||||||
|
on users_list (id);
|
||||||
|
|
||||||
|
--- users_namespace
|
||||||
|
create table users_namespace_dg_tmp
|
||||||
|
(
|
||||||
|
id INTEGER not null
|
||||||
|
primary key autoincrement,
|
||||||
|
user_id INTEGER not null,
|
||||||
|
namespace_id INTEGER not null,
|
||||||
|
"right" INTEGER default 0 not null,
|
||||||
|
created datetime not null,
|
||||||
|
updated datetime not null
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into users_namespace_dg_tmp(id, user_id, namespace_id, "right", created, updated) select id, user_id, namespace_id, "right", created, updated from users_namespace;
|
||||||
|
|
||||||
|
drop table users_namespace;
|
||||||
|
|
||||||
|
alter table users_namespace_dg_tmp rename to users_namespace;
|
||||||
|
|
||||||
|
create index IDX_users_namespace_namespace_id
|
||||||
|
on users_namespace (namespace_id);
|
||||||
|
|
||||||
|
create index IDX_users_namespace_right
|
||||||
|
on users_namespace ("right");
|
||||||
|
|
||||||
|
create index IDX_users_namespace_user_id
|
||||||
|
on users_namespace (user_id);
|
||||||
|
|
||||||
|
create unique index UQE_users_namespace_id
|
||||||
|
on users_namespace (id);
|
||||||
|
`
|
||||||
|
sess := tx.NewSession()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := sess.Exec(sql)
|
||||||
|
if err != nil {
|
||||||
|
_ = sess.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := sess.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convertTime := func(table, column string) error {
|
||||||
|
|
||||||
|
var sql []string
|
||||||
|
colOld := "`" + column + "`"
|
||||||
|
colTmp := "`" + column + `_ts` + "`"
|
||||||
|
// If the column namme ends with "_unix", we want to directly remove that since the timestamp
|
||||||
|
// isn't a unix one anymore.
|
||||||
|
var colFinal = colOld
|
||||||
|
if strings.HasSuffix(column, "_unix") {
|
||||||
|
colFinal = "`" + column[:len(column)-5] + "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tx.Dialect().URI().DBType {
|
||||||
|
case schemas.POSTGRES:
|
||||||
|
sql = []string{
|
||||||
|
"ALTER TABLE " + table + " DROP COLUMN IF EXISTS " + colTmp + ";",
|
||||||
|
"ALTER TABLE " + table + " ADD COLUMN " + colTmp + " TIMESTAMP WITHOUT TIME ZONE NULL;",
|
||||||
|
}
|
||||||
|
if colFinal != colOld {
|
||||||
|
sql = append(sql, "ALTER TABLE "+table+" ADD COLUMN "+colFinal+" TIMESTAMP WITHOUT TIME ZONE NULL;")
|
||||||
|
}
|
||||||
|
sql = append(sql,
|
||||||
|
// #nosec
|
||||||
|
"UPDATE "+table+" SET "+colTmp+" = (CASE WHEN "+colOld+" = 0 THEN NULL ELSE TIMESTAMP 'epoch' + "+colOld+" * INTERVAL '1 second' END);",
|
||||||
|
"ALTER TABLE "+table+" ALTER COLUMN "+colFinal+" TYPE TIMESTAMP USING "+colTmp+";",
|
||||||
|
"ALTER TABLE "+table+" DROP COLUMN "+colTmp+";",
|
||||||
|
)
|
||||||
|
if colFinal != colOld {
|
||||||
|
sql = append(sql, "ALTER TABLE "+table+" DROP COLUMN "+colOld+";")
|
||||||
|
}
|
||||||
|
case schemas.MYSQL:
|
||||||
|
sql = []string{
|
||||||
|
"ALTER TABLE " + table + " DROP COLUMN IF EXISTS " + colTmp + ";",
|
||||||
|
"ALTER TABLE " + table + " ADD COLUMN " + colTmp + " DATETIME NULL;",
|
||||||
|
// #nosec
|
||||||
|
"UPDATE " + table + " SET " + colTmp + " = IF(" + colOld + " = 0, NULL, FROM_UNIXTIME(" + colOld + "));",
|
||||||
|
"ALTER TABLE " + table + " DROP COLUMN " + colOld + ";",
|
||||||
|
"ALTER TABLE " + table + " CHANGE " + colTmp + " " + colFinal + " DATETIME NULL;",
|
||||||
|
}
|
||||||
|
case schemas.SQLITE:
|
||||||
|
// welp
|
||||||
|
// All created and updated columns are set to not null
|
||||||
|
// But some of the test data is 0 so we can't use our update script on it.
|
||||||
|
if column != "updated" && column != "created" {
|
||||||
|
sql = []string{
|
||||||
|
// #nosec
|
||||||
|
"UPDATE " + table + " SET " + colFinal + " = CASE WHEN " + colFinal + " > 0 THEN DATETIME(" + colFinal + ", 'unixepoch', 'localtime') ELSE NULL END",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sql = []string{
|
||||||
|
// #nosec
|
||||||
|
"UPDATE " + table + " SET " + colFinal + " = DATETIME(" + colFinal + ", 'unixepoch', 'localtime')",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported dbms: %s", tx.Dialect().URI().DBType)
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := tx.NewSession()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return fmt.Errorf("unable to open session: %s", err)
|
||||||
|
}
|
||||||
|
for _, s := range sql {
|
||||||
|
_, err := sess.Exec(s)
|
||||||
|
if err != nil {
|
||||||
|
_ = sess.Rollback()
|
||||||
|
return fmt.Errorf("error executing update data for table %s, column %s: %s", table, column, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := sess.Commit(); err != nil {
|
||||||
|
return fmt.Errorf("error committing data change: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for table, columns := range map[string][]string{
|
||||||
|
"buckets": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"created_unix",
|
||||||
|
},
|
||||||
|
"label_task": {
|
||||||
|
"created",
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"link_sharing": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"migration_status": {
|
||||||
|
"created_unix",
|
||||||
|
},
|
||||||
|
"namespaces": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"task_assignees": {
|
||||||
|
"created",
|
||||||
|
},
|
||||||
|
"task_attachments": {
|
||||||
|
"created",
|
||||||
|
},
|
||||||
|
"task_comments": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"task_relations": {
|
||||||
|
"created",
|
||||||
|
},
|
||||||
|
"task_reminders": {
|
||||||
|
"created",
|
||||||
|
"reminder_unix",
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"done_at_unix",
|
||||||
|
"due_date_unix",
|
||||||
|
"start_date_unix",
|
||||||
|
"end_date_unix",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"team_list": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"team_members": {
|
||||||
|
"created",
|
||||||
|
},
|
||||||
|
"team_namespaces": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"teams": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"users_list": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
"users_namespace": {
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
for _, column := range columns {
|
||||||
|
if err := convertTime(table, column); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Rollback: func(tx *xorm.Engine) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -113,12 +113,12 @@ func (bt *BulkTask) Update() (err error) {
|
||||||
Cols("title",
|
Cols("title",
|
||||||
"description",
|
"description",
|
||||||
"done",
|
"done",
|
||||||
"due_date_unix",
|
"due_date",
|
||||||
"reminders_unix",
|
"reminders",
|
||||||
"repeat_after",
|
"repeat_after",
|
||||||
"priority",
|
"priority",
|
||||||
"start_date_unix",
|
"start_date",
|
||||||
"end_date_unix").
|
"end_date").
|
||||||
Update(oldtask)
|
Update(oldtask)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sess.Rollback()
|
return sess.Rollback()
|
||||||
|
|
|
@ -734,6 +734,34 @@ func (err ErrInvalidTaskFilterConcatinator) HTTPError() web.HTTPError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInvalidTaskFilterValue represents an error where the provided task filter value is invalid
|
||||||
|
type ErrInvalidTaskFilterValue struct {
|
||||||
|
Value string
|
||||||
|
Field string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidTaskFilterValue checks if an error is ErrInvalidTaskFilterValue.
|
||||||
|
func IsErrInvalidTaskFilterValue(err error) bool {
|
||||||
|
_, ok := err.(ErrInvalidTaskFilterValue)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidTaskFilterValue) Error() string {
|
||||||
|
return fmt.Sprintf("Task filter value is invalid [Value: %s, Field: %s]", err.Value, err.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCodeInvalidTaskFilterValue holds the unique world-error code of this error
|
||||||
|
const ErrCodeInvalidTaskFilterValue = 4019
|
||||||
|
|
||||||
|
// HTTPError holds the http error description
|
||||||
|
func (err ErrInvalidTaskFilterValue) HTTPError() web.HTTPError {
|
||||||
|
return web.HTTPError{
|
||||||
|
HTTPCode: http.StatusBadRequest,
|
||||||
|
Code: ErrCodeInvalidTaskFilterValue,
|
||||||
|
Message: fmt.Sprintf("The task filter value '%s' for field '%s' is invalid.", err.Value, err.Field),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =================
|
// =================
|
||||||
// Namespace errors
|
// Namespace errors
|
||||||
// =================
|
// =================
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bucket represents a kanban bucket
|
// Bucket represents a kanban bucket
|
||||||
|
@ -35,9 +35,9 @@ type Bucket struct {
|
||||||
Tasks []*Task `xorm:"-" json:"tasks"`
|
Tasks []*Task `xorm:"-" json:"tasks"`
|
||||||
|
|
||||||
// A timestamp when this bucket was created. You cannot change this value.
|
// A timestamp when this bucket was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this bucket was last updated. You cannot change this value.
|
// A timestamp when this bucket was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
// The user who initially created the bucket.
|
// The user who initially created the bucket.
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
CreatedBy *user.User `xorm:"-" json:"created_by" valid:"-"`
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Label represents a label
|
// Label represents a label
|
||||||
|
@ -38,9 +38,9 @@ type Label struct {
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
||||||
|
|
||||||
// A timestamp when this label was created. You cannot change this value.
|
// A timestamp when this label was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this label was last updated. You cannot change this value.
|
// A timestamp when this label was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ type LabelTask struct {
|
||||||
// The label id you want to associate with a task.
|
// The label id you want to associate with a task.
|
||||||
LabelID int64 `xorm:"int(11) INDEX not null" json:"label_id" param:"label"`
|
LabelID int64 `xorm:"int(11) INDEX not null" json:"label_id" param:"label"`
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -2,12 +2,12 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -17,7 +17,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,15 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||||
Label: Label{
|
Label: Label{
|
||||||
ID: 4,
|
ID: 4,
|
||||||
Title: "Label #4 - visible via other task",
|
Title: "Label #4 - visible via other task",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
CreatedByID: 2,
|
CreatedByID: 2,
|
||||||
CreatedBy: &user.User{
|
CreatedBy: &user.User{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -113,7 +117,7 @@ func TestLabelTask_Create(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -207,7 +211,7 @@ func TestLabelTask_Delete(t *testing.T) {
|
||||||
ID int64
|
ID int64
|
||||||
TaskID int64
|
TaskID int64
|
||||||
LabelID int64
|
LabelID int64
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -36,8 +36,8 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,8 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -71,6 +73,8 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Title: "Label #1",
|
Title: "Label #1",
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -79,17 +83,23 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||||
Title: "Label #2",
|
Title: "Label #2",
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Label: Label{
|
Label: Label{
|
||||||
ID: 4,
|
ID: 4,
|
||||||
Title: "Label #4 - visible via other task",
|
Title: "Label #4 - visible via other task",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
CreatedByID: 2,
|
CreatedByID: 2,
|
||||||
CreatedBy: &user.User{
|
CreatedBy: &user.User{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -138,8 +148,8 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -148,6 +158,8 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -168,6 +180,8 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
Title: "Label #1",
|
Title: "Label #1",
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
auth: &user.User{ID: 1},
|
auth: &user.User{ID: 1},
|
||||||
},
|
},
|
||||||
|
@ -202,7 +216,11 @@ func TestLabel_ReadOne(t *testing.T) {
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
auth: &user.User{ID: 1},
|
auth: &user.User{ID: 1},
|
||||||
},
|
},
|
||||||
|
@ -248,8 +266,8 @@ func TestLabel_Create(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -308,8 +326,8 @@ func TestLabel_Update(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -390,8 +408,8 @@ func TestLabel_Delete(t *testing.T) {
|
||||||
HexColor string
|
HexColor string
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SharingType holds the sharing type
|
// SharingType holds the sharing type
|
||||||
|
@ -54,9 +54,9 @@ type LinkSharing struct {
|
||||||
SharedByID int64 `xorm:"int(11) INDEX not null" json:"-"`
|
SharedByID int64 `xorm:"int(11) INDEX not null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this list was shared. You cannot change this value.
|
// A timestamp when this list was shared. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this share was last updated. You cannot change this value.
|
// A timestamp when this share was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -19,10 +19,10 @@ package models
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
@ -58,9 +58,9 @@ type List struct {
|
||||||
BackgroundInformation interface{} `xorm:"-" json:"background_information"`
|
BackgroundInformation interface{} `xorm:"-" json:"background_information"`
|
||||||
|
|
||||||
// A timestamp when this list was created. You cannot change this value.
|
// A timestamp when this list was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this list was last updated. You cannot change this value.
|
// A timestamp when this list was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TeamList defines the relation between a team and a list
|
// TeamList defines the relation between a team and a list
|
||||||
|
@ -33,9 +33,9 @@ type TeamList struct {
|
||||||
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,13 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTeamList(t *testing.T) {
|
func TestTeamList(t *testing.T) {
|
||||||
|
@ -121,8 +121,8 @@ func TestTeamList_Update(t *testing.T) {
|
||||||
TeamID int64
|
TeamID int64
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListUser represents a list <-> user relation
|
// ListUser represents a list <-> user relation
|
||||||
|
@ -36,9 +36,9 @@ type ListUser struct {
|
||||||
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -31,8 +31,8 @@ func TestListUser_CanDoSomething(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -35,8 +35,8 @@ func TestListUser_Create(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -136,8 +136,8 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,8 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Right: RightRead,
|
||||||
},
|
},
|
||||||
|
@ -177,6 +179,8 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Right: RightRead,
|
||||||
},
|
},
|
||||||
|
@ -228,8 +232,8 @@ func TestListUser_Update(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -305,8 +309,8 @@ func TestListUser_Delete(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
ListID int64
|
ListID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,37 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func setupTime() {
|
||||||
|
var err error
|
||||||
|
loc, err := time.LoadLocation("GMT")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
testCreatedTime, err = time.ParseInLocation(time.RFC3339Nano, "2018-12-01T15:13:12.0+00:00", loc)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
testCreatedTime = testCreatedTime.In(loc)
|
||||||
|
testUpdatedTime, err = time.ParseInLocation(time.RFC3339Nano, "2018-12-02T15:13:12.0+00:00", loc)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error setting up time: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
testUpdatedTime = testUpdatedTime.In(loc)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|
||||||
|
setupTime()
|
||||||
|
|
||||||
// Set default config
|
// Set default config
|
||||||
config.InitDefaultConfig()
|
config.InitDefaultConfig()
|
||||||
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
|
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
_ "github.com/go-sql-driver/mysql" // Because.
|
_ "github.com/go-sql-driver/mysql" // Because.
|
||||||
_ "github.com/lib/pq" // Because.
|
_ "github.com/lib/pq" // Because.
|
||||||
|
"time"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3" // Because.
|
_ "github.com/mattn/go-sqlite3" // Because.
|
||||||
|
@ -29,6 +30,9 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
x *xorm.Engine
|
||||||
|
|
||||||
|
testCreatedTime time.Time
|
||||||
|
testUpdatedTime time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTables returns all structs which are also a table.
|
// GetTables returns all structs which are also a table.
|
||||||
|
|
|
@ -18,7 +18,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
|
@ -46,9 +45,9 @@ type Namespace struct {
|
||||||
Owner *user.User `xorm:"-" json:"owner" valid:"-"`
|
Owner *user.User `xorm:"-" json:"owner" valid:"-"`
|
||||||
|
|
||||||
// A timestamp when this namespace was created. You cannot change this value.
|
// A timestamp when this namespace was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this namespace was last updated. You cannot change this value.
|
// A timestamp when this namespace was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
@ -59,8 +58,8 @@ var PseudoNamespace = Namespace{
|
||||||
ID: -1,
|
ID: -1,
|
||||||
Title: "Shared Lists",
|
Title: "Shared Lists",
|
||||||
Description: "Lists of other users shared with you via teams or directly.",
|
Description: "Lists of other users shared with you via teams or directly.",
|
||||||
Created: timeutil.FromTime(time.Now()),
|
Created: time.Now(),
|
||||||
Updated: timeutil.FromTime(time.Now()),
|
Updated: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TeamNamespace defines the relationship between a Team and a Namespace
|
// TeamNamespace defines the relationship between a Team and a Namespace
|
||||||
|
@ -33,9 +33,9 @@ type TeamNamespace struct {
|
||||||
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -31,8 +31,8 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
|
||||||
TeamID int64
|
TeamID int64
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTeamNamespace(t *testing.T) {
|
func TestTeamNamespace(t *testing.T) {
|
||||||
|
@ -113,8 +113,8 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||||
TeamID int64
|
TeamID int64
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
user2 "code.vikunja.io/api/pkg/user"
|
user2 "code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NamespaceUser represents a namespace <-> user relation
|
// NamespaceUser represents a namespace <-> user relation
|
||||||
|
@ -35,9 +35,9 @@ type NamespaceUser struct {
|
||||||
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
Right Right `xorm:"int(11) INDEX not null default 0" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -31,8 +31,8 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNamespaceUser_Create(t *testing.T) {
|
func TestNamespaceUser_Create(t *testing.T) {
|
||||||
|
@ -33,8 +33,8 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,8 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||||
UserID int64
|
UserID int64
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -166,6 +166,8 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Right: RightRead,
|
||||||
},
|
},
|
||||||
|
@ -174,6 +176,8 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
Right: RightRead,
|
Right: RightRead,
|
||||||
},
|
},
|
||||||
|
@ -226,8 +230,8 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
@ -303,8 +307,8 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||||
Username string
|
Username string
|
||||||
NamespaceID int64
|
NamespaceID int64
|
||||||
Right Right
|
Right Right
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,17 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskAssginee represents an assignment of a user to a task
|
// TaskAssginee represents an assignment of a user to a task
|
||||||
type TaskAssginee struct {
|
type TaskAssginee struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"-"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"-"`
|
||||||
TaskID int64 `xorm:"int(11) INDEX not null" json:"-" param:"listtask"`
|
TaskID int64 `xorm:"int(11) INDEX not null" json:"-" param:"listtask"`
|
||||||
UserID int64 `xorm:"int(11) INDEX not null" json:"user_id" param:"user"`
|
UserID int64 `xorm:"int(11) INDEX not null" json:"user_id" param:"user"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null"`
|
Created time.Time `xorm:"created not null"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,10 +18,10 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskAttachment is the definition of a task attachment
|
// TaskAttachment is the definition of a task attachment
|
||||||
|
@ -35,7 +35,7 @@ type TaskAttachment struct {
|
||||||
|
|
||||||
File *files.File `xorm:"-" json:"file"`
|
File *files.File `xorm:"-" json:"file"`
|
||||||
|
|
||||||
Created timeutil.TimeStamp `xorm:"created" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
@ -149,7 +149,6 @@ func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.File = fs[r.FileID]
|
r.File = fs[r.FileID]
|
||||||
r.File.Created = r.File.CreatedUnix.ToTime()
|
|
||||||
r.CreatedBy = us[r.CreatedByID]
|
r.CreatedBy = us[r.CreatedByID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ type TaskCollection struct {
|
||||||
FilterComparatorArr []string `query:"filter_comparator[]"`
|
FilterComparatorArr []string `query:"filter_comparator[]"`
|
||||||
// The way all filter conditions are concatenated together, can be either "and" or "or".,
|
// The way all filter conditions are concatenated together, can be either "and" or "or".,
|
||||||
FilterConcat string `query:"filter_concat"`
|
FilterConcat string `query:"filter_concat"`
|
||||||
|
// If set to true, the result will also include null values
|
||||||
|
FilterIncludeNulls bool `query:"filter_include_nulls"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
@ -57,14 +59,14 @@ func validateTaskField(fieldName string) error {
|
||||||
taskPropertyTitle,
|
taskPropertyTitle,
|
||||||
taskPropertyDescription,
|
taskPropertyDescription,
|
||||||
taskPropertyDone,
|
taskPropertyDone,
|
||||||
taskPropertyDoneAtUnix,
|
taskPropertyDoneAt,
|
||||||
taskPropertyDueDateUnix,
|
taskPropertyDueDate,
|
||||||
taskPropertyCreatedByID,
|
taskPropertyCreatedByID,
|
||||||
taskPropertyListID,
|
taskPropertyListID,
|
||||||
taskPropertyRepeatAfter,
|
taskPropertyRepeatAfter,
|
||||||
taskPropertyPriority,
|
taskPropertyPriority,
|
||||||
taskPropertyStartDateUnix,
|
taskPropertyStartDate,
|
||||||
taskPropertyEndDateUnix,
|
taskPropertyEndDate,
|
||||||
taskPropertyHexColor,
|
taskPropertyHexColor,
|
||||||
taskPropertyPercentDone,
|
taskPropertyPercentDone,
|
||||||
taskPropertyUID,
|
taskPropertyUID,
|
||||||
|
@ -87,12 +89,13 @@ func validateTaskField(fieldName string) error {
|
||||||
// @Param page query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
// @Param page query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||||
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
|
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
|
||||||
// @Param s query string false "Search tasks by task text."
|
// @Param s query string false "Search tasks by task text."
|
||||||
// @Param sort_by query string false "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with `order_by`. Possible values to sort by are `id`, `title`, `description`, `done`, `done_at_unix`, `due_date_unix`, `created_by_id`, `list_id`, `repeat_after`, `priority`, `start_date_unix`, `end_date_unix`, `hex_color`, `percent_done`, `uid`, `created`, `updated`. Default is `id`."
|
// @Param sort_by query string false "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with `order_by`. Possible values to sort by are `id`, `title`, `description`, `done`, `done_at`, `due_date`, `created_by_id`, `list_id`, `repeat_after`, `priority`, `start_date`, `end_date`, `hex_color`, `percent_done`, `uid`, `created`, `updated`. Default is `id`."
|
||||||
// @Param order_by query string false "The ordering parameter. Possible values to order by are `asc` or `desc`. Default is `asc`."
|
// @Param order_by query string false "The ordering parameter. Possible values to order by are `asc` or `desc`. Default is `asc`."
|
||||||
// @Param filter_by query string false "The name of the field to filter by. Accepts an array for multiple filters which will be chanied together, all supplied filter must match."
|
// @Param filter_by query string false "The name of the field to filter by. Accepts an array for multiple filters which will be chanied together, all supplied filter must match."
|
||||||
// @Param filter_value query string false "The value to filter for."
|
// @Param filter_value query string false "The value to filter for."
|
||||||
// @Param filter_comparator query string false "The comparator to use for a filter. Available values are `equals`, `greater`, `greater_equals`, `less` and `less_equals`. Defaults to `equals`"
|
// @Param filter_comparator query string false "The comparator to use for a filter. Available values are `equals`, `greater`, `greater_equals`, `less` and `less_equals`. Defaults to `equals`"
|
||||||
// @Param filter_concat query string false "The concatinator to use for filters. Available values are `and` or `or`. Defaults to `or`."
|
// @Param filter_concat query string false "The concatinator to use for filters. Available values are `and` or `or`. Defaults to `or`."
|
||||||
|
// @Param filter_include_nulls query string false "If set to true the result will include filtered fields whose value is set to `null`. Available values are `true` or `false`. Defaults to `false`."
|
||||||
// @Security JWTKeyAuth
|
// @Security JWTKeyAuth
|
||||||
// @Success 200 {array} models.Task "The tasks"
|
// @Success 200 {array} models.Task "The tasks"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
@ -119,19 +122,6 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
|
||||||
param.orderBy = getSortOrderFromString(tf.OrderBy[i])
|
param.orderBy = getSortOrderFromString(tf.OrderBy[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case for pseudo date fields
|
|
||||||
// FIXME: This is really dirty, to fix this properly the db fields should be renamed
|
|
||||||
switch param.sortBy {
|
|
||||||
case "done_at":
|
|
||||||
param.sortBy = taskPropertyDoneAtUnix
|
|
||||||
case "due_date":
|
|
||||||
param.sortBy = taskPropertyDueDateUnix
|
|
||||||
case "start_date":
|
|
||||||
param.sortBy = taskPropertyStartDateUnix
|
|
||||||
case "end_date":
|
|
||||||
param.sortBy = taskPropertyEndDateUnix
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param validation
|
// Param validation
|
||||||
if err := param.validate(); err != nil {
|
if err := param.validate(); err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
|
@ -140,11 +130,12 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
|
||||||
}
|
}
|
||||||
|
|
||||||
taskopts := &taskOptions{
|
taskopts := &taskOptions{
|
||||||
search: search,
|
search: search,
|
||||||
page: page,
|
page: page,
|
||||||
perPage: perPage,
|
perPage: perPage,
|
||||||
sortby: sort,
|
sortby: sort,
|
||||||
filterConcat: taskFilterConcatinator(tf.FilterConcat),
|
filterConcat: taskFilterConcatinator(tf.FilterConcat),
|
||||||
|
filterIncludeNulls: tf.FilterIncludeNulls,
|
||||||
}
|
}
|
||||||
|
|
||||||
taskopts.filters, err = getTaskFiltersByCollections(tf)
|
taskopts.filters, err = getTaskFiltersByCollections(tf)
|
||||||
|
|
|
@ -18,9 +18,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/config"
|
||||||
|
"fmt"
|
||||||
"github.com/iancoleman/strcase"
|
"github.com/iancoleman/strcase"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
type taskFilterComparator string
|
type taskFilterComparator string
|
||||||
|
@ -85,19 +89,13 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
|
||||||
if len(c.FilterValue) > i {
|
if len(c.FilterValue) > i {
|
||||||
filter.value, err = getNativeValueForTaskField(filter.field, c.FilterValue[i])
|
filter.value, err = getNativeValueForTaskField(filter.field, c.FilterValue[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, ErrInvalidTaskFilterValue{
|
||||||
|
Value: filter.field,
|
||||||
|
Field: c.FilterValue[i],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case for pseudo date fields
|
|
||||||
// FIXME: This is really dirty, to fix this properly the db fields should be renamed
|
|
||||||
if filter.field+"_unix" == taskPropertyDoneAtUnix ||
|
|
||||||
filter.field+"_unix" == taskPropertyDueDateUnix ||
|
|
||||||
filter.field+"_unix" == taskPropertyStartDateUnix ||
|
|
||||||
filter.field+"_unix" == taskPropertyEndDateUnix {
|
|
||||||
filter.field += "_unix"
|
|
||||||
}
|
|
||||||
|
|
||||||
filters = append(filters, filter)
|
filters = append(filters, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +151,13 @@ func getNativeValueForTaskField(fieldName, value string) (nativeValue interface{
|
||||||
nativeValue = value
|
nativeValue = value
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
nativeValue, err = strconv.ParseBool(value)
|
nativeValue, err = strconv.ParseBool(value)
|
||||||
|
case reflect.Struct:
|
||||||
|
if field.Type == schemas.TimeType {
|
||||||
|
nativeValue, err = time.Parse(time.RFC3339, value)
|
||||||
|
nativeValue = nativeValue.(time.Time).In(config.GetTimeZone())
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
|
panic(fmt.Errorf("unrecognized filter type %s for field %s, value %s", field.Type.String(), fieldName, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -26,24 +26,24 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
taskPropertyID string = "id"
|
taskPropertyID string = "id"
|
||||||
taskPropertyTitle string = "title"
|
taskPropertyTitle string = "title"
|
||||||
taskPropertyDescription string = "description"
|
taskPropertyDescription string = "description"
|
||||||
taskPropertyDone string = "done"
|
taskPropertyDone string = "done"
|
||||||
taskPropertyDoneAtUnix string = "done_at_unix"
|
taskPropertyDoneAt string = "done_at"
|
||||||
taskPropertyDueDateUnix string = "due_date_unix"
|
taskPropertyDueDate string = "due_date"
|
||||||
taskPropertyCreatedByID string = "created_by_id"
|
taskPropertyCreatedByID string = "created_by_id"
|
||||||
taskPropertyListID string = "list_id"
|
taskPropertyListID string = "list_id"
|
||||||
taskPropertyRepeatAfter string = "repeat_after"
|
taskPropertyRepeatAfter string = "repeat_after"
|
||||||
taskPropertyPriority string = "priority"
|
taskPropertyPriority string = "priority"
|
||||||
taskPropertyStartDateUnix string = "start_date_unix"
|
taskPropertyStartDate string = "start_date"
|
||||||
taskPropertyEndDateUnix string = "end_date_unix"
|
taskPropertyEndDate string = "end_date"
|
||||||
taskPropertyHexColor string = "hex_color"
|
taskPropertyHexColor string = "hex_color"
|
||||||
taskPropertyPercentDone string = "percent_done"
|
taskPropertyPercentDone string = "percent_done"
|
||||||
taskPropertyUID string = "uid"
|
taskPropertyUID string = "uid"
|
||||||
taskPropertyCreated string = "created"
|
taskPropertyCreated string = "created"
|
||||||
taskPropertyUpdated string = "updated"
|
taskPropertyUpdated string = "updated"
|
||||||
taskPropertyPosition string = "position"
|
taskPropertyPosition string = "position"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -46,14 +46,14 @@ func TestSortParamValidation(t *testing.T) {
|
||||||
taskPropertyTitle,
|
taskPropertyTitle,
|
||||||
taskPropertyDescription,
|
taskPropertyDescription,
|
||||||
taskPropertyDone,
|
taskPropertyDone,
|
||||||
taskPropertyDoneAtUnix,
|
taskPropertyDoneAt,
|
||||||
taskPropertyDueDateUnix,
|
taskPropertyDueDate,
|
||||||
taskPropertyCreatedByID,
|
taskPropertyCreatedByID,
|
||||||
taskPropertyListID,
|
taskPropertyListID,
|
||||||
taskPropertyRepeatAfter,
|
taskPropertyRepeatAfter,
|
||||||
taskPropertyPriority,
|
taskPropertyPriority,
|
||||||
taskPropertyStartDateUnix,
|
taskPropertyStartDate,
|
||||||
taskPropertyEndDateUnix,
|
taskPropertyEndDate,
|
||||||
taskPropertyHexColor,
|
taskPropertyHexColor,
|
||||||
taskPropertyPercentDone,
|
taskPropertyPercentDone,
|
||||||
taskPropertyUID,
|
taskPropertyUID,
|
||||||
|
|
|
@ -17,13 +17,14 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTaskCollection_ReadAll(t *testing.T) {
|
func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
|
@ -33,19 +34,27 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
user2 := &user.User{
|
user2 := &user.User{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
user6 := &user.User{
|
user6 := &user.User{
|
||||||
ID: 6,
|
ID: 6,
|
||||||
Username: "user6",
|
Username: "user6",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loc := config.GetTimeZone()
|
||||||
|
|
||||||
// We use individual variables for the tasks here to be able to rearrange or remove ones more easily
|
// We use individual variables for the tasks here to be able to rearrange or remove ones more easily
|
||||||
task1 := &Task{
|
task1 := &Task{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
|
@ -63,8 +72,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Title: "Label #4 - visible via other task",
|
Title: "Label #4 - visible via other task",
|
||||||
CreatedByID: 2,
|
CreatedByID: 2,
|
||||||
CreatedBy: user2,
|
CreatedBy: user2,
|
||||||
Updated: 0,
|
Created: testCreatedTime,
|
||||||
Created: 0,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RelatedTasks: map[RelationKind][]*Task{
|
RelatedTasks: map[RelationKind][]*Task{
|
||||||
|
@ -76,8 +85,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -88,11 +97,12 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
FileID: 1,
|
FileID: 1,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
File: &files.File{
|
File: &files.File{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Size: 100,
|
Size: 100,
|
||||||
CreatedUnix: 1570998791,
|
Created: time.Unix(1570998791, 0).In(loc),
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -102,10 +112,11 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
FileID: 9999,
|
FileID: 9999,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
|
Created: testCreatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task2 := &Task{
|
task2 := &Task{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
|
@ -123,13 +134,13 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Title: "Label #4 - visible via other task",
|
Title: "Label #4 - visible via other task",
|
||||||
CreatedByID: 2,
|
CreatedByID: 2,
|
||||||
CreatedBy: user2,
|
CreatedBy: user2,
|
||||||
Updated: 0,
|
Created: testCreatedTime,
|
||||||
Created: 0,
|
Updated: testUpdatedTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task3 := &Task{
|
task3 := &Task{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
|
@ -140,8 +151,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
Priority: 100,
|
Priority: 100,
|
||||||
BucketID: 2,
|
BucketID: 2,
|
||||||
}
|
}
|
||||||
|
@ -154,8 +165,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
BucketID: 2,
|
BucketID: 2,
|
||||||
}
|
}
|
||||||
|
@ -168,9 +179,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
DueDate: 1543636724,
|
DueDate: time.Unix(1543636724, 0).In(loc),
|
||||||
BucketID: 2,
|
BucketID: 2,
|
||||||
}
|
}
|
||||||
task6 := &Task{
|
task6 := &Task{
|
||||||
|
@ -182,9 +193,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
DueDate: 1543616724,
|
DueDate: time.Unix(1543616724, 0).In(loc),
|
||||||
BucketID: 3,
|
BucketID: 3,
|
||||||
}
|
}
|
||||||
task7 := &Task{
|
task7 := &Task{
|
||||||
|
@ -196,9 +207,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
StartDate: 1544600000,
|
StartDate: time.Unix(1544600000, 0).In(loc),
|
||||||
BucketID: 3,
|
BucketID: 3,
|
||||||
}
|
}
|
||||||
task8 := &Task{
|
task8 := &Task{
|
||||||
|
@ -210,9 +221,9 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
EndDate: 1544700000,
|
EndDate: time.Unix(1544700000, 0).In(loc),
|
||||||
BucketID: 3,
|
BucketID: 3,
|
||||||
}
|
}
|
||||||
task9 := &Task{
|
task9 := &Task{
|
||||||
|
@ -225,10 +236,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
StartDate: 1544600000,
|
StartDate: time.Unix(1544600000, 0).In(loc),
|
||||||
EndDate: 1544700000,
|
EndDate: time.Unix(1544700000, 0).In(loc),
|
||||||
}
|
}
|
||||||
task10 := &Task{
|
task10 := &Task{
|
||||||
ID: 10,
|
ID: 10,
|
||||||
|
@ -240,8 +251,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task11 := &Task{
|
task11 := &Task{
|
||||||
ID: 11,
|
ID: 11,
|
||||||
|
@ -253,8 +264,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task12 := &Task{
|
task12 := &Task{
|
||||||
ID: 12,
|
ID: 12,
|
||||||
|
@ -266,8 +277,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task15 := &Task{
|
task15 := &Task{
|
||||||
ID: 15,
|
ID: 15,
|
||||||
|
@ -279,8 +290,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 6,
|
ListID: 6,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 6,
|
BucketID: 6,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task16 := &Task{
|
task16 := &Task{
|
||||||
ID: 16,
|
ID: 16,
|
||||||
|
@ -292,8 +303,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 7,
|
ListID: 7,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 7,
|
BucketID: 7,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task17 := &Task{
|
task17 := &Task{
|
||||||
ID: 17,
|
ID: 17,
|
||||||
|
@ -305,8 +316,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 8,
|
ListID: 8,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 8,
|
BucketID: 8,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task18 := &Task{
|
task18 := &Task{
|
||||||
ID: 18,
|
ID: 18,
|
||||||
|
@ -318,8 +329,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 9,
|
ListID: 9,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 9,
|
BucketID: 9,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task19 := &Task{
|
task19 := &Task{
|
||||||
ID: 19,
|
ID: 19,
|
||||||
|
@ -331,8 +342,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 10,
|
ListID: 10,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 10,
|
BucketID: 10,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task20 := &Task{
|
task20 := &Task{
|
||||||
ID: 20,
|
ID: 20,
|
||||||
|
@ -344,8 +355,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 11,
|
ListID: 11,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 11,
|
BucketID: 11,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task21 := &Task{
|
task21 := &Task{
|
||||||
ID: 21,
|
ID: 21,
|
||||||
|
@ -357,8 +368,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 12,
|
ListID: 12,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 12,
|
BucketID: 12,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task22 := &Task{
|
task22 := &Task{
|
||||||
ID: 22,
|
ID: 22,
|
||||||
|
@ -370,8 +381,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 13,
|
ListID: 13,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 13,
|
BucketID: 13,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task23 := &Task{
|
task23 := &Task{
|
||||||
ID: 23,
|
ID: 23,
|
||||||
|
@ -383,8 +394,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 14,
|
ListID: 14,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 14,
|
BucketID: 14,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task24 := &Task{
|
task24 := &Task{
|
||||||
ID: 24,
|
ID: 24,
|
||||||
|
@ -396,8 +407,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 15,
|
ListID: 15,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 15,
|
BucketID: 15,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task25 := &Task{
|
task25 := &Task{
|
||||||
ID: 25,
|
ID: 25,
|
||||||
|
@ -409,8 +420,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 16,
|
ListID: 16,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 16,
|
BucketID: 16,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task26 := &Task{
|
task26 := &Task{
|
||||||
ID: 26,
|
ID: 26,
|
||||||
|
@ -422,22 +433,25 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 17,
|
ListID: 17,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 17,
|
BucketID: 17,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task27 := &Task{
|
task27 := &Task{
|
||||||
ID: 27,
|
ID: 27,
|
||||||
Title: "task #27 with reminders",
|
Title: "task #27 with reminders",
|
||||||
Identifier: "test1-12",
|
Identifier: "test1-12",
|
||||||
Index: 12,
|
Index: 12,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
Reminders: []timeutil.TimeStamp{1543626724, 1543626824},
|
Reminders: []time.Time{
|
||||||
|
time.Unix(1543626724, 0).In(loc),
|
||||||
|
time.Unix(1543626824, 0).In(loc),
|
||||||
|
},
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task28 := &Task{
|
task28 := &Task{
|
||||||
ID: 28,
|
ID: 28,
|
||||||
|
@ -450,8 +464,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
RepeatAfter: 3600,
|
RepeatAfter: 3600,
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task29 := &Task{
|
task29 := &Task{
|
||||||
ID: 29,
|
ID: 29,
|
||||||
|
@ -470,15 +484,15 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
Index: 1,
|
Index: 1,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task30 := &Task{
|
task30 := &Task{
|
||||||
ID: 30,
|
ID: 30,
|
||||||
|
@ -494,8 +508,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
},
|
},
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task31 := &Task{
|
task31 := &Task{
|
||||||
ID: 31,
|
ID: 31,
|
||||||
|
@ -508,8 +522,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 1,
|
ListID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task32 := &Task{
|
task32 := &Task{
|
||||||
ID: 32,
|
ID: 32,
|
||||||
|
@ -521,8 +535,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
ListID: 3,
|
ListID: 3,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 21,
|
BucketID: 21,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
task33 := &Task{
|
task33 := &Task{
|
||||||
ID: 33,
|
ID: 33,
|
||||||
|
@ -535,8 +549,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
PercentDone: 0.5,
|
PercentDone: 0.5,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
Created: 1543626724,
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: 1543626724,
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
|
@ -545,9 +559,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
SortBy []string // Is a string, since this is the place where a query string comes from the user
|
SortBy []string // Is a string, since this is the place where a query string comes from the user
|
||||||
OrderBy []string
|
OrderBy []string
|
||||||
|
|
||||||
FilterBy []string
|
FilterBy []string
|
||||||
FilterValue []string
|
FilterValue []string
|
||||||
FilterComparator []string
|
FilterComparator []string
|
||||||
|
FilterIncludeNulls bool
|
||||||
|
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
|
@ -658,7 +673,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
name: "ReadAll Tasks with range",
|
name: "ReadAll Tasks with range",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterBy: []string{"start_date", "end_date"},
|
FilterBy: []string{"start_date", "end_date"},
|
||||||
FilterValue: []string{"1544500000", "1544700001"},
|
FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"},
|
||||||
FilterComparator: []string{"greater", "less"},
|
FilterComparator: []string{"greater", "less"},
|
||||||
},
|
},
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
@ -673,7 +688,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
name: "ReadAll Tasks with different range",
|
name: "ReadAll Tasks with different range",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterBy: []string{"start_date", "end_date"},
|
FilterBy: []string{"start_date", "end_date"},
|
||||||
FilterValue: []string{"1544700000", "1545000000"},
|
FilterValue: []string{"2018-12-13T11:20:00+00:00", "2018-12-16T22:40:00+00:00"},
|
||||||
FilterComparator: []string{"greater", "less"},
|
FilterComparator: []string{"greater", "less"},
|
||||||
},
|
},
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
@ -687,7 +702,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
name: "ReadAll Tasks with range with start date only",
|
name: "ReadAll Tasks with range with start date only",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterBy: []string{"start_date"},
|
FilterBy: []string{"start_date"},
|
||||||
FilterValue: []string{"1544600000"},
|
FilterValue: []string{"2018-12-12T07:33:20+00:00"},
|
||||||
FilterComparator: []string{"greater"},
|
FilterComparator: []string{"greater"},
|
||||||
},
|
},
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
@ -698,7 +713,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
name: "ReadAll Tasks with range with start date only and greater equals",
|
name: "ReadAll Tasks with range with start date only and greater equals",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterBy: []string{"start_date"},
|
FilterBy: []string{"start_date"},
|
||||||
FilterValue: []string{"1544600000"},
|
FilterValue: []string{"2018-12-12T07:33:20+00:00"},
|
||||||
FilterComparator: []string{"greater_equals"},
|
FilterComparator: []string{"greater_equals"},
|
||||||
},
|
},
|
||||||
args: defaultArgs,
|
args: defaultArgs,
|
||||||
|
@ -777,6 +792,50 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "range with nulls",
|
||||||
|
fields: fields{
|
||||||
|
FilterBy: []string{"start_date", "end_date"},
|
||||||
|
FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"},
|
||||||
|
FilterComparator: []string{"greater", "less"},
|
||||||
|
FilterIncludeNulls: true,
|
||||||
|
},
|
||||||
|
args: defaultArgs,
|
||||||
|
want: []*Task{
|
||||||
|
task1, // has nil dates
|
||||||
|
task2, // has nil dates
|
||||||
|
task3, // has nil dates
|
||||||
|
task4, // has nil dates
|
||||||
|
task5, // has nil dates
|
||||||
|
task6, // has nil dates
|
||||||
|
task7,
|
||||||
|
task8,
|
||||||
|
task9,
|
||||||
|
task10, // has nil dates
|
||||||
|
task11, // has nil dates
|
||||||
|
task12, // has nil dates
|
||||||
|
task15, // has nil dates
|
||||||
|
task16, // has nil dates
|
||||||
|
task17, // has nil dates
|
||||||
|
task18, // has nil dates
|
||||||
|
task19, // has nil dates
|
||||||
|
task20, // has nil dates
|
||||||
|
task21, // has nil dates
|
||||||
|
task22, // has nil dates
|
||||||
|
task23, // has nil dates
|
||||||
|
task24, // has nil dates
|
||||||
|
task25, // has nil dates
|
||||||
|
task26, // has nil dates
|
||||||
|
task27, // has nil dates
|
||||||
|
task28, // has nil dates
|
||||||
|
task29, // has nil dates
|
||||||
|
task30, // has nil dates
|
||||||
|
task31, // has nil dates
|
||||||
|
task32, // has nil dates
|
||||||
|
task33, // has nil dates
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -788,9 +847,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
||||||
SortBy: tt.fields.SortBy,
|
SortBy: tt.fields.SortBy,
|
||||||
OrderBy: tt.fields.OrderBy,
|
OrderBy: tt.fields.OrderBy,
|
||||||
|
|
||||||
FilterBy: tt.fields.FilterBy,
|
FilterBy: tt.fields.FilterBy,
|
||||||
FilterValue: tt.fields.FilterValue,
|
FilterValue: tt.fields.FilterValue,
|
||||||
FilterComparator: tt.fields.FilterComparator,
|
FilterComparator: tt.fields.FilterComparator,
|
||||||
|
FilterIncludeNulls: tt.fields.FilterIncludeNulls,
|
||||||
|
|
||||||
CRUDable: tt.fields.CRUDable,
|
CRUDable: tt.fields.CRUDable,
|
||||||
Rights: tt.fields.Rights,
|
Rights: tt.fields.Rights,
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskComment represents a task comment
|
// TaskComment represents a task comment
|
||||||
|
@ -31,8 +31,8 @@ type TaskComment struct {
|
||||||
Author *user.User `xorm:"-" json:"author"`
|
Author *user.User `xorm:"-" json:"author"`
|
||||||
TaskID int64 `xorm:"not null" json:"-" param:"task"`
|
TaskID int64 `xorm:"not null" json:"-" param:"task"`
|
||||||
|
|
||||||
Created timeutil.TimeStamp `xorm:"created" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated" json:"updated"`
|
Updated time.Time `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RelationKind represents a kind of relation between to tasks
|
// RelationKind represents a kind of relation between to tasks
|
||||||
|
@ -88,7 +88,7 @@ type TaskRelation struct {
|
||||||
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
CreatedBy *user.User `xorm:"-" json:"created_by"`
|
||||||
|
|
||||||
// A timestamp when this label was created. You cannot change this value.
|
// A timestamp when this label was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
@ -44,12 +43,12 @@ type Task struct {
|
||||||
// Whether a task is done or not.
|
// Whether a task is done or not.
|
||||||
Done bool `xorm:"INDEX null" json:"done"`
|
Done bool `xorm:"INDEX null" json:"done"`
|
||||||
// The time when a task was marked as done.
|
// The time when a task was marked as done.
|
||||||
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"done_at"`
|
DoneAt time.Time `xorm:"INDEX null 'done_at'" json:"done_at"`
|
||||||
// The time when the task is due.
|
// The time when the task is due.
|
||||||
DueDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'due_date_unix'" json:"due_date"`
|
DueDate time.Time `xorm:"DATETIME INDEX null 'due_date'" json:"due_date"`
|
||||||
// An array of datetimes when the user wants to be reminded of the task.
|
// An array of datetimes when the user wants to be reminded of the task.
|
||||||
Reminders []timeutil.TimeStamp `xorm:"-" json:"reminder_dates"`
|
Reminders []time.Time `xorm:"-" json:"reminder_dates"`
|
||||||
CreatedByID int64 `xorm:"int(11) not null" json:"-"` // ID of the user who put that task on the list
|
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.
|
// 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:"list_id" 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.
|
// 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.
|
||||||
|
@ -59,9 +58,9 @@ type Task struct {
|
||||||
// The task priority. Can be anything you want, it is possible to sort by this later.
|
// The task priority. Can be anything you want, it is possible to sort by this later.
|
||||||
Priority int64 `xorm:"int(11) null" json:"priority"`
|
Priority int64 `xorm:"int(11) null" json:"priority"`
|
||||||
// When this task starts.
|
// When this task starts.
|
||||||
StartDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'start_date_unix'" json:"start_date" query:"-"`
|
StartDate time.Time `xorm:"DATETIME INDEX null 'start_date'" json:"start_date" query:"-"`
|
||||||
// When this task ends.
|
// When this task ends.
|
||||||
EndDate timeutil.TimeStamp `xorm:"int(11) INDEX null 'end_date_unix'" json:"end_date" query:"-"`
|
EndDate time.Time `xorm:"DATETIME INDEX null 'end_date'" json:"end_date" query:"-"`
|
||||||
// An array of users who are assigned to this task
|
// An array of users who are assigned to this task
|
||||||
Assignees []*user.User `xorm:"-" json:"assignees"`
|
Assignees []*user.User `xorm:"-" json:"assignees"`
|
||||||
// An array of labels which are associated with this task.
|
// An array of labels which are associated with this task.
|
||||||
|
@ -86,9 +85,9 @@ type Task struct {
|
||||||
Attachments []*TaskAttachment `xorm:"-" json:"attachments"`
|
Attachments []*TaskAttachment `xorm:"-" json:"attachments"`
|
||||||
|
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this task was last updated. You cannot change this value.
|
// A timestamp when this task was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
// BucketID is the ID of the kanban bucket this task belongs to.
|
// BucketID is the ID of the kanban bucket this task belongs to.
|
||||||
BucketID int64 `xorm:"int(11) null" json:"bucket_id"`
|
BucketID int64 `xorm:"int(11) null" json:"bucket_id"`
|
||||||
|
@ -115,10 +114,10 @@ func (Task) TableName() string {
|
||||||
|
|
||||||
// TaskReminder holds a reminder on a task
|
// TaskReminder holds a reminder on a task
|
||||||
type TaskReminder struct {
|
type TaskReminder struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk"`
|
||||||
TaskID int64 `xorm:"int(11) not null INDEX"`
|
TaskID int64 `xorm:"int(11) not null INDEX"`
|
||||||
Reminder timeutil.TimeStamp `xorm:"int(11) not null INDEX 'reminder_unix'"`
|
Reminder time.Time `xorm:"DATETIME not null INDEX 'reminder'"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null"`
|
Created time.Time `xorm:"created not null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns a pretty table name
|
// TableName returns a pretty table name
|
||||||
|
@ -134,12 +133,13 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type taskOptions struct {
|
type taskOptions struct {
|
||||||
search string
|
search string
|
||||||
page int
|
page int
|
||||||
perPage int
|
perPage int
|
||||||
sortby []*sortParam
|
sortby []*sortParam
|
||||||
filters []*taskFilter
|
filters []*taskFilter
|
||||||
filterConcat taskFilterConcatinator
|
filterConcat taskFilterConcatinator
|
||||||
|
filterIncludeNulls bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAll is a dummy function to still have that endpoint documented
|
// ReadAll is a dummy function to still have that endpoint documented
|
||||||
|
@ -151,7 +151,7 @@ type taskOptions struct {
|
||||||
// @Param page query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
// @Param page query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||||
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
|
// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page."
|
||||||
// @Param s query string false "Search tasks by task text."
|
// @Param s query string false "Search tasks by task text."
|
||||||
// @Param sort_by query string false "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with `order_by`. Possible values to sort by are `id`, `text`, `description`, `done`, `done_at_unix`, `due_date_unix`, `created_by_id`, `list_id`, `repeat_after`, `priority`, `start_date_unix`, `end_date_unix`, `hex_color`, `percent_done`, `uid`, `created`, `updated`. Default is `id`."
|
// @Param sort_by query string false "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with `order_by`. Possible values to sort by are `id`, `text`, `description`, `done`, `done_at`, `due_date`, `created_by_id`, `list_id`, `repeat_after`, `priority`, `start_date`, `end_date`, `hex_color`, `percent_done`, `uid`, `created`, `updated`. Default is `id`."
|
||||||
// @Param order_by query string false "The ordering parameter. Possible values to order by are `asc` or `desc`. Default is `asc`."
|
// @Param order_by query string false "The ordering parameter. Possible values to order by are `asc` or `desc`. Default is `asc`."
|
||||||
// @Param filter_by query string false "The name of the field to filter by. Accepts an array for multiple filters which will be chanied together, all supplied filter must match."
|
// @Param filter_by query string false "The name of the field to filter by. Accepts an array for multiple filters which will be chanied together, all supplied filter must match."
|
||||||
// @Param filter_value query string false "The value to filter for."
|
// @Param filter_value query string false "The value to filter for."
|
||||||
|
@ -228,13 +228,29 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
|
||||||
case taskFilterComparatorNotEquals:
|
case taskFilterComparatorNotEquals:
|
||||||
filters = append(filters, &builder.Neq{f.field: f.value})
|
filters = append(filters, &builder.Neq{f.field: f.value})
|
||||||
case taskFilterComparatorGreater:
|
case taskFilterComparatorGreater:
|
||||||
filters = append(filters, builder.Or(&builder.Gt{f.field: f.value}, &builder.Eq{f.field: 0}))
|
if opts.filterIncludeNulls {
|
||||||
|
filters = append(filters, builder.Or(&builder.Gt{f.field: f.value}, &builder.IsNull{f.field}))
|
||||||
|
} else {
|
||||||
|
filters = append(filters, &builder.Gt{f.field: f.value})
|
||||||
|
}
|
||||||
case taskFilterComparatorGreateEquals:
|
case taskFilterComparatorGreateEquals:
|
||||||
filters = append(filters, builder.Or(&builder.Gte{f.field: f.value}, &builder.Eq{f.field: 0}))
|
if opts.filterIncludeNulls {
|
||||||
|
filters = append(filters, builder.Or(&builder.Gte{f.field: f.value}, &builder.IsNull{f.field}))
|
||||||
|
} else {
|
||||||
|
filters = append(filters, &builder.Gte{f.field: f.value})
|
||||||
|
}
|
||||||
case taskFilterComparatorLess:
|
case taskFilterComparatorLess:
|
||||||
filters = append(filters, builder.Or(&builder.Lt{f.field: f.value}, &builder.Eq{f.field: 0}))
|
if opts.filterIncludeNulls {
|
||||||
|
filters = append(filters, builder.Or(&builder.Lt{f.field: f.value}, &builder.IsNull{f.field}))
|
||||||
|
} else {
|
||||||
|
filters = append(filters, &builder.Lt{f.field: f.value})
|
||||||
|
}
|
||||||
case taskFilterComparatorLessEquals:
|
case taskFilterComparatorLessEquals:
|
||||||
filters = append(filters, builder.Or(&builder.Lte{f.field: f.value}, &builder.Eq{f.field: 0}))
|
if opts.filterIncludeNulls {
|
||||||
|
filters = append(filters, builder.Or(&builder.Lte{f.field: f.value}, &builder.IsNull{f.field}))
|
||||||
|
} else {
|
||||||
|
filters = append(filters, &builder.Lte{f.field: f.value})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,9 +487,9 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
taskRemindersUnix := make(map[int64][]timeutil.TimeStamp)
|
taskReminders := make(map[int64][]time.Time)
|
||||||
for _, r := range reminders {
|
for _, r := range reminders {
|
||||||
taskRemindersUnix[r.TaskID] = append(taskRemindersUnix[r.TaskID], r.Reminder)
|
taskReminders[r.TaskID] = append(taskReminders[r.TaskID], r.Reminder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all identifiers
|
// Get all identifiers
|
||||||
|
@ -490,7 +506,7 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
||||||
task.CreatedBy = users[task.CreatedByID]
|
task.CreatedBy = users[task.CreatedByID]
|
||||||
|
|
||||||
// Add the reminders
|
// Add the reminders
|
||||||
task.Reminders = taskRemindersUnix[task.ID]
|
task.Reminders = taskReminders[task.ID]
|
||||||
|
|
||||||
// Prepare the subtasks
|
// Prepare the subtasks
|
||||||
task.RelatedTasks = make(RelatedTaskMap)
|
task.RelatedTasks = make(RelatedTaskMap)
|
||||||
|
@ -663,7 +679,7 @@ func (t *Task) Update() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ot.Reminders = make([]timeutil.TimeStamp, len(reminders))
|
ot.Reminders = make([]time.Time, len(reminders))
|
||||||
for i, r := range reminders {
|
for i, r := range reminders {
|
||||||
ot.Reminders[i] = r.Reminder
|
ot.Reminders[i] = r.Reminder
|
||||||
}
|
}
|
||||||
|
@ -737,20 +753,20 @@ func (t *Task) Update() (err error) {
|
||||||
ot.Description = ""
|
ot.Description = ""
|
||||||
}
|
}
|
||||||
// Due date
|
// Due date
|
||||||
if t.DueDate == 0 {
|
if t.DueDate.IsZero() {
|
||||||
ot.DueDate = 0
|
ot.DueDate = time.Time{}
|
||||||
}
|
}
|
||||||
// Repeat after
|
// Repeat after
|
||||||
if t.RepeatAfter == 0 {
|
if t.RepeatAfter == 0 {
|
||||||
ot.RepeatAfter = 0
|
ot.RepeatAfter = 0
|
||||||
}
|
}
|
||||||
// Start date
|
// Start date
|
||||||
if t.StartDate == 0 {
|
if t.StartDate.IsZero() {
|
||||||
ot.StartDate = 0
|
ot.StartDate = time.Time{}
|
||||||
}
|
}
|
||||||
// End date
|
// End date
|
||||||
if t.EndDate == 0 {
|
if t.EndDate.IsZero() {
|
||||||
ot.EndDate = 0
|
ot.EndDate = time.Time{}
|
||||||
}
|
}
|
||||||
// Color
|
// Color
|
||||||
if t.HexColor == "" {
|
if t.HexColor == "" {
|
||||||
|
@ -773,13 +789,13 @@ func (t *Task) Update() (err error) {
|
||||||
Cols("title",
|
Cols("title",
|
||||||
"description",
|
"description",
|
||||||
"done",
|
"done",
|
||||||
"due_date_unix",
|
"due_date",
|
||||||
"repeat_after",
|
"repeat_after",
|
||||||
"priority",
|
"priority",
|
||||||
"start_date_unix",
|
"start_date",
|
||||||
"end_date_unix",
|
"end_date",
|
||||||
"hex_color",
|
"hex_color",
|
||||||
"done_at_unix",
|
"done_at",
|
||||||
"percent_done",
|
"percent_done",
|
||||||
"list_id",
|
"list_id",
|
||||||
"bucket_id",
|
"bucket_id",
|
||||||
|
@ -796,7 +812,7 @@ func (t *Task) Update() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// This helper function updates the reminders, doneAtUnix, start and end dates of the *old* task
|
// This helper function updates the reminders, doneAt, start and end dates of the *old* task
|
||||||
// and saves the new values in the newTask object.
|
// and saves the new values in the newTask object.
|
||||||
// We make a few assumtions here:
|
// We make a few assumtions here:
|
||||||
// 1. Everything in oldTask is the truth - we figure out if we update anything at all if oldTask.RepeatAfter has a value > 0
|
// 1. Everything in oldTask is the truth - we figure out if we update anything at all if oldTask.RepeatAfter has a value > 0
|
||||||
|
@ -810,16 +826,16 @@ func updateDone(oldTask *Task, newTask *Task) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// assuming we'll merge the new task over the old task
|
// assuming we'll merge the new task over the old task
|
||||||
if oldTask.DueDate > 0 {
|
if !oldTask.DueDate.IsZero() {
|
||||||
if oldTask.RepeatFromCurrentDate {
|
if oldTask.RepeatFromCurrentDate {
|
||||||
newTask.DueDate = timeutil.FromTime(now.Add(repeatDuration))
|
newTask.DueDate = now.Add(repeatDuration)
|
||||||
} else {
|
} else {
|
||||||
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
// Always add one instance of the repeating interval to catch cases where a due date is already in the future
|
||||||
// but not the repeating interval
|
// but not the repeating interval
|
||||||
newTask.DueDate = timeutil.FromTime(oldTask.DueDate.ToTime().Add(repeatDuration))
|
newTask.DueDate = oldTask.DueDate.Add(repeatDuration)
|
||||||
// Add the repeating interval until the new due date is in the future
|
// Add the repeating interval until the new due date is in the future
|
||||||
for !newTask.DueDate.ToTime().After(now) {
|
for !newTask.DueDate.After(now) {
|
||||||
newTask.DueDate = timeutil.FromTime(newTask.DueDate.ToTime().Add(repeatDuration))
|
newTask.DueDate = newTask.DueDate.Add(repeatDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -830,47 +846,47 @@ func updateDone(oldTask *Task, newTask *Task) {
|
||||||
if len(oldTask.Reminders) > 0 {
|
if len(oldTask.Reminders) > 0 {
|
||||||
if oldTask.RepeatFromCurrentDate {
|
if oldTask.RepeatFromCurrentDate {
|
||||||
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
||||||
return oldTask.Reminders[i] < oldTask.Reminders[j]
|
return oldTask.Reminders[i].Unix() < oldTask.Reminders[j].Unix()
|
||||||
})
|
})
|
||||||
first := oldTask.Reminders[0]
|
first := oldTask.Reminders[0]
|
||||||
for in, r := range oldTask.Reminders {
|
for in, r := range oldTask.Reminders {
|
||||||
diff := time.Duration(r-first) * time.Second
|
diff := r.Sub(first)
|
||||||
newTask.Reminders[in] = timeutil.FromTime(now.Add(repeatDuration + diff))
|
newTask.Reminders[in] = now.Add(repeatDuration + diff)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for in, r := range oldTask.Reminders {
|
for in, r := range oldTask.Reminders {
|
||||||
newTask.Reminders[in] = timeutil.FromTime(r.ToTime().Add(repeatDuration))
|
newTask.Reminders[in] = r.Add(repeatDuration)
|
||||||
for !newTask.Reminders[in].ToTime().After(now) {
|
for !newTask.Reminders[in].After(now) {
|
||||||
newTask.Reminders[in] = timeutil.FromTime(newTask.Reminders[in].ToTime().Add(repeatDuration))
|
newTask.Reminders[in] = newTask.Reminders[in].Add(repeatDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a task has a start and end date, the end date should keep the difference to the start date when setting them as new
|
// If a task has a start and end date, the end date should keep the difference to the start date when setting them as new
|
||||||
if oldTask.RepeatFromCurrentDate && oldTask.StartDate > 0 && oldTask.EndDate > 0 {
|
if oldTask.RepeatFromCurrentDate && !oldTask.StartDate.IsZero() && !oldTask.EndDate.IsZero() {
|
||||||
diff := time.Duration(oldTask.EndDate-oldTask.StartDate) * time.Second
|
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||||
newTask.StartDate = timeutil.FromTime(now.Add(repeatDuration))
|
newTask.StartDate = now.Add(repeatDuration)
|
||||||
newTask.EndDate = timeutil.FromTime(now.Add(repeatDuration + diff))
|
newTask.EndDate = now.Add(repeatDuration + diff)
|
||||||
} else {
|
} else {
|
||||||
if oldTask.StartDate > 0 {
|
if !oldTask.StartDate.IsZero() {
|
||||||
if oldTask.RepeatFromCurrentDate {
|
if oldTask.RepeatFromCurrentDate {
|
||||||
newTask.StartDate = timeutil.FromTime(now.Add(repeatDuration))
|
newTask.StartDate = now.Add(repeatDuration)
|
||||||
} else {
|
} else {
|
||||||
newTask.StartDate = timeutil.FromTime(oldTask.StartDate.ToTime().Add(repeatDuration))
|
newTask.StartDate = oldTask.StartDate.Add(repeatDuration)
|
||||||
for !newTask.StartDate.ToTime().After(now) {
|
for !newTask.StartDate.After(now) {
|
||||||
newTask.StartDate = timeutil.FromTime(newTask.StartDate.ToTime().Add(repeatDuration))
|
newTask.StartDate = newTask.StartDate.Add(repeatDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldTask.EndDate > 0 {
|
if !oldTask.EndDate.IsZero() {
|
||||||
if oldTask.RepeatFromCurrentDate {
|
if oldTask.RepeatFromCurrentDate {
|
||||||
newTask.EndDate = timeutil.FromTime(now.Add(repeatDuration))
|
newTask.EndDate = now.Add(repeatDuration)
|
||||||
} else {
|
} else {
|
||||||
newTask.EndDate = timeutil.FromTime(oldTask.EndDate.ToTime().Add(repeatDuration))
|
newTask.EndDate = oldTask.EndDate.Add(repeatDuration)
|
||||||
for !newTask.EndDate.ToTime().After(now) {
|
for !newTask.EndDate.After(now) {
|
||||||
newTask.EndDate = timeutil.FromTime(newTask.EndDate.ToTime().Add(repeatDuration))
|
newTask.EndDate = newTask.EndDate.Add(repeatDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,17 +897,17 @@ func updateDone(oldTask *Task, newTask *Task) {
|
||||||
|
|
||||||
// Update the "done at" timestamp
|
// Update the "done at" timestamp
|
||||||
if !oldTask.Done && newTask.Done {
|
if !oldTask.Done && newTask.Done {
|
||||||
newTask.DoneAt = timeutil.FromTime(time.Now())
|
newTask.DoneAt = time.Now()
|
||||||
}
|
}
|
||||||
// When unmarking a task as done, reset the timestamp
|
// When unmarking a task as done, reset the timestamp
|
||||||
if oldTask.Done && !newTask.Done {
|
if oldTask.Done && !newTask.Done {
|
||||||
newTask.DoneAt = 0
|
newTask.DoneAt = time.Time{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates or deletes all necessary reminders without unneded db operations.
|
// Creates or deletes all necessary reminders without unneded db operations.
|
||||||
// The parameter is a slice with unix dates which holds the new reminders.
|
// The parameter is a slice with unix dates which holds the new reminders.
|
||||||
func (t *Task) updateReminders(reminders []timeutil.TimeStamp) (err error) {
|
func (t *Task) updateReminders(reminders []time.Time) (err error) {
|
||||||
|
|
||||||
// Load the current reminders
|
// Load the current reminders
|
||||||
taskReminders, err := getRemindersForTasks([]int64{t.ID})
|
taskReminders, err := getRemindersForTasks([]int64{t.ID})
|
||||||
|
@ -899,7 +915,7 @@ func (t *Task) updateReminders(reminders []timeutil.TimeStamp) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Reminders = make([]timeutil.TimeStamp, 0, len(taskReminders))
|
t.Reminders = make([]time.Time, 0, len(taskReminders))
|
||||||
for _, reminder := range taskReminders {
|
for _, reminder := range taskReminders {
|
||||||
t.Reminders = append(t.Reminders, reminder.Reminder)
|
t.Reminders = append(t.Reminders, reminder.Reminder)
|
||||||
}
|
}
|
||||||
|
@ -918,15 +934,15 @@ func (t *Task) updateReminders(reminders []timeutil.TimeStamp) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a hashmap of the new reminders for easier comparison
|
// Make a hashmap of the new reminders for easier comparison
|
||||||
newReminders := make(map[timeutil.TimeStamp]*TaskReminder, len(reminders))
|
newReminders := make(map[time.Time]*TaskReminder, len(reminders))
|
||||||
for _, newReminder := range reminders {
|
for _, newReminder := range reminders {
|
||||||
newReminders[newReminder] = &TaskReminder{Reminder: newReminder}
|
newReminders[newReminder] = &TaskReminder{Reminder: newReminder}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get old reminders to delete
|
// Get old reminders to delete
|
||||||
var found bool
|
var found bool
|
||||||
var remindersToDelete []timeutil.TimeStamp
|
var remindersToDelete []time.Time
|
||||||
oldReminders := make(map[timeutil.TimeStamp]*TaskReminder, len(t.Reminders))
|
oldReminders := make(map[time.Time]*TaskReminder, len(t.Reminders))
|
||||||
for _, oldReminder := range t.Reminders {
|
for _, oldReminder := range t.Reminders {
|
||||||
found = false
|
found = false
|
||||||
// If a new reminder is already in the list with old reminders
|
// If a new reminder is already in the list with old reminders
|
||||||
|
@ -944,7 +960,7 @@ func (t *Task) updateReminders(reminders []timeutil.TimeStamp) (err error) {
|
||||||
|
|
||||||
// Delete all reminders not passed
|
// Delete all reminders not passed
|
||||||
if len(remindersToDelete) > 0 {
|
if len(remindersToDelete) > 0 {
|
||||||
_, err = x.In("reminder_unix", remindersToDelete).
|
_, err = x.In("reminder", remindersToDelete).
|
||||||
And("task_id = ?", t.ID).
|
And("task_id = ?", t.ID).
|
||||||
Delete(TaskReminder{})
|
Delete(TaskReminder{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,7 +18,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -131,54 +130,54 @@ func TestUpdateDone(t *testing.T) {
|
||||||
oldTask := &Task{Done: false}
|
oldTask := &Task{Done: false}
|
||||||
newTask := &Task{Done: true}
|
newTask := &Task{Done: true}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.NotEqual(t, timeutil.TimeStamp(0), newTask.DoneAt)
|
assert.NotEqual(t, time.Time{}, newTask.DoneAt)
|
||||||
})
|
})
|
||||||
t.Run("unmarking a task as done", func(t *testing.T) {
|
t.Run("unmarking a task as done", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
oldTask := &Task{Done: true}
|
oldTask := &Task{Done: true}
|
||||||
newTask := &Task{Done: false}
|
newTask := &Task{Done: false}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.Equal(t, timeutil.TimeStamp(0), newTask.DoneAt)
|
assert.Equal(t, time.Time{}, newTask.DoneAt)
|
||||||
})
|
})
|
||||||
t.Run("repeating interval", func(t *testing.T) {
|
t.Run("repeating interval", func(t *testing.T) {
|
||||||
t.Run("normal", func(t *testing.T) {
|
t.Run("normal", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: timeutil.TimeStamp(1550000000),
|
DueDate: time.Unix(1550000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
var expected int64 = 1550008600
|
var expected = time.Unix(1550008600, 0)
|
||||||
for expected < time.Now().Unix() {
|
for time.Since(expected) > 0 {
|
||||||
expected += oldTask.RepeatAfter
|
expected = expected.Add(time.Second * time.Duration(oldTask.RepeatAfter))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected), newTask.DueDate)
|
assert.Equal(t, expected, newTask.DueDate)
|
||||||
})
|
})
|
||||||
t.Run("don't update if due date is zero", func(t *testing.T) {
|
t.Run("don't update if due date is zero", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: timeutil.TimeStamp(0),
|
DueDate: time.Time{},
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: timeutil.TimeStamp(1543626724),
|
DueDate: time.Unix(1543626724, 0),
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
assert.Equal(t, timeutil.TimeStamp(1543626724), newTask.DueDate)
|
assert.Equal(t, time.Unix(1543626724, 0), newTask.DueDate)
|
||||||
})
|
})
|
||||||
t.Run("update reminders", func(t *testing.T) {
|
t.Run("update reminders", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
1550000000,
|
time.Unix(1550000000, 0),
|
||||||
1555000000,
|
time.Unix(1555000000, 0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
|
@ -186,67 +185,67 @@ func TestUpdateDone(t *testing.T) {
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
var expected1 int64 = 1550008600
|
var expected1 = time.Unix(1550008600, 0)
|
||||||
var expected2 int64 = 1555008600
|
var expected2 = time.Unix(1555008600, 0)
|
||||||
for expected1 < time.Now().Unix() {
|
for time.Since(expected1) > 0 {
|
||||||
expected1 += oldTask.RepeatAfter
|
expected1 = expected1.Add(time.Duration(oldTask.RepeatAfter) * time.Second)
|
||||||
}
|
}
|
||||||
for expected2 < time.Now().Unix() {
|
for time.Since(expected2) > 0 {
|
||||||
expected2 += oldTask.RepeatAfter
|
expected2 = expected2.Add(time.Duration(oldTask.RepeatAfter) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, 2)
|
assert.Len(t, newTask.Reminders, 2)
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected1), newTask.Reminders[0])
|
assert.Equal(t, expected1, newTask.Reminders[0])
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected2), newTask.Reminders[1])
|
assert.Equal(t, expected2, newTask.Reminders[1])
|
||||||
})
|
})
|
||||||
t.Run("update start date", func(t *testing.T) {
|
t.Run("update start date", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
StartDate: timeutil.TimeStamp(1550000000),
|
StartDate: time.Unix(1550000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
var expected int64 = 1550008600
|
var expected = time.Unix(1550008600, 0)
|
||||||
for expected < time.Now().Unix() {
|
for time.Since(expected) > 0 {
|
||||||
expected += oldTask.RepeatAfter
|
expected = expected.Add(time.Second * time.Duration(oldTask.RepeatAfter))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected), newTask.StartDate)
|
assert.Equal(t, expected, newTask.StartDate)
|
||||||
})
|
})
|
||||||
t.Run("update end date", func(t *testing.T) {
|
t.Run("update end date", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
EndDate: timeutil.TimeStamp(1550000000),
|
EndDate: time.Unix(1550000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
var expected int64 = 1550008600
|
var expected = time.Unix(1550008600, 0)
|
||||||
for expected < time.Now().Unix() {
|
for time.Since(expected) > 0 {
|
||||||
expected += oldTask.RepeatAfter
|
expected = expected.Add(time.Second * time.Duration(oldTask.RepeatAfter))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected), newTask.EndDate)
|
assert.Equal(t, expected, newTask.EndDate)
|
||||||
})
|
})
|
||||||
t.Run("ensure due date is repeated even if the original one is in the future", func(t *testing.T) {
|
t.Run("ensure due date is repeated even if the original one is in the future", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
DueDate: timeutil.FromTime(time.Now().Add(time.Hour)),
|
DueDate: time.Now().Add(time.Hour),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
expected := int64(oldTask.DueDate) + oldTask.RepeatAfter
|
expected := oldTask.DueDate.Add(time.Duration(oldTask.RepeatAfter) * time.Second)
|
||||||
assert.Equal(t, timeutil.TimeStamp(expected), newTask.DueDate)
|
assert.Equal(t, expected, newTask.DueDate)
|
||||||
})
|
})
|
||||||
t.Run("repeat from current date", func(t *testing.T) {
|
t.Run("repeat from current date", func(t *testing.T) {
|
||||||
t.Run("due date", func(t *testing.T) {
|
t.Run("due date", func(t *testing.T) {
|
||||||
|
@ -254,23 +253,24 @@ func TestUpdateDone(t *testing.T) {
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatFromCurrentDate: true,
|
RepeatFromCurrentDate: true,
|
||||||
DueDate: timeutil.TimeStamp(1550000000),
|
DueDate: time.Unix(1550000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.DueDate)
|
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
||||||
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.DueDate.Unix())
|
||||||
})
|
})
|
||||||
t.Run("reminders", func(t *testing.T) {
|
t.Run("reminders", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatFromCurrentDate: true,
|
RepeatFromCurrentDate: true,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
1550000000,
|
time.Unix(1550000000, 0),
|
||||||
1555000000,
|
time.Unix(1555000000, 0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
|
@ -278,57 +278,61 @@ func TestUpdateDone(t *testing.T) {
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
diff := time.Duration(oldTask.Reminders[1]-oldTask.Reminders[0]) * time.Second
|
diff := oldTask.Reminders[1].Sub(oldTask.Reminders[0])
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, 2)
|
assert.Len(t, newTask.Reminders, 2)
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.Reminders[0])
|
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.Reminders[1])
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[0].Unix())
|
||||||
|
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[1].Unix())
|
||||||
})
|
})
|
||||||
t.Run("start date", func(t *testing.T) {
|
t.Run("start date", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatFromCurrentDate: true,
|
RepeatFromCurrentDate: true,
|
||||||
StartDate: timeutil.TimeStamp(1550000000),
|
StartDate: time.Unix(1550000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.StartDate)
|
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
||||||
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
||||||
})
|
})
|
||||||
t.Run("end date", func(t *testing.T) {
|
t.Run("end date", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatFromCurrentDate: true,
|
RepeatFromCurrentDate: true,
|
||||||
EndDate: timeutil.TimeStamp(1560000000),
|
EndDate: time.Unix(1560000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.EndDate)
|
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
||||||
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
||||||
})
|
})
|
||||||
t.Run("start and end date", func(t *testing.T) {
|
t.Run("start and end date", func(t *testing.T) {
|
||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatFromCurrentDate: true,
|
RepeatFromCurrentDate: true,
|
||||||
StartDate: timeutil.TimeStamp(1550000000),
|
StartDate: time.Unix(1550000000, 0),
|
||||||
EndDate: timeutil.TimeStamp(1560000000),
|
EndDate: time.Unix(1560000000, 0),
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
diff := time.Duration(oldTask.EndDate-oldTask.StartDate) * time.Second
|
diff := oldTask.EndDate.Sub(oldTask.StartDate)
|
||||||
|
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.StartDate)
|
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
||||||
assert.Equal(t, timeutil.FromTime(time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second)), newTask.EndDate)
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.StartDate.Unix())
|
||||||
|
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.EndDate.Unix())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ type Team struct {
|
||||||
Members []*TeamUser `xorm:"-" json:"members"`
|
Members []*TeamUser `xorm:"-" json:"members"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created" json:"created"`
|
Created time.Time `xorm:"created" json:"created"`
|
||||||
// A timestamp when this relation was last updated. You cannot change this value.
|
// A timestamp when this relation was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated" json:"updated"`
|
Updated time.Time `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
@ -71,7 +71,7 @@ type TeamMember struct {
|
||||||
Admin bool `xorm:"null" json:"admin"`
|
Admin bool `xorm:"null" json:"admin"`
|
||||||
|
|
||||||
// A timestamp when this relation was created. You cannot change this value.
|
// A timestamp when this relation was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
|
|
||||||
web.CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
web.Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
|
|
|
@ -18,9 +18,9 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/db"
|
"code.vikunja.io/api/pkg/db"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
@ -33,8 +33,8 @@ func TestTeam_CanDoSomething(t *testing.T) {
|
||||||
CreatedByID int64
|
CreatedByID int64
|
||||||
CreatedBy *user.User
|
CreatedBy *user.User
|
||||||
Members []*TeamUser
|
Members []*TeamUser
|
||||||
Created timeutil.TimeStamp
|
Created time.Time
|
||||||
Updated timeutil.TimeStamp
|
Updated time.Time
|
||||||
CRUDable web.CRUDable
|
CRUDable web.CRUDable
|
||||||
Rights web.Rights
|
Rights web.Rights
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,17 +30,23 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser2 := &user.User{
|
testuser2 := &user.User{
|
||||||
ID: 2,
|
ID: 2,
|
||||||
Username: "user2",
|
Username: "user2",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser3 := &user.User{
|
testuser3 := &user.User{
|
||||||
ID: 3,
|
ID: 3,
|
||||||
Username: "user3",
|
Username: "user3",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
PasswordResetToken: "passwordresettesttoken",
|
PasswordResetToken: "passwordresettesttoken",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser4 := &user.User{
|
testuser4 := &user.User{
|
||||||
ID: 4,
|
ID: 4,
|
||||||
|
@ -48,6 +54,8 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: false,
|
IsActive: false,
|
||||||
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
|
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser5 := &user.User{
|
testuser5 := &user.User{
|
||||||
ID: 5,
|
ID: 5,
|
||||||
|
@ -55,54 +63,72 @@ func TestListUsersFromList(t *testing.T) {
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: false,
|
IsActive: false,
|
||||||
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
|
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser6 := &user.User{
|
testuser6 := &user.User{
|
||||||
ID: 6,
|
ID: 6,
|
||||||
Username: "user6",
|
Username: "user6",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser7 := &user.User{
|
testuser7 := &user.User{
|
||||||
ID: 7,
|
ID: 7,
|
||||||
Username: "user7",
|
Username: "user7",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser8 := &user.User{
|
testuser8 := &user.User{
|
||||||
ID: 8,
|
ID: 8,
|
||||||
Username: "user8",
|
Username: "user8",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser9 := &user.User{
|
testuser9 := &user.User{
|
||||||
ID: 9,
|
ID: 9,
|
||||||
Username: "user9",
|
Username: "user9",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser10 := &user.User{
|
testuser10 := &user.User{
|
||||||
ID: 10,
|
ID: 10,
|
||||||
Username: "user10",
|
Username: "user10",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser11 := &user.User{
|
testuser11 := &user.User{
|
||||||
ID: 11,
|
ID: 11,
|
||||||
Username: "user11",
|
Username: "user11",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser12 := &user.User{
|
testuser12 := &user.User{
|
||||||
ID: 12,
|
ID: 12,
|
||||||
Username: "user12",
|
Username: "user12",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
testuser13 := &user.User{
|
testuser13 := &user.User{
|
||||||
ID: 13,
|
ID: 13,
|
||||||
Username: "user13",
|
Username: "user13",
|
||||||
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
|
Created: testCreatedTime,
|
||||||
|
Updated: testUpdatedTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
|
|
|
@ -17,16 +17,16 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status represents this migration status
|
// Status represents this migration status
|
||||||
type Status struct {
|
type Status struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
UserID int64 `xorm:"int(11) not null" json:"-"`
|
UserID int64 `xorm:"int(11) not null" json:"-"`
|
||||||
MigratorName string `xorm:"varchar(255)" json:"migrator_name"`
|
MigratorName string `xorm:"varchar(255)" json:"migrator_name"`
|
||||||
Created timeutil.TimeStamp `xorm:"created not null 'created_unix'" json:"time_unix"`
|
Created time.Time `xorm:"created not null 'created'" json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName holds the table name for the migration status table
|
// TableName holds the table name for the migration status table
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/modules/migration"
|
"code.vikunja.io/api/pkg/modules/migration"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -267,14 +266,14 @@ func convertTodoistToVikunja(sync *sync) (fullVikunjaHierachie []*models.Namespa
|
||||||
for _, i := range sync.Items {
|
for _, i := range sync.Items {
|
||||||
task := &models.Task{
|
task := &models.Task{
|
||||||
Title: i.Content,
|
Title: i.Content,
|
||||||
Created: timeutil.FromTime(i.DateAdded),
|
Created: i.DateAdded.In(config.GetTimeZone()),
|
||||||
Done: i.Checked == 1,
|
Done: i.Checked == 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only try to parse the task done at date if the task is actually done
|
// Only try to parse the task done at date if the task is actually done
|
||||||
// Sometimes weired things happen if we try to parse nil dates.
|
// Sometimes weired things happen if we try to parse nil dates.
|
||||||
if task.Done {
|
if task.Done {
|
||||||
task.DoneAt = timeutil.FromTime(i.DateCompleted)
|
task.DoneAt = i.DateCompleted.In(config.GetTimeZone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todoist priorities only range from 1 (lowest) and max 4 (highest), so we need to make slight adjustments
|
// Todoist priorities only range from 1 (lowest) and max 4 (highest), so we need to make slight adjustments
|
||||||
|
@ -288,7 +287,7 @@ func convertTodoistToVikunja(sync *sync) (fullVikunjaHierachie []*models.Namespa
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
task.DueDate = timeutil.FromTime(dueDate)
|
task.DueDate = dueDate.In(config.GetTimeZone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put all labels together from earlier
|
// Put all labels together from earlier
|
||||||
|
@ -341,17 +340,16 @@ func convertTodoistToVikunja(sync *sync) (fullVikunjaHierachie []*models.Namespa
|
||||||
|
|
||||||
tasks[n.ItemID].Attachments = append(tasks[n.ItemID].Attachments, &models.TaskAttachment{
|
tasks[n.ItemID].Attachments = append(tasks[n.ItemID].Attachments, &models.TaskAttachment{
|
||||||
File: &files.File{
|
File: &files.File{
|
||||||
Name: n.FileAttachment.FileName,
|
Name: n.FileAttachment.FileName,
|
||||||
Mime: n.FileAttachment.FileType,
|
Mime: n.FileAttachment.FileType,
|
||||||
Size: uint64(n.FileAttachment.FileSize),
|
Size: uint64(n.FileAttachment.FileSize),
|
||||||
Created: n.Posted,
|
Created: n.Posted,
|
||||||
CreatedUnix: timeutil.FromTime(n.Posted),
|
|
||||||
// We directly pass the file contents here to have a way to link the attachment to the file later.
|
// We directly pass the file contents here to have a way to link the attachment to the file later.
|
||||||
// Because we don't have an ID for our task at this point of the migration, we cannot just throw all
|
// Because we don't have an ID for our task at this point of the migration, we cannot just throw all
|
||||||
// attachments in a slice and do the work of downloading and properly storing them later.
|
// attachments in a slice and do the work of downloading and properly storing them later.
|
||||||
FileContent: buf.Bytes(),
|
FileContent: buf.Bytes(),
|
||||||
},
|
},
|
||||||
Created: timeutil.FromTime(n.Posted),
|
Created: n.Posted,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +373,7 @@ func convertTodoistToVikunja(sync *sync) (fullVikunjaHierachie []*models.Namespa
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks[r.ItemID].Reminders = append(tasks[r.ItemID].Reminders, timeutil.FromTime(date))
|
tasks[r.ItemID].Reminders = append(tasks[r.ItemID].Reminders, date.In(config.GetTimeZone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*models.NamespaceWithLists{
|
return []*models.NamespaceWithLists{
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -35,12 +34,16 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
|
|
||||||
time1, err := time.Parse(time.RFC3339Nano, "2014-09-26T08:25:05Z")
|
time1, err := time.Parse(time.RFC3339Nano, "2014-09-26T08:25:05Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time1 = time1.In(config.GetTimeZone())
|
||||||
time3, err := time.Parse(time.RFC3339Nano, "2014-10-21T08:25:05Z")
|
time3, err := time.Parse(time.RFC3339Nano, "2014-10-21T08:25:05Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time3 = time3.In(config.GetTimeZone())
|
||||||
dueTime, err := time.Parse(time.RFC3339Nano, "2020-05-31T00:00:00Z")
|
dueTime, err := time.Parse(time.RFC3339Nano, "2020-05-31T00:00:00Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
nilTime, err := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:00Z")
|
dueTime = dueTime.In(config.GetTimeZone())
|
||||||
|
nilTime, err := time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
//nilTime = nilTime.In(config.GetTimeZone())
|
||||||
exampleFile, err := ioutil.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/wunderlist/testimage.jpg")
|
exampleFile, err := ioutil.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/wunderlist/testimage.jpg")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -344,68 +347,68 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Title: "Task400000000",
|
Title: "Task400000000",
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
timeutil.FromTime(time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
timeutil.FromTime(time.Date(2020, time.June, 16, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.June, 16, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000001",
|
Title: "Task400000001",
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000002",
|
Title: "Task400000002",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
timeutil.FromTime(time.Date(2020, time.July, 15, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.July, 15, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000003",
|
Title: "Task400000003",
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
timeutil.FromTime(time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000004",
|
Title: "Task400000004",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000005",
|
Title: "Task400000005",
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
timeutil.FromTime(time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000006",
|
Title: "Task400000006",
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindSubtask: {
|
models.RelationKindSubtask: {
|
||||||
{
|
{
|
||||||
Title: "Task with parent",
|
Title: "Task with parent",
|
||||||
Done: false,
|
Done: false,
|
||||||
Priority: 2,
|
Priority: 2,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(nilTime),
|
DoneAt: nilTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -414,34 +417,34 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Title: "Task with parent",
|
Title: "Task with parent",
|
||||||
Done: false,
|
Done: false,
|
||||||
Priority: 2,
|
Priority: 2,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(nilTime),
|
DoneAt: nilTime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000106",
|
Title: "Task400000106",
|
||||||
Done: true,
|
Done: true,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000107",
|
Title: "Task400000107",
|
||||||
Done: true,
|
Done: true,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000108",
|
Title: "Task400000108",
|
||||||
Done: true,
|
Done: true,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000109",
|
Title: "Task400000109",
|
||||||
Done: true,
|
Done: true,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -453,35 +456,35 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
{
|
{
|
||||||
Title: "Task400000007",
|
Title: "Task400000007",
|
||||||
Done: false,
|
Done: false,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000008",
|
Title: "Task400000008",
|
||||||
Done: false,
|
Done: false,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000009",
|
Title: "Task400000009",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Reminders: []timeutil.TimeStamp{
|
Reminders: []time.Time{
|
||||||
timeutil.FromTime(time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC)),
|
time.Date(2020, time.June, 15, 0, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000010",
|
Title: "Task400000010",
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: true,
|
Done: true,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000101",
|
Title: "Task400000101",
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Attachments: []*models.TaskAttachment{
|
Attachments: []*models.TaskAttachment{
|
||||||
{
|
{
|
||||||
File: &files.File{
|
File: &files.File{
|
||||||
|
@ -489,37 +492,36 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
Mime: "text/plain",
|
Mime: "text/plain",
|
||||||
Size: 12345,
|
Size: 12345,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
CreatedUnix: timeutil.FromTime(time1),
|
|
||||||
FileContent: exampleFile,
|
FileContent: exampleFile,
|
||||||
},
|
},
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000102",
|
Title: "Task400000102",
|
||||||
Done: false,
|
Done: false,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000103",
|
Title: "Task400000103",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000104",
|
Title: "Task400000104",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Task400000105",
|
Title: "Task400000105",
|
||||||
Done: false,
|
Done: false,
|
||||||
DueDate: timeutil.FromTime(dueTime),
|
DueDate: dueTime,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -532,8 +534,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
||||||
{
|
{
|
||||||
Title: "Task400000111",
|
Title: "Task400000111",
|
||||||
Done: true,
|
Done: true,
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
DoneAt: timeutil.FromTime(time3),
|
DoneAt: time3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/modules/migration"
|
"code.vikunja.io/api/pkg/modules/migration"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -145,7 +144,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
|
||||||
|
|
||||||
l := &models.List{
|
l := &models.List{
|
||||||
Title: list.Title,
|
Title: list.Title,
|
||||||
Created: timeutil.FromTime(list.CreatedAt),
|
Created: list.CreatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all tasks belonging to this list and put them in
|
// Find all tasks belonging to this list and put them in
|
||||||
|
@ -153,13 +152,13 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
|
||||||
if t.ListID == listID {
|
if t.ListID == listID {
|
||||||
newTask := &models.Task{
|
newTask := &models.Task{
|
||||||
Title: t.Title,
|
Title: t.Title,
|
||||||
Created: timeutil.FromTime(t.CreatedAt),
|
Created: t.CreatedAt,
|
||||||
Done: t.Completed,
|
Done: t.Completed,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Done At
|
// Set Done At
|
||||||
if newTask.Done {
|
if newTask.Done {
|
||||||
newTask.DoneAt = timeutil.FromTime(t.CompletedAt)
|
newTask.DoneAt = t.CompletedAt.In(config.GetTimeZone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the due date
|
// Parse the due date
|
||||||
|
@ -168,7 +167,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newTask.DueDate = timeutil.FromTime(dueDate)
|
newTask.DueDate = dueDate.In(config.GetTimeZone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find related notes
|
// Find related notes
|
||||||
|
@ -195,17 +194,16 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
|
||||||
|
|
||||||
newTask.Attachments = append(newTask.Attachments, &models.TaskAttachment{
|
newTask.Attachments = append(newTask.Attachments, &models.TaskAttachment{
|
||||||
File: &files.File{
|
File: &files.File{
|
||||||
Name: f.FileName,
|
Name: f.FileName,
|
||||||
Mime: f.ContentType,
|
Mime: f.ContentType,
|
||||||
Size: uint64(f.FileSize),
|
Size: uint64(f.FileSize),
|
||||||
Created: f.CreatedAt,
|
Created: f.CreatedAt,
|
||||||
CreatedUnix: timeutil.FromTime(f.CreatedAt),
|
|
||||||
// We directly pass the file contents here to have a way to link the attachment to the file later.
|
// We directly pass the file contents here to have a way to link the attachment to the file later.
|
||||||
// Because we don't have an ID for our task at this point of the migration, we cannot just throw all
|
// Because we don't have an ID for our task at this point of the migration, we cannot just throw all
|
||||||
// attachments in a slice and do the work of downloading and properly storing them later.
|
// attachments in a slice and do the work of downloading and properly storing them later.
|
||||||
FileContent: buf.Bytes(),
|
FileContent: buf.Bytes(),
|
||||||
},
|
},
|
||||||
Created: timeutil.FromTime(f.CreatedAt),
|
Created: f.CreatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +223,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
|
||||||
// Reminders
|
// Reminders
|
||||||
for _, r := range content.reminders {
|
for _, r := range content.reminders {
|
||||||
if r.TaskID == t.ID {
|
if r.TaskID == t.ID {
|
||||||
newTask.Reminders = append(newTask.Reminders, timeutil.FromTime(r.Date))
|
newTask.Reminders = append(newTask.Reminders, r.Date.In(config.GetTimeZone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,8 +246,8 @@ func convertWunderlistToVikunja(content *wunderlistContents) (fullVikunjaHierach
|
||||||
namespace := &models.NamespaceWithLists{
|
namespace := &models.NamespaceWithLists{
|
||||||
Namespace: models.Namespace{
|
Namespace: models.Namespace{
|
||||||
Title: folder.Title,
|
Title: folder.Title,
|
||||||
Created: timeutil.FromTime(folder.CreatedAt),
|
Created: folder.CreatedAt,
|
||||||
Updated: timeutil.FromTime(folder.UpdatedAt),
|
Updated: folder.UpdatedAt,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/files"
|
"code.vikunja.io/api/pkg/files"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/d4l3k/messagediff.v1"
|
"gopkg.in/d4l3k/messagediff.v1"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -35,12 +34,16 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
|
|
||||||
time1, err := time.Parse(time.RFC3339Nano, "2013-08-30T08:29:46.203Z")
|
time1, err := time.Parse(time.RFC3339Nano, "2013-08-30T08:29:46.203Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time1 = time1.In(config.GetTimeZone())
|
||||||
time2, err := time.Parse(time.RFC3339Nano, "2013-08-30T08:36:13.273Z")
|
time2, err := time.Parse(time.RFC3339Nano, "2013-08-30T08:36:13.273Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time2 = time2.In(config.GetTimeZone())
|
||||||
time3, err := time.Parse(time.RFC3339Nano, "2013-09-05T08:36:13.273Z")
|
time3, err := time.Parse(time.RFC3339Nano, "2013-09-05T08:36:13.273Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time3 = time3.In(config.GetTimeZone())
|
||||||
time4, err := time.Parse(time.RFC3339Nano, "2013-08-02T11:58:55Z")
|
time4, err := time.Parse(time.RFC3339Nano, "2013-08-02T11:58:55Z")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
time4 = time4.In(config.GetTimeZone())
|
||||||
|
|
||||||
exampleFile, err := ioutil.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/wunderlist/testimage.jpg")
|
exampleFile, err := ioutil.ReadFile(config.ServiceRootpath.GetString() + "/pkg/modules/migration/wunderlist/testimage.jpg")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -51,6 +54,7 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
if done {
|
if done {
|
||||||
completedAt = time1
|
completedAt = time1
|
||||||
}
|
}
|
||||||
|
completedAt = completedAt.In(config.GetTimeZone())
|
||||||
return &task{
|
return &task{
|
||||||
ID: id,
|
ID: id,
|
||||||
AssigneeID: 123,
|
AssigneeID: 123,
|
||||||
|
@ -193,18 +197,18 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
{
|
{
|
||||||
Namespace: models.Namespace{
|
Namespace: models.Namespace{
|
||||||
Title: "Lorem Ipsum",
|
Title: "Lorem Ipsum",
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Updated: timeutil.FromTime(time2),
|
Updated: time2,
|
||||||
},
|
},
|
||||||
Lists: []*models.List{
|
Lists: []*models.List{
|
||||||
{
|
{
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Title: "Lorem1",
|
Title: "Lorem1",
|
||||||
Tasks: []*models.Task{
|
Tasks: []*models.Task{
|
||||||
{
|
{
|
||||||
Title: "Ipsum1",
|
Title: "Ipsum1",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Attachments: []*models.TaskAttachment{
|
Attachments: []*models.TaskAttachment{
|
||||||
{
|
{
|
||||||
|
@ -213,18 +217,17 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
Mime: "text/plain",
|
Mime: "text/plain",
|
||||||
Size: 12345,
|
Size: 12345,
|
||||||
Created: time2,
|
Created: time2,
|
||||||
CreatedUnix: timeutil.FromTime(time2),
|
|
||||||
FileContent: exampleFile,
|
FileContent: exampleFile,
|
||||||
},
|
},
|
||||||
Created: timeutil.FromTime(time2),
|
Created: time2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Reminders: []timeutil.TimeStamp{timeutil.FromTime(time4)},
|
Reminders: []time.Time{time4},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum2",
|
Title: "Ipsum2",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindSubtask: {
|
models.RelationKindSubtask: {
|
||||||
|
@ -240,15 +243,15 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Title: "Lorem2",
|
Title: "Lorem2",
|
||||||
Tasks: []*models.Task{
|
Tasks: []*models.Task{
|
||||||
{
|
{
|
||||||
Title: "Ipsum3",
|
Title: "Ipsum3",
|
||||||
Done: true,
|
Done: true,
|
||||||
DoneAt: timeutil.FromTime(time1),
|
DoneAt: time1,
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Attachments: []*models.TaskAttachment{
|
Attachments: []*models.TaskAttachment{
|
||||||
{
|
{
|
||||||
|
@ -257,18 +260,17 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
Mime: "text/plain",
|
Mime: "text/plain",
|
||||||
Size: 12345,
|
Size: 12345,
|
||||||
Created: time3,
|
Created: time3,
|
||||||
CreatedUnix: timeutil.FromTime(time3),
|
|
||||||
FileContent: exampleFile,
|
FileContent: exampleFile,
|
||||||
},
|
},
|
||||||
Created: timeutil.FromTime(time3),
|
Created: time3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum4",
|
Title: "Ipsum4",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Reminders: []timeutil.TimeStamp{timeutil.FromTime(time3)},
|
Reminders: []time.Time{time3},
|
||||||
RelatedTasks: map[models.RelationKind][]*models.Task{
|
RelatedTasks: map[models.RelationKind][]*models.Task{
|
||||||
models.RelationKindSubtask: {
|
models.RelationKindSubtask: {
|
||||||
{
|
{
|
||||||
|
@ -280,52 +282,52 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Title: "Lorem3",
|
Title: "Lorem3",
|
||||||
Tasks: []*models.Task{
|
Tasks: []*models.Task{
|
||||||
{
|
{
|
||||||
Title: "Ipsum5",
|
Title: "Ipsum5",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum6",
|
Title: "Ipsum6",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Done: true,
|
Done: true,
|
||||||
DoneAt: timeutil.FromTime(time1),
|
DoneAt: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum7",
|
Title: "Ipsum7",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Done: true,
|
Done: true,
|
||||||
DoneAt: timeutil.FromTime(time1),
|
DoneAt: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum8",
|
Title: "Ipsum8",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Title: "Lorem4",
|
Title: "Lorem4",
|
||||||
Tasks: []*models.Task{
|
Tasks: []*models.Task{
|
||||||
{
|
{
|
||||||
Title: "Ipsum9",
|
Title: "Ipsum9",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Done: true,
|
Done: true,
|
||||||
DoneAt: timeutil.FromTime(time1),
|
DoneAt: time1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Title: "Ipsum10",
|
Title: "Ipsum10",
|
||||||
DueDate: 1378339200,
|
DueDate: time.Unix(1378339200, 0).In(config.GetTimeZone()),
|
||||||
Created: timeutil.FromTime(time1),
|
Created: time1,
|
||||||
Done: true,
|
Done: true,
|
||||||
DoneAt: timeutil.FromTime(time1),
|
DoneAt: time1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -337,7 +339,7 @@ func TestWunderlistParsing(t *testing.T) {
|
||||||
},
|
},
|
||||||
Lists: []*models.List{
|
Lists: []*models.List{
|
||||||
{
|
{
|
||||||
Created: timeutil.FromTime(time4),
|
Created: time4,
|
||||||
Title: "List without a namespace",
|
Title: "List without a namespace",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -197,7 +197,7 @@ func (vcls *VikunjaCaldavListStorage) GetResource(rpath string) (*data.Resource,
|
||||||
}
|
}
|
||||||
|
|
||||||
vcls.task = &task
|
vcls.task = &task
|
||||||
if updated > 0 {
|
if updated.Unix() > 0 {
|
||||||
vcls.task.Updated = updated
|
vcls.task.Updated = updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,12 +342,12 @@ func (vlra *VikunjaListResourceAdapter) CalculateEtag() string {
|
||||||
|
|
||||||
// Return the etag of a task if we have one
|
// Return the etag of a task if we have one
|
||||||
if vlra.task != nil {
|
if vlra.task != nil {
|
||||||
return `"` + strconv.FormatInt(vlra.task.ID, 10) + `-` + strconv.FormatInt(vlra.task.Updated.ToTime().Unix(), 10) + `"`
|
return `"` + strconv.FormatInt(vlra.task.ID, 10) + `-` + strconv.FormatInt(vlra.task.Updated.Unix(), 10) + `"`
|
||||||
}
|
}
|
||||||
// This also returns the etag of the list, and not of the task,
|
// This also returns the etag of the list, and not of the task,
|
||||||
// which becomes problematic because the client uses this etag (= the one from the list) to make
|
// which becomes problematic because the client uses this etag (= the one from the list) to make
|
||||||
// Requests to update a task. These do not match and thus updating a task fails.
|
// Requests to update a task. These do not match and thus updating a task fails.
|
||||||
return `"` + strconv.FormatInt(vlra.list.ID, 10) + `-` + strconv.FormatInt(vlra.list.Updated.ToTime().Unix(), 10) + `"`
|
return `"` + strconv.FormatInt(vlra.list.ID, 10) + `-` + strconv.FormatInt(vlra.list.Updated.Unix(), 10) + `"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContent returns the content string of a resource (a task in our case)
|
// GetContent returns the content string of a resource (a task in our case)
|
||||||
|
@ -372,11 +372,11 @@ func (vlra *VikunjaListResourceAdapter) GetContentSize() int64 {
|
||||||
// GetModTime returns when the resource was last modified
|
// GetModTime returns when the resource was last modified
|
||||||
func (vlra *VikunjaListResourceAdapter) GetModTime() time.Time {
|
func (vlra *VikunjaListResourceAdapter) GetModTime() time.Time {
|
||||||
if vlra.task != nil {
|
if vlra.task != nil {
|
||||||
return time.Unix(vlra.task.Updated.ToTime().Unix(), 0)
|
return vlra.task.Updated
|
||||||
}
|
}
|
||||||
|
|
||||||
if vlra.list != nil {
|
if vlra.list != nil {
|
||||||
return time.Unix(vlra.list.Updated.ToTime().Unix(), 0)
|
return vlra.list.Updated
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/caldav"
|
"code.vikunja.io/api/pkg/caldav"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"github.com/laurent22/ical-go"
|
"github.com/laurent22/ical-go"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -32,7 +31,7 @@ func getCaldavTodosForTasks(list *models.List) string {
|
||||||
var caldavtodos []*caldav.Todo
|
var caldavtodos []*caldav.Todo
|
||||||
for _, t := range list.Tasks {
|
for _, t := range list.Tasks {
|
||||||
|
|
||||||
duration := t.EndDate.ToTime().Sub(t.StartDate.ToTime())
|
duration := t.EndDate.Sub(t.StartDate)
|
||||||
|
|
||||||
caldavtodos = append(caldavtodos, &caldav.Todo{
|
caldavtodos = append(caldavtodos, &caldav.Todo{
|
||||||
Timestamp: t.Updated,
|
Timestamp: t.Updated,
|
||||||
|
@ -104,22 +103,22 @@ func parseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||||
vTask.Done = true
|
vTask.Done = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if duration > 0 && vTask.StartDate > 0 {
|
if duration > 0 && !vTask.StartDate.IsZero() {
|
||||||
vTask.EndDate = timeutil.FromTime(vTask.StartDate.ToTime().Add(duration))
|
vTask.EndDate = vTask.StartDate.Add(duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func caldavTimeToTimestamp(tstring string) timeutil.TimeStamp {
|
func caldavTimeToTimestamp(tstring string) time.Time {
|
||||||
if tstring == "" {
|
if tstring == "" {
|
||||||
return 0
|
return time.Time{}
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := time.Parse(caldav.DateFormat, tstring)
|
t, err := time.Parse(caldav.DateFormat, tstring)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s", tstring, err)
|
log.Warningf("Error while parsing caldav time %s to TimeStamp: %s", tstring, err)
|
||||||
return 0
|
return time.Time{}
|
||||||
}
|
}
|
||||||
return timeutil.FromTime(t)
|
return t
|
||||||
}
|
}
|
||||||
|
|
|
@ -1013,7 +1013,7 @@ var doc = `{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with ` + "`" + `order_by` + "`" + `. Possible values to sort by are ` + "`" + `id` + "`" + `, ` + "`" + `text` + "`" + `, ` + "`" + `description` + "`" + `, ` + "`" + `done` + "`" + `, ` + "`" + `done_at_unix` + "`" + `, ` + "`" + `due_date_unix` + "`" + `, ` + "`" + `created_by_id` + "`" + `, ` + "`" + `list_id` + "`" + `, ` + "`" + `repeat_after` + "`" + `, ` + "`" + `priority` + "`" + `, ` + "`" + `start_date_unix` + "`" + `, ` + "`" + `end_date_unix` + "`" + `, ` + "`" + `hex_color` + "`" + `, ` + "`" + `percent_done` + "`" + `, ` + "`" + `uid` + "`" + `, ` + "`" + `created` + "`" + `, ` + "`" + `updated` + "`" + `. Default is ` + "`" + `id` + "`" + `.",
|
"description": "The sorting parameter. You can pass this multiple times to get the tasks ordered by multiple different parametes, along with ` + "`" + `order_by` + "`" + `. Possible values to sort by are ` + "`" + `id` + "`" + `, ` + "`" + `text` + "`" + `, ` + "`" + `description` + "`" + `, ` + "`" + `done` + "`" + `, ` + "`" + `done_at` + "`" + `, ` + "`" + `due_date` + "`" + `, ` + "`" + `created_by_id` + "`" + `, ` + "`" + `list_id` + "`" + `, ` + "`" + `repeat_after` + "`" + `, ` + "`" + `priority` + "`" + `, ` + "`" + `start_date` + "`" + `, ` + "`" + `end_date` + "`" + `, ` + "`" + `hex_color` + "`" + `, ` + "`" + `percent_done` + "`" + `, ` + "`" + `uid` + "`" + `, ` + "`" + `created` + "`" + `, ` + "`" + `updated` + "`" + `. Default is ` + "`" + `id` + "`" + `.",
|
||||||
"name": "sort_by",
|
"name": "sort_by",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
|
@ -4588,7 +4588,7 @@ var doc = `{
|
||||||
"migrator_name": {
|
"migrator_name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"time_unix": {
|
"time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Vikunja is a todo-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 timeutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"4d63.com/tz"
|
|
||||||
"code.vikunja.io/api/pkg/config"
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TimeStamp is a type which holds a unix timestamp, but becomes a RFC3339 date when parsed to json.
|
|
||||||
// This allows us to save the time as a unix timestamp into the database while returning it as an iso
|
|
||||||
// date to the api client.
|
|
||||||
type TimeStamp int64
|
|
||||||
|
|
||||||
// ToTime returns a time.Time from a TimeStamp
|
|
||||||
func (ts *TimeStamp) ToTime() time.Time {
|
|
||||||
return time.Unix(int64(*ts), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromTime converts a time.Time to a TimeStamp
|
|
||||||
func FromTime(t time.Time) TimeStamp {
|
|
||||||
return TimeStamp(t.Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON converts a TimeStamp to a json string
|
|
||||||
func (ts *TimeStamp) MarshalJSON() ([]byte, error) {
|
|
||||||
if int64(*ts) == 0 {
|
|
||||||
return []byte("null"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
loc, err := tz.LoadLocation(config.ServiceTimeZone.GetString())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := `"` + ts.ToTime().In(loc).Format(time.RFC3339) + `"`
|
|
||||||
return []byte(s), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON converts an iso date string from json to a TimeStamp
|
|
||||||
func (ts *TimeStamp) UnmarshalJSON(data []byte) error {
|
|
||||||
var s string
|
|
||||||
err := json.Unmarshal(data, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if s == "" {
|
|
||||||
*ts = FromTime(time.Unix(0, 0))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339, s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
loc, err := time.LoadLocation(config.ServiceTimeZone.GetString())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*ts = TimeStamp(t.In(loc).Unix())
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/mail"
|
"code.vikunja.io/api/pkg/mail"
|
||||||
"code.vikunja.io/api/pkg/metrics"
|
"code.vikunja.io/api/pkg/metrics"
|
||||||
"code.vikunja.io/api/pkg/timeutil"
|
|
||||||
"code.vikunja.io/api/pkg/utils"
|
"code.vikunja.io/api/pkg/utils"
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -29,6 +28,7 @@ import (
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Login Object to recive user credentials in JSON format
|
// Login Object to recive user credentials in JSON format
|
||||||
|
@ -56,9 +56,9 @@ type User struct {
|
||||||
EmailConfirmToken string `xorm:"varchar(450) null" json:"-"`
|
EmailConfirmToken string `xorm:"varchar(450) null" json:"-"`
|
||||||
|
|
||||||
// A timestamp when this task was created. You cannot change this value.
|
// A timestamp when this task was created. You cannot change this value.
|
||||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
Created time.Time `xorm:"created not null" json:"created"`
|
||||||
// A timestamp when this task was last updated. You cannot change this value.
|
// A timestamp when this task was last updated. You cannot change this value.
|
||||||
Updated timeutil.TimeStamp `xorm:"updated not null" json:"updated"`
|
Updated time.Time `xorm:"updated not null" json:"updated"`
|
||||||
|
|
||||||
web.Auth `xorm:"-" json:"-"`
|
web.Auth `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,9 @@ func getUser(user *User, withEmail bool) (userOut *User, err error) {
|
||||||
userOut = &User{} // To prevent a panic if user is nil
|
userOut = &User{} // To prevent a panic if user is nil
|
||||||
*userOut = *user
|
*userOut = *user
|
||||||
exists, err := x.Get(userOut)
|
exists, err := x.Get(userOut)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
return &User{}, ErrUserDoesNotExist{UserID: user.ID}
|
return &User{}, ErrUserDoesNotExist{UserID: user.ID}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue