feat: Add relative Reminders (#1427)
Partially resolves #1416 Co-authored-by: ce72 <christoph.ernst72@googlemail.com> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/1427 Reviewed-by: konrad <k@knt.li> Co-authored-by: cernst <ce72@noreply.kolaente.de> Co-committed-by: cernst <ce72@noreply.kolaente.de>
This commit is contained in:
parent
823c817b1f
commit
3f5252dc24
@ -90,6 +90,7 @@ This document describes the different errors Vikunja can return.
|
|||||||
| 4019 | 400 | Invalid task filter value. |
|
| 4019 | 400 | Invalid task filter value. |
|
||||||
| 4020 | 400 | The provided attachment does not belong to that task. |
|
| 4020 | 400 | The provided attachment does not belong to that task. |
|
||||||
| 4021 | 400 | This user is already assigned to that task. |
|
| 4021 | 400 | This user is already assigned to that task. |
|
||||||
|
| 4022 | 400 | The task has a relative reminder which does not specify relative to what. |
|
||||||
|
|
||||||
## Namespace
|
## Namespace
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
task_id: 27
|
task_id: 27
|
||||||
reminder: 2018-12-01 01:13:44
|
reminder: 2018-12-01 01:13:44
|
||||||
created: 2018-12-01 01:12:04
|
created: 2018-12-01 01:12:04
|
||||||
|
relative_to: 'start_date'
|
||||||
|
relative_period: -3600
|
||||||
- id: 3
|
- id: 3
|
||||||
task_id: 2
|
task_id: 2
|
||||||
reminder: 2018-12-01 01:13:44
|
reminder: 2018-12-01 01:13:44
|
||||||
|
@ -244,7 +244,7 @@
|
|||||||
created: 2018-12-01 01:12:04
|
created: 2018-12-01 01:12:04
|
||||||
updated: 2018-12-01 01:12:04
|
updated: 2018-12-01 01:12:04
|
||||||
- id: 27
|
- id: 27
|
||||||
title: 'task #27 with reminders'
|
title: 'task #27 with reminders and start_date'
|
||||||
done: false
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
project_id: 1
|
project_id: 1
|
||||||
@ -252,6 +252,7 @@
|
|||||||
bucket_id: 1
|
bucket_id: 1
|
||||||
created: 2018-12-01 01:12:04
|
created: 2018-12-01 01:12:04
|
||||||
updated: 2018-12-01 01:12:04
|
updated: 2018-12-01 01:12:04
|
||||||
|
start_date: 2018-11-30 22:25:24
|
||||||
- id: 28
|
- id: 28
|
||||||
title: 'task #28 with repeat after'
|
title: 'task #28 with repeat after'
|
||||||
done: false
|
done: false
|
||||||
|
@ -42,14 +42,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
||||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #2`)
|
assert.Contains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #3`)
|
assert.Contains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #4`)
|
assert.Contains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #10`)
|
assert.Contains(t, rec.Body.String(), `task #10`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #11`)
|
assert.Contains(t, rec.Body.String(), `task #11`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #12`)
|
assert.Contains(t, rec.Body.String(), `task #12`)
|
||||||
@ -75,14 +75,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #7`)
|
assert.NotContains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #9`)
|
assert.NotContains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -93,14 +93,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"tASk #6"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"tASk #6"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #7`)
|
assert.NotContains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #9`)
|
assert.NotContains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// 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"}}, 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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
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":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
// Due date without unix suffix
|
// Due date without unix suffix
|
||||||
t.Run("by duedate asc without suffix", func(t *testing.T) {
|
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)
|
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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by due_date without suffix", func(t *testing.T) {
|
t.Run("by due_date without 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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc without suffix", func(t *testing.T) {
|
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)
|
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":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
t.Run("by duedate asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, urlParams)
|
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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("invalid sort parameter", func(t *testing.T) {
|
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)
|
||||||
@ -171,10 +171,10 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
// Invalid parameter should not sort at all
|
// Invalid parameter should not sort at all
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, urlParams)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, urlParams)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
||||||
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_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Filter", func(t *testing.T) {
|
t.Run("Filter", func(t *testing.T) {
|
||||||
@ -190,14 +190,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -215,14 +215,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #6`)
|
assert.NotContains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -255,14 +255,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -291,14 +291,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -314,14 +314,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
// Not using assert.Equal to avoid having the tests break every time we add new fixtures
|
||||||
assert.Contains(t, rec.Body.String(), `task #1`)
|
assert.Contains(t, rec.Body.String(), `task #1`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #2`)
|
assert.Contains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #3`)
|
assert.Contains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #4`)
|
assert.Contains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #10`)
|
assert.Contains(t, rec.Body.String(), `task #10`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #11`)
|
assert.Contains(t, rec.Body.String(), `task #11`)
|
||||||
assert.Contains(t, rec.Body.String(), `task #12`)
|
assert.Contains(t, rec.Body.String(), `task #12`)
|
||||||
@ -347,14 +347,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #7`)
|
assert.NotContains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #9`)
|
assert.NotContains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -366,42 +366,42 @@ 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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
t.Run("by priority desc", func(t *testing.T) {
|
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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-3","index":3,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":4,"title":"task #4 low prio","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"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":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
|
||||||
})
|
})
|
||||||
// should equal duedate asc
|
// 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"}}, 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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate desc", func(t *testing.T) {
|
t.Run("by duedate desc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"desc"}}, nil)
|
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":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":6,"title":"task #6 lower due date`)
|
||||||
})
|
})
|
||||||
t.Run("by duedate asc", func(t *testing.T) {
|
t.Run("by duedate asc", func(t *testing.T) {
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"due_date"}, "order_by": []string{"asc"}}, nil)
|
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":"0001-01-01T00:00:00Z","due_date":"2018-11-30T22:25:24Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
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,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-6","index":6,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":3,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"2018-12-01T03:58:44Z","reminder_dates":null,"reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"test1-5","index":5,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":2,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}`)
|
||||||
})
|
})
|
||||||
t.Run("invalid parameter", func(t *testing.T) {
|
t.Run("invalid parameter", func(t *testing.T) {
|
||||||
// Invalid parameter should not sort at all
|
// Invalid parameter should not sort at all
|
||||||
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, nil)
|
rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
assert.NotContains(t, rec.Body.String(), `[{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
assert.NotContains(t, rec.Body.String(), `{"id":4,"title":"task #4 low prio","description":"","done":false,"due_date":0,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":1,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":3,"title":"task #3 high prio","description":"","done":false,"due_date":0,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":100,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||||
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
assert.NotContains(t, rec.Body.String(), `[{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":6,"title":"task #6 lower due date"`)
|
||||||
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_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"reminders":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"name":"","username":"","email":"","created":0,"updated":0}}]`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Filter", func(t *testing.T) {
|
t.Run("Filter", func(t *testing.T) {
|
||||||
@ -417,14 +417,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #5`)
|
assert.Contains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #6`)
|
assert.Contains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #8`)
|
assert.Contains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
@ -442,14 +442,14 @@ func TestTaskCollection(t *testing.T) {
|
|||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #1`)
|
assert.NotContains(t, rec.Body.String(), `task #1`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #2`)
|
assert.NotContains(t, rec.Body.String(), `task #2 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #3`)
|
assert.NotContains(t, rec.Body.String(), `task #3 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #4`)
|
assert.NotContains(t, rec.Body.String(), `task #4 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #5`)
|
assert.NotContains(t, rec.Body.String(), `task #5 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #6`)
|
assert.NotContains(t, rec.Body.String(), `task #6 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #7`)
|
assert.Contains(t, rec.Body.String(), `task #7 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #8`)
|
assert.NotContains(t, rec.Body.String(), `task #8 `)
|
||||||
assert.Contains(t, rec.Body.String(), `task #9`)
|
assert.Contains(t, rec.Body.String(), `task #9 `)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #10`)
|
assert.NotContains(t, rec.Body.String(), `task #10`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #11`)
|
assert.NotContains(t, rec.Body.String(), `task #11`)
|
||||||
assert.NotContains(t, rec.Body.String(), `task #12`)
|
assert.NotContains(t, rec.Body.String(), `task #12`)
|
||||||
|
@ -95,24 +95,47 @@ func TestTask(t *testing.T) {
|
|||||||
assert.Contains(t, rec.Body.String(), `"due_date":"0001-01-01T00:00:00Z"`)
|
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) {
|
// Deprecated: Remove if ReminderDates is removed
|
||||||
|
t.Run("ReminderDates", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"reminder_dates": ["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"reminder_dates": ["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]`)
|
assert.Contains(t, rec.Body.String(), `"reminder_dates":["2020-02-10T10:00:00Z","2020-02-11T10:00:00Z"]`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates": null`)
|
assert.NotContains(t, rec.Body.String(), `"reminder_dates": null`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
// Deprecated: Remove if ReminderDates is removed
|
||||||
|
t.Run("ReminderDates unset to empty array", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminder_dates": []}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminder_dates": []}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
||||||
})
|
})
|
||||||
t.Run("Reminders unset to null", func(t *testing.T) {
|
// Deprecated: Remove if ReminderDates is removed
|
||||||
|
t.Run("ReminderDates unset to null", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminder_dates": null}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminder_dates": null}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
||||||
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
assert.NotContains(t, rec.Body.String(), `"reminder_dates":[1543626724,1543626824]`)
|
||||||
})
|
})
|
||||||
|
t.Run("Reminders", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"reminders": [{"reminder": "2020-02-10T10:00:00Z"},{"reminder": "2020-02-11T10:00:00Z"}]}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"reminders":[`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `{"reminder":"2020-02-10T10:00:00Z"`)
|
||||||
|
assert.Contains(t, rec.Body.String(), `{"reminder":"2020-02-11T10:00:00Z"`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `"reminders":null`)
|
||||||
|
})
|
||||||
|
t.Run("Reminders unset to empty array", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders": []}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"reminders":null`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||||
|
})
|
||||||
|
t.Run("Reminders unset to null", func(t *testing.T) {
|
||||||
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "27"}, `{"reminders": null}`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, rec.Body.String(), `"reminder_dates":null`)
|
||||||
|
assert.NotContains(t, rec.Body.String(), `{"Reminder":"2020-02-10T10:00:00Z"`)
|
||||||
|
})
|
||||||
t.Run("Repeat after", func(t *testing.T) {
|
t.Run("Repeat after", func(t *testing.T) {
|
||||||
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"repeat_after":3600}`)
|
rec, err := testHandler.testUpdateWithUser(nil, map[string]string{"projecttask": "1"}, `{"repeat_after":3600}`)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
44
pkg/migration/20230307171848.go
Normal file
44
pkg/migration/20230307171848.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Vikunja is a to-do list application to facilitate your life.
|
||||||
|
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public Licensee for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public Licensee
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"src.techknowlogick.com/xormigrate"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type taskReminders20230307171848 struct {
|
||||||
|
RelativePeriod int64 `xorm:"bigint null" json:"relative_period"`
|
||||||
|
RelativeTo string `xorm:"varchar(50) null" json:"relative_to,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (taskReminders20230307171848) TableName() string {
|
||||||
|
return "task_reminders"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
migrations = append(migrations, &xormigrate.Migration{
|
||||||
|
ID: "20230307171848",
|
||||||
|
Description: "Add relative period to task reminders",
|
||||||
|
Migrate: func(tx *xorm.Engine) error {
|
||||||
|
return tx.Sync2(taskReminders20230307171848{})
|
||||||
|
},
|
||||||
|
Rollback: func(tx *xorm.Engine) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -875,6 +875,33 @@ func (err ErrUserAlreadyAssigned) HTTPError() web.HTTPError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrReminderRelativeToMissing represents an error where a task has a relative reminder without reference date
|
||||||
|
type ErrReminderRelativeToMissing struct {
|
||||||
|
TaskID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrReminderRelativeToMissing checks if an error is ErrReminderRelativeToMissing.
|
||||||
|
func IsErrReminderRelativeToMissing(err error) bool {
|
||||||
|
_, ok := err.(ErrReminderRelativeToMissing)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrReminderRelativeToMissing) Error() string {
|
||||||
|
return fmt.Sprintf("Task [TaskID: %v] has a relative reminder without relative_to", err.TaskID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCodeRelationDoesNotExist holds the unique world-error code of this error
|
||||||
|
const ErrCodeReminderRelativeToMissing = 4022
|
||||||
|
|
||||||
|
// HTTPError holds the http error description
|
||||||
|
func (err ErrReminderRelativeToMissing) HTTPError() web.HTTPError {
|
||||||
|
return web.HTTPError{
|
||||||
|
HTTPCode: http.StatusBadRequest,
|
||||||
|
Code: ErrCodeReminderRelativeToMissing,
|
||||||
|
Message: "Please provide what the reminder date is relative to",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =================
|
// =================
|
||||||
// Namespace errors
|
// Namespace errors
|
||||||
// =================
|
// =================
|
||||||
|
@ -266,6 +266,13 @@ func getNativeValueForTaskField(fieldName string, comparator taskFilterComparato
|
|||||||
return nil, nil, ErrInvalidTaskField{TaskField: fieldName}
|
return nil, nil, ErrInvalidTaskField{TaskField: fieldName}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if realFieldName == "Reminders" {
|
||||||
|
field, ok = reflect.TypeOf(&TaskReminder{}).Elem().FieldByName("Reminder")
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, ErrInvalidTaskField{TaskField: fieldName}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if comparator == taskFilterComparatorIn {
|
if comparator == taskFilterComparatorIn {
|
||||||
vals := strings.Split(value, ",")
|
vals := strings.Split(value, ",")
|
||||||
valueSlice := []interface{}{}
|
valueSlice := []interface{}{}
|
||||||
|
@ -169,9 +169,17 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||||||
label4,
|
label4,
|
||||||
},
|
},
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
Reminders: []time.Time{
|
ReminderDates: []time.Time{
|
||||||
time.Unix(1543626824, 0).In(loc),
|
time.Unix(1543626824, 0).In(loc),
|
||||||
},
|
},
|
||||||
|
Reminders: []*TaskReminder{
|
||||||
|
{
|
||||||
|
ID: 3,
|
||||||
|
TaskID: 2,
|
||||||
|
Reminder: time.Unix(1543626824, 0).In(loc),
|
||||||
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
|
},
|
||||||
|
},
|
||||||
Created: time.Unix(1543626724, 0).In(loc),
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
Updated: time.Unix(1543626724, 0).In(loc),
|
Updated: time.Unix(1543626724, 0).In(loc),
|
||||||
}
|
}
|
||||||
@ -472,15 +480,32 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
task27 := &Task{
|
task27 := &Task{
|
||||||
ID: 27,
|
ID: 27,
|
||||||
Title: "task #27 with reminders",
|
Title: "task #27 with reminders and start_date",
|
||||||
Identifier: "test1-12",
|
Identifier: "test1-12",
|
||||||
Index: 12,
|
Index: 12,
|
||||||
CreatedByID: 1,
|
CreatedByID: 1,
|
||||||
CreatedBy: user1,
|
CreatedBy: user1,
|
||||||
Reminders: []time.Time{
|
ReminderDates: []time.Time{
|
||||||
time.Unix(1543626724, 0).In(loc),
|
time.Unix(1543626724, 0).In(loc),
|
||||||
time.Unix(1543626824, 0).In(loc),
|
time.Unix(1543626824, 0).In(loc),
|
||||||
},
|
},
|
||||||
|
Reminders: []*TaskReminder{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
TaskID: 27,
|
||||||
|
Reminder: time.Unix(1543626724, 0).In(loc),
|
||||||
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
TaskID: 27,
|
||||||
|
Reminder: time.Unix(1543626824, 0).In(loc),
|
||||||
|
Created: time.Unix(1543626724, 0).In(loc),
|
||||||
|
RelativePeriod: -3600,
|
||||||
|
RelativeTo: "start_date",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StartDate: time.Unix(1543616724, 0).In(loc),
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
BucketID: 1,
|
BucketID: 1,
|
||||||
RelatedTasks: map[RelationKind][]*Task{},
|
RelatedTasks: map[RelationKind][]*Task{},
|
||||||
@ -906,7 +931,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "filtered reminders",
|
name: "filtered reminder dates",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
FilterBy: []string{"reminders", "reminders"},
|
FilterBy: []string{"reminders", "reminders"},
|
||||||
FilterValue: []string{"2018-10-01T00:00:00+00:00", "2018-12-10T00:00:00+00:00"},
|
FilterValue: []string{"2018-10-01T00:00:00+00:00", "2018-12-10T00:00:00+00:00"},
|
||||||
@ -1246,7 +1271,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Errorf("Test %s, Task.ReadAll() = %v, want %v, \ndiff: %v", tt.name, got, tt.want, diff)
|
t.Errorf("Test %s, Task.ReadAll() = %v, \nwant %v, \ndiff: %v", tt.name, got, tt.want, diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,29 @@ import (
|
|||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskReminder holds a reminder on a task
|
// ReminderRelation represents the date attribute of the task which a period based reminder relates to
|
||||||
|
type ReminderRelation string
|
||||||
|
|
||||||
|
// All valid ReminderRelations
|
||||||
|
const (
|
||||||
|
ReminderRelationDueDate ReminderRelation = `due_date`
|
||||||
|
ReminderRelationStartDate ReminderRelation = `start_date`
|
||||||
|
ReminderRelationEndDate ReminderRelation = `end_date`
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskReminder holds a reminder on a task.
|
||||||
|
// If RelativeTo and the assciated date field are defined, then the attribute Reminder will be computed.
|
||||||
|
// If RelativeTo is missing, than Reminder must be given.
|
||||||
type TaskReminder struct {
|
type TaskReminder struct {
|
||||||
ID int64 `xorm:"bigint autoincr not null unique pk"`
|
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
|
||||||
TaskID int64 `xorm:"bigint not null INDEX"`
|
TaskID int64 `xorm:"bigint not null INDEX" json:"-"`
|
||||||
Reminder time.Time `xorm:"DATETIME not null INDEX 'reminder'"`
|
// The absolute time when the user wants to be reminded of the task.
|
||||||
Created time.Time `xorm:"created not null"`
|
Reminder time.Time `xorm:"DATETIME not null INDEX 'reminder'" json:"reminder"`
|
||||||
|
Created time.Time `xorm:"created not null" json:"-"`
|
||||||
|
// A period in seconds relative to another date argument. Negative values mean the reminder triggers before the date. Default: 0, tiggers when RelativeTo is due.
|
||||||
|
RelativePeriod int64 `xorm:"bigint null" json:"relative_period"`
|
||||||
|
// The name of the date field to which the relative period refers to.
|
||||||
|
RelativeTo ReminderRelation `xorm:"varchar(50) null" json:"relative_to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns a pretty table name
|
// TableName returns a pretty table name
|
||||||
|
@ -62,7 +62,11 @@ type Task struct {
|
|||||||
// The time when the task is due.
|
// The time when the task is due.
|
||||||
DueDate time.Time `xorm:"DATETIME INDEX null 'due_date'" 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 []time.Time `xorm:"-" json:"reminder_dates"`
|
//
|
||||||
|
// Deprecated: Use Reminders
|
||||||
|
ReminderDates []time.Time `xorm:"-" json:"reminder_dates"`
|
||||||
|
// An array of reminders that are associated with this task.
|
||||||
|
Reminders []*TaskReminder `xorm:"-" json:"reminders"`
|
||||||
// The project this task belongs to.
|
// The project this task belongs to.
|
||||||
ProjectID int64 `xorm:"bigint INDEX not null" json:"project_id" param:"project"`
|
ProjectID int64 `xorm:"bigint INDEX not null" json:"project_id" param:"project"`
|
||||||
// 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.
|
||||||
@ -641,8 +645,8 @@ func addAttachmentsToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTaskReminderMap(s *xorm.Session, taskIDs []int64) (taskReminders map[int64][]time.Time, err error) {
|
func getTaskReminderMap(s *xorm.Session, taskIDs []int64) (taskReminders map[int64][]*TaskReminder, err error) {
|
||||||
taskReminders = make(map[int64][]time.Time)
|
taskReminders = make(map[int64][]*TaskReminder)
|
||||||
|
|
||||||
// Get all reminders and put them in a map to have it easier later
|
// Get all reminders and put them in a map to have it easier later
|
||||||
reminders, err := getRemindersForTasks(s, taskIDs)
|
reminders, err := getRemindersForTasks(s, taskIDs)
|
||||||
@ -651,7 +655,7 @@ func getTaskReminderMap(s *xorm.Session, taskIDs []int64) (taskReminders map[int
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range reminders {
|
for _, r := range reminders {
|
||||||
taskReminders[r.TaskID] = append(taskReminders[r.TaskID], r.Reminder)
|
taskReminders[r.TaskID] = append(taskReminders[r.TaskID], r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -772,6 +776,11 @@ func addMoreInfoToTasks(s *xorm.Session, taskMap map[int64]*Task, a web.Auth) (e
|
|||||||
// Make created by user objects
|
// Make created by user objects
|
||||||
task.CreatedBy = users[task.CreatedByID]
|
task.CreatedBy = users[task.CreatedByID]
|
||||||
|
|
||||||
|
// Add the reminder dates (Remove, when ReminderDates is removed)
|
||||||
|
for _, r := range taskReminders[task.ID] {
|
||||||
|
task.ReminderDates = append(task.ReminderDates, r.Reminder)
|
||||||
|
}
|
||||||
|
|
||||||
// Add the reminders
|
// Add the reminders
|
||||||
task.Reminders = taskReminders[task.ID]
|
task.Reminders = taskReminders[task.ID]
|
||||||
|
|
||||||
@ -965,7 +974,7 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the reminders
|
// Update the reminders
|
||||||
if err := t.updateReminders(s, t.Reminders); err != nil {
|
if err := t.updateReminders(s, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1017,15 +1026,19 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||||||
t.ProjectID = ot.ProjectID
|
t.ProjectID = ot.ProjectID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the reminders
|
// Get the stored reminders
|
||||||
reminders, err := getRemindersForTasks(s, []int64{t.ID})
|
reminders, err := getRemindersForTasks(s, []int64{t.ID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ot.Reminders = make([]time.Time, len(reminders))
|
// Old task has the stored reminders
|
||||||
|
ot.Reminders = reminders
|
||||||
|
|
||||||
|
// Deprecated: remove when ReminderDates is removed
|
||||||
|
ot.ReminderDates = make([]time.Time, len(reminders))
|
||||||
for i, r := range reminders {
|
for i, r := range reminders {
|
||||||
ot.Reminders[i] = r.Reminder
|
ot.ReminderDates[i] = r.Reminder
|
||||||
}
|
}
|
||||||
|
|
||||||
targetBucket, err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID)
|
targetBucket, err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID)
|
||||||
@ -1049,7 +1062,7 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the reminders
|
// Update the reminders
|
||||||
if err := ot.updateReminders(s, t.Reminders); err != nil {
|
if err := ot.updateReminders(s, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1331,9 +1344,9 @@ func setTaskDatesDefault(oldTask, newTask *Task) {
|
|||||||
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
||||||
if len(oldTask.Reminders) > 0 {
|
if len(oldTask.Reminders) > 0 {
|
||||||
for in, r := range oldTask.Reminders {
|
for in, r := range oldTask.Reminders {
|
||||||
newTask.Reminders[in] = r.Add(repeatDuration)
|
newTask.Reminders[in].Reminder = r.Reminder.Add(repeatDuration)
|
||||||
for !newTask.Reminders[in].After(now) {
|
for !newTask.Reminders[in].Reminder.After(now) {
|
||||||
newTask.Reminders[in] = newTask.Reminders[in].Add(repeatDuration)
|
newTask.Reminders[in].Reminder = newTask.Reminders[in].Reminder.Add(repeatDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1364,7 +1377,7 @@ func setTaskDatesMonthRepeat(oldTask, newTask *Task) {
|
|||||||
newTask.Reminders = oldTask.Reminders
|
newTask.Reminders = oldTask.Reminders
|
||||||
if len(oldTask.Reminders) > 0 {
|
if len(oldTask.Reminders) > 0 {
|
||||||
for in, r := range oldTask.Reminders {
|
for in, r := range oldTask.Reminders {
|
||||||
newTask.Reminders[in] = addOneMonthToDate(r)
|
newTask.Reminders[in].Reminder = addOneMonthToDate(r.Reminder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1405,12 +1418,12 @@ func setTaskDatesFromCurrentDateRepeat(oldTask, newTask *Task) {
|
|||||||
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
// To make this easier, we sort them first because we can then rely on the fact the first is the smallest
|
||||||
if len(oldTask.Reminders) > 0 {
|
if len(oldTask.Reminders) > 0 {
|
||||||
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
sort.Slice(oldTask.Reminders, func(i, j int) bool {
|
||||||
return oldTask.Reminders[i].Unix() < oldTask.Reminders[j].Unix()
|
return oldTask.Reminders[i].Reminder.Unix() < oldTask.Reminders[j].Reminder.Unix()
|
||||||
})
|
})
|
||||||
first := oldTask.Reminders[0]
|
first := oldTask.Reminders[0].Reminder
|
||||||
for in, r := range oldTask.Reminders {
|
for in, r := range oldTask.Reminders {
|
||||||
diff := r.Sub(first)
|
diff := r.Reminder.Sub(first)
|
||||||
newTask.Reminders[in] = now.Add(repeatDuration + diff)
|
newTask.Reminders[in].Reminder = now.Add(repeatDuration + diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1478,11 +1491,66 @@ func updateDone(oldTask *Task, newTask *Task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: will be removed when ReminderDates are removed from Task.
|
||||||
|
// For now the method just creates TaskReminder objects from the ReminderDates and overwrites Reminder.
|
||||||
|
func (t *Task) overwriteRemindersWithReminderDates(reminderDates []time.Time) {
|
||||||
|
// If the client still sends old reminder_dates, then these will overwrite
|
||||||
|
// the Reminders, if the were sent by the client, too.
|
||||||
|
// We assume that clients still using the old API with reminder_dates do not understand the new reminders.
|
||||||
|
// Clients who want to use the new Reminder structure must explicitey unset reminder_dates.
|
||||||
|
|
||||||
|
// start with empty Reminders
|
||||||
|
reminders := make([]*TaskReminder, 0)
|
||||||
|
|
||||||
|
// append absolute triggers from ReminderDates
|
||||||
|
for _, reminderDate := range reminderDates {
|
||||||
|
reminders = append(reminders, &TaskReminder{TaskID: t.ID, Reminder: reminderDate})
|
||||||
|
}
|
||||||
|
t.Reminders = reminders
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the absolute trigger dates for Reminders with relative period
|
||||||
|
func updateRelativeReminderDates(task *Task) (err error) {
|
||||||
|
for _, reminder := range task.Reminders {
|
||||||
|
relativeDuration := time.Duration(reminder.RelativePeriod) * time.Second
|
||||||
|
if reminder.RelativeTo != "" {
|
||||||
|
reminder.Reminder = time.Time{}
|
||||||
|
}
|
||||||
|
switch reminder.RelativeTo {
|
||||||
|
case ReminderRelationDueDate:
|
||||||
|
if !task.DueDate.IsZero() {
|
||||||
|
reminder.Reminder = task.DueDate.Add(relativeDuration)
|
||||||
|
}
|
||||||
|
case ReminderRelationStartDate:
|
||||||
|
if !task.StartDate.IsZero() {
|
||||||
|
reminder.Reminder = task.StartDate.Add(relativeDuration)
|
||||||
|
}
|
||||||
|
case ReminderRelationEndDate:
|
||||||
|
if !task.EndDate.IsZero() {
|
||||||
|
reminder.Reminder = task.EndDate.Add(relativeDuration)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if reminder.RelativePeriod != 0 {
|
||||||
|
err = ErrReminderRelativeToMissing{
|
||||||
|
TaskID: task.ID,
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Removes all old reminders and adds the new ones. This is a lot easier and less buggy than
|
// Removes all old reminders and adds the new ones. This is a lot easier and less buggy than
|
||||||
// trying to figure out which reminders changed and then only re-add those needed. And since it does
|
// trying to figure out which reminders changed and then only re-add those needed. And since it does
|
||||||
// not make a performance difference we'll just do that.
|
// not make a performance difference we'll just do that.
|
||||||
// The parameter is a slice with unix dates which holds the new reminders.
|
// The parameter is a slice which holds the new reminders.
|
||||||
func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err error) {
|
func (t *Task) updateReminders(s *xorm.Session, task *Task) (err error) {
|
||||||
|
|
||||||
|
// Deprecated: This statement must be removed when ReminderDates will be removed
|
||||||
|
if task.ReminderDates != nil {
|
||||||
|
task.overwriteRemindersWithReminderDates(task.ReminderDates)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = s.
|
_, err = s.
|
||||||
Where("task_id = ?", t.ID).
|
Where("task_id = ?", t.ID).
|
||||||
@ -1491,23 +1559,43 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve duplicates and sort them
|
err = updateRelativeReminderDates(task)
|
||||||
reminderMap := make(map[int64]time.Time, len(reminders))
|
if err != nil {
|
||||||
for _, reminder := range reminders {
|
return
|
||||||
reminderMap[reminder.UTC().Unix()] = reminder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve duplicates and sort them
|
||||||
|
reminderMap := make(map[int64]*TaskReminder, len(task.Reminders))
|
||||||
|
for _, reminder := range task.Reminders {
|
||||||
|
reminderMap[reminder.Reminder.UTC().Unix()] = reminder
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Reminders = make([]*TaskReminder, 0, len(reminderMap))
|
||||||
|
t.ReminderDates = make([]time.Time, 0, len(reminderMap))
|
||||||
|
|
||||||
// Loop through all reminders and add them
|
// Loop through all reminders and add them
|
||||||
for _, r := range reminderMap {
|
for _, r := range reminderMap {
|
||||||
_, err = s.Insert(&TaskReminder{TaskID: t.ID, Reminder: r})
|
taskReminder := &TaskReminder{
|
||||||
|
TaskID: t.ID,
|
||||||
|
Reminder: r.Reminder,
|
||||||
|
RelativePeriod: r.RelativePeriod,
|
||||||
|
RelativeTo: r.RelativeTo}
|
||||||
|
_, err = s.Insert(taskReminder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
t.Reminders = append(t.Reminders, taskReminder)
|
||||||
|
t.ReminderDates = append(t.ReminderDates, taskReminder.Reminder)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Reminders = reminders
|
// sort reminders
|
||||||
if len(reminders) == 0 {
|
sort.Slice(t.Reminders, func(i, j int) bool {
|
||||||
|
return t.Reminders[i].Reminder.Before(t.Reminders[j].Reminder)
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(t.Reminders) == 0 {
|
||||||
t.Reminders = nil
|
t.Reminders = nil
|
||||||
|
t.ReminderDates = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = updateProjectLastUpdated(s, &Project{ID: t.ProjectID})
|
err = updateProjectLastUpdated(s, &Project{ID: t.ProjectID})
|
||||||
|
@ -70,6 +70,48 @@ func TestTask_Create(t *testing.T) {
|
|||||||
|
|
||||||
events.AssertDispatched(t, &TaskCreatedEvent{})
|
events.AssertDispatched(t, &TaskCreatedEvent{})
|
||||||
})
|
})
|
||||||
|
t.Run("with reminders", func(t *testing.T) {
|
||||||
|
db.LoadAndAssertFixtures(t)
|
||||||
|
s := db.NewSession()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
task := &Task{
|
||||||
|
Title: "Lorem",
|
||||||
|
Description: "Lorem Ipsum Dolor",
|
||||||
|
ProjectID: 1,
|
||||||
|
DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local),
|
||||||
|
StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local),
|
||||||
|
EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local),
|
||||||
|
Reminders: []*TaskReminder{
|
||||||
|
{
|
||||||
|
RelativeTo: "due_date",
|
||||||
|
RelativePeriod: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RelativeTo: "start_date",
|
||||||
|
RelativePeriod: -2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RelativeTo: "end_date",
|
||||||
|
RelativePeriod: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := task.Create(s, usr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.Local), task.Reminders[0].Reminder)
|
||||||
|
assert.Equal(t, int64(1), task.Reminders[0].RelativePeriod)
|
||||||
|
assert.Equal(t, ReminderRelationDueDate, task.Reminders[0].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.Local), task.Reminders[1].Reminder)
|
||||||
|
assert.Equal(t, ReminderRelationStartDate, task.Reminders[1].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.Local), task.Reminders[2].Reminder)
|
||||||
|
assert.Equal(t, ReminderRelationEndDate, task.Reminders[2].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), task.Reminders[3].Reminder)
|
||||||
|
err = s.Commit()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
t.Run("empty title", func(t *testing.T) {
|
t.Run("empty title", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
@ -98,7 +140,7 @@ func TestTask_Create(t *testing.T) {
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrProjectDoesNotExist(err))
|
assert.True(t, IsErrProjectDoesNotExist(err))
|
||||||
})
|
})
|
||||||
t.Run("noneixtant user", func(t *testing.T) {
|
t.Run("nonexistant user", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
@ -368,7 +410,51 @@ func TestTask_Update(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(3), task.Index)
|
assert.Equal(t, int64(3), task.Index)
|
||||||
})
|
})
|
||||||
t.Run("the same date multiple times should be saved once", func(t *testing.T) {
|
|
||||||
|
t.Run("reminders will be updated", func(t *testing.T) {
|
||||||
|
db.LoadAndAssertFixtures(t)
|
||||||
|
s := db.NewSession()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
task := &Task{
|
||||||
|
ID: 1,
|
||||||
|
ProjectID: 1,
|
||||||
|
Title: "test",
|
||||||
|
DueDate: time.Date(2023, time.March, 7, 22, 5, 0, 0, time.Local),
|
||||||
|
StartDate: time.Date(2023, time.March, 7, 22, 5, 10, 0, time.Local),
|
||||||
|
EndDate: time.Date(2023, time.March, 7, 22, 5, 20, 0, time.Local),
|
||||||
|
Reminders: []*TaskReminder{
|
||||||
|
{
|
||||||
|
RelativeTo: "due_date",
|
||||||
|
RelativePeriod: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RelativeTo: "start_date",
|
||||||
|
RelativePeriod: -2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RelativeTo: "end_date",
|
||||||
|
RelativePeriod: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reminder: time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := task.Update(s, u)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 1, 0, time.Local), task.Reminders[0].Reminder)
|
||||||
|
assert.Equal(t, int64(1), task.Reminders[0].RelativePeriod)
|
||||||
|
assert.Equal(t, ReminderRelationDueDate, task.Reminders[0].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 8, 0, time.Local), task.Reminders[1].Reminder)
|
||||||
|
assert.Equal(t, ReminderRelationStartDate, task.Reminders[1].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 22, 5, 19, 0, time.Local), task.Reminders[2].Reminder)
|
||||||
|
assert.Equal(t, ReminderRelationEndDate, task.Reminders[2].RelativeTo)
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 7, 23, 0, 0, 0, time.Local), task.Reminders[3].Reminder)
|
||||||
|
err = s.Commit()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
db.AssertCount(t, "task_reminders", builder.Eq{"task_id": 1}, 4)
|
||||||
|
})
|
||||||
|
t.Run("the same reminder multiple times should be saved once", func(t *testing.T) {
|
||||||
db.LoadAndAssertFixtures(t)
|
db.LoadAndAssertFixtures(t)
|
||||||
s := db.NewSession()
|
s := db.NewSession()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
@ -376,9 +462,13 @@ func TestTask_Update(t *testing.T) {
|
|||||||
task := &Task{
|
task := &Task{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Title: "test",
|
Title: "test",
|
||||||
Reminders: []time.Time{
|
Reminders: []*TaskReminder{
|
||||||
time.Unix(1674745156, 0),
|
{
|
||||||
time.Unix(1674745156, 223),
|
Reminder: time.Unix(1674745156, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reminder: time.Unix(1674745156, 223),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ProjectID: 1,
|
ProjectID: 1,
|
||||||
}
|
}
|
||||||
@ -386,9 +476,42 @@ func TestTask_Update(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = s.Commit()
|
err = s.Commit()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
db.AssertCount(t, "task_reminders", builder.Eq{"task_id": 1}, 1)
|
db.AssertCount(t, "task_reminders", builder.Eq{"task_id": 1}, 1)
|
||||||
})
|
})
|
||||||
|
t.Run("update relative reminder when start_date changes", func(t *testing.T) {
|
||||||
|
db.LoadAndAssertFixtures(t)
|
||||||
|
s := db.NewSession()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
// given task with start_date and relative reminder for start_date
|
||||||
|
taskBefore := &Task{
|
||||||
|
Title: "test",
|
||||||
|
ProjectID: 1,
|
||||||
|
StartDate: time.Date(2022, time.March, 8, 8, 5, 20, 0, time.Local),
|
||||||
|
Reminders: []*TaskReminder{
|
||||||
|
{
|
||||||
|
RelativeTo: "start_date",
|
||||||
|
RelativePeriod: -60,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := taskBefore.Create(s, u)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = s.Commit()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, time.Date(2022, time.March, 8, 8, 4, 20, 0, time.Local), taskBefore.Reminders[0].Reminder)
|
||||||
|
|
||||||
|
// when start_date is modified
|
||||||
|
task := taskBefore
|
||||||
|
task.StartDate = time.Date(2023, time.March, 8, 8, 5, 0, 0, time.Local)
|
||||||
|
task.ReminderDates = nil
|
||||||
|
err = task.Update(s, u)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// then reminder time is updated
|
||||||
|
assert.Equal(t, time.Date(2023, time.March, 8, 8, 4, 0, 0, time.Local), task.Reminders[0].Reminder)
|
||||||
|
err = s.Commit()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTask_Delete(t *testing.T) {
|
func TestTask_Delete(t *testing.T) {
|
||||||
@ -487,9 +610,13 @@ func TestUpdateDone(t *testing.T) {
|
|||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
Reminders: []time.Time{
|
Reminders: []*TaskReminder{
|
||||||
time.Unix(1550000000, 0),
|
{
|
||||||
time.Unix(1555000000, 0),
|
Reminder: time.Unix(1550000000, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reminder: time.Unix(1555000000, 0),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
@ -507,8 +634,8 @@ func TestUpdateDone(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, 2)
|
assert.Len(t, newTask.Reminders, 2)
|
||||||
assert.Equal(t, expected1, newTask.Reminders[0])
|
assert.Equal(t, expected1, newTask.Reminders[0].Reminder)
|
||||||
assert.Equal(t, expected2, newTask.Reminders[1])
|
assert.Equal(t, expected2, newTask.Reminders[1].Reminder)
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
t.Run("update start date", func(t *testing.T) {
|
t.Run("update start date", func(t *testing.T) {
|
||||||
@ -585,22 +712,25 @@ func TestUpdateDone(t *testing.T) {
|
|||||||
Done: false,
|
Done: false,
|
||||||
RepeatAfter: 8600,
|
RepeatAfter: 8600,
|
||||||
RepeatMode: TaskRepeatModeFromCurrentDate,
|
RepeatMode: TaskRepeatModeFromCurrentDate,
|
||||||
Reminders: []time.Time{
|
Reminders: []*TaskReminder{
|
||||||
time.Unix(1550000000, 0),
|
{
|
||||||
time.Unix(1555000000, 0),
|
Reminder: time.Unix(1550000000, 0),
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
|
Reminder: time.Unix(1555000000, 0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
diff := oldTask.Reminders[1].Sub(oldTask.Reminders[0])
|
diff := oldTask.Reminders[1].Reminder.Sub(oldTask.Reminders[0].Reminder)
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, 2)
|
assert.Len(t, newTask.Reminders, 2)
|
||||||
// Only comparing unix timestamps because time.Time use nanoseconds which can't ever possibly have the same value
|
// 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.Reminders[0].Unix())
|
assert.Equal(t, time.Now().Add(time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[0].Reminder.Unix())
|
||||||
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[1].Unix())
|
assert.Equal(t, time.Now().Add(diff+time.Duration(oldTask.RepeatAfter)*time.Second).Unix(), newTask.Reminders[1].Reminder.Unix())
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
t.Run("start date", func(t *testing.T) {
|
t.Run("start date", func(t *testing.T) {
|
||||||
@ -678,23 +808,28 @@ func TestUpdateDone(t *testing.T) {
|
|||||||
oldTask := &Task{
|
oldTask := &Task{
|
||||||
Done: false,
|
Done: false,
|
||||||
RepeatMode: TaskRepeatModeMonth,
|
RepeatMode: TaskRepeatModeMonth,
|
||||||
Reminders: []time.Time{
|
Reminders: []*TaskReminder{
|
||||||
time.Unix(1550000000, 0),
|
{
|
||||||
time.Unix(1555000000, 0),
|
Reminder: time.Unix(1550000000, 0),
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
|
Reminder: time.Unix(1555000000, 0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
newTask := &Task{
|
newTask := &Task{
|
||||||
Done: true,
|
Done: true,
|
||||||
}
|
}
|
||||||
oldReminders := make([]time.Time, len(oldTask.Reminders))
|
oldReminders := make([]time.Time, len(oldTask.Reminders))
|
||||||
copy(oldReminders, oldTask.Reminders)
|
for i, r := range newTask.Reminders {
|
||||||
|
oldReminders[i] = r.Reminder
|
||||||
|
}
|
||||||
|
|
||||||
updateDone(oldTask, newTask)
|
updateDone(oldTask, newTask)
|
||||||
|
|
||||||
assert.Len(t, newTask.Reminders, len(oldReminders))
|
assert.Len(t, newTask.Reminders, len(oldReminders))
|
||||||
for i, r := range newTask.Reminders {
|
for i, r := range newTask.Reminders {
|
||||||
assert.True(t, r.After(oldReminders[i]))
|
assert.True(t, r.Reminder.After(oldReminders[i]))
|
||||||
assert.NotEqual(t, oldReminders[i].Month(), r.Month())
|
assert.NotEqual(t, oldReminders[i].Month(), r.Reminder.Month())
|
||||||
}
|
}
|
||||||
assert.False(t, newTask.Done)
|
assert.False(t, newTask.Done)
|
||||||
})
|
})
|
||||||
|
@ -329,7 +329,9 @@ func convertMicrosoftTodoData(todoData []*project) (vikunjsStructure []*models.N
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
task.Reminders = []time.Time{reminder}
|
task.Reminders = []*models.TaskReminder{
|
||||||
|
{Reminder: reminder},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due Date
|
// Due Date
|
||||||
|
@ -141,8 +141,10 @@ func TestConverting(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Task: models.Task{
|
Task: models.Task{
|
||||||
Title: "Task 5",
|
Title: "Task 5",
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
testtimeTime,
|
{
|
||||||
|
Reminder: testtimeTime,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -147,8 +147,11 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.Namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !t.DueDate.IsZero() && t.Reminder > 0 {
|
if !t.DueDate.IsZero() && t.Reminder > 0 {
|
||||||
task.Task.Reminders = []time.Time{
|
task.Task.Reminders = []*models.TaskReminder{
|
||||||
t.DueDate.Add(t.Reminder * -1),
|
{
|
||||||
|
RelativeTo: models.ReminderRelationDueDate,
|
||||||
|
RelativePeriod: int64((t.Reminder * -1).Seconds()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,8 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
|
|||||||
{Title: "label1"},
|
{Title: "label1"},
|
||||||
{Title: "label2"},
|
{Title: "label2"},
|
||||||
})
|
})
|
||||||
//assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
|
||||||
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders[0].RelativePeriod, int64(-24*3600))
|
||||||
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Position, tickTickTasks[0].Order)
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Position, tickTickTasks[0].Order)
|
||||||
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Done, false)
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Done, false)
|
||||||
|
|
||||||
@ -127,7 +128,8 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
|
|||||||
{Title: "label2"},
|
{Title: "label2"},
|
||||||
{Title: "other label"},
|
{Title: "other label"},
|
||||||
})
|
})
|
||||||
//assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
|
||||||
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Reminders[0].RelativePeriod, int64(-24*3600))
|
||||||
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Position, tickTickTasks[2].Order)
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Position, tickTickTasks[2].Order)
|
||||||
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Done, false)
|
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Done, false)
|
||||||
|
|
||||||
|
@ -471,7 +471,10 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks[r.ItemID].Reminders = append(tasks[r.ItemID].Reminders, date.In(config.GetTimeZone()))
|
tasks[r.ItemID].Reminders = append(tasks[r.ItemID].Reminders, &models.TaskReminder{
|
||||||
|
Reminder: date.In(config.GetTimeZone()),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*models.NamespaceWithProjectsAndTasks{
|
return []*models.NamespaceWithProjectsAndTasks{
|
||||||
|
@ -388,9 +388,9 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
|||||||
Description: "Lorem Ipsum dolor sit amet",
|
Description: "Lorem Ipsum dolor sit amet",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
time.Date(2020, time.June, 15, 23, 59, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.June, 15, 23, 59, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
time.Date(2020, time.June, 16, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.June, 16, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -407,8 +407,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
|||||||
Title: "Task400000002",
|
Title: "Task400000002",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
time.Date(2020, time.July, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.July, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -421,8 +421,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
|||||||
Created: time1,
|
Created: time1,
|
||||||
DoneAt: time3,
|
DoneAt: time3,
|
||||||
Labels: vikunjaLabels,
|
Labels: vikunjaLabels,
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -441,8 +441,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
|||||||
DueDate: dueTime,
|
DueDate: dueTime,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
DoneAt: time3,
|
DoneAt: time3,
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -531,8 +531,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
|
|||||||
Title: "Task400000009",
|
Title: "Task400000009",
|
||||||
Done: false,
|
Done: false,
|
||||||
Created: time1,
|
Created: time1,
|
||||||
Reminders: []time.Time{
|
Reminders: []*models.TaskReminder{
|
||||||
time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone()),
|
{Reminder: time.Date(2020, time.June, 15, 7, 0, 0, 0, time.UTC).In(config.GetTimeZone())},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// Package swagger GENERATED BY SWAG; DO NOT EDIT
|
// Code generated by swaggo/swag. DO NOT EDIT
|
||||||
// This file was generated by swaggo/swag
|
|
||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
import "github.com/swaggo/swag"
|
import "github.com/swaggo/swag"
|
||||||
@ -7791,12 +7790,19 @@ const docTemplate = `{
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reminder_dates": {
|
"reminder_dates": {
|
||||||
"description": "An array of datetimes when the user wants to be reminded of the task.",
|
"description": "An array of datetimes when the user wants to be reminded of the task.\n\nDeprecated: Use Reminders",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reminders": {
|
||||||
|
"description": "An array of reminders that are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.TaskReminder"
|
||||||
|
}
|
||||||
|
},
|
||||||
"repeat_after": {
|
"repeat_after": {
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -8303,6 +8309,19 @@ const docTemplate = `{
|
|||||||
"RelationKindCopiedTo"
|
"RelationKindCopiedTo"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"models.ReminderRelation": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"due_date",
|
||||||
|
"start_date",
|
||||||
|
"end_date"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ReminderRelationDueDate",
|
||||||
|
"ReminderRelationStartDate",
|
||||||
|
"ReminderRelationEndDate"
|
||||||
|
]
|
||||||
|
},
|
||||||
"models.Right": {
|
"models.Right": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
@ -8518,12 +8537,19 @@ const docTemplate = `{
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reminder_dates": {
|
"reminder_dates": {
|
||||||
"description": "An array of datetimes when the user wants to be reminded of the task.",
|
"description": "An array of datetimes when the user wants to be reminded of the task.\n\nDeprecated: Use Reminders",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reminders": {
|
||||||
|
"description": "An array of reminders that are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.TaskReminder"
|
||||||
|
}
|
||||||
|
},
|
||||||
"repeat_after": {
|
"repeat_after": {
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -8691,6 +8717,27 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.TaskReminder": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"relative_period": {
|
||||||
|
"description": "A period in seconds relative to another date argument. Negative values mean the reminder triggers before the date. Default: 0, tiggers when RelativeTo is due.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"relative_to": {
|
||||||
|
"description": "The name of the date field to which the relative period refers to.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/models.ReminderRelation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reminder": {
|
||||||
|
"description": "The absolute time when the user wants to be reminded of the task.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.TaskRepeatMode": {
|
"models.TaskRepeatMode": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
@ -7782,12 +7782,19 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reminder_dates": {
|
"reminder_dates": {
|
||||||
"description": "An array of datetimes when the user wants to be reminded of the task.",
|
"description": "An array of datetimes when the user wants to be reminded of the task.\n\nDeprecated: Use Reminders",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reminders": {
|
||||||
|
"description": "An array of reminders that are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.TaskReminder"
|
||||||
|
}
|
||||||
|
},
|
||||||
"repeat_after": {
|
"repeat_after": {
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -8294,6 +8301,19 @@
|
|||||||
"RelationKindCopiedTo"
|
"RelationKindCopiedTo"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"models.ReminderRelation": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"due_date",
|
||||||
|
"start_date",
|
||||||
|
"end_date"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ReminderRelationDueDate",
|
||||||
|
"ReminderRelationStartDate",
|
||||||
|
"ReminderRelationEndDate"
|
||||||
|
]
|
||||||
|
},
|
||||||
"models.Right": {
|
"models.Right": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
@ -8509,12 +8529,19 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reminder_dates": {
|
"reminder_dates": {
|
||||||
"description": "An array of datetimes when the user wants to be reminded of the task.",
|
"description": "An array of datetimes when the user wants to be reminded of the task.\n\nDeprecated: Use Reminders",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reminders": {
|
||||||
|
"description": "An array of reminders that are associated with this task.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.TaskReminder"
|
||||||
|
}
|
||||||
|
},
|
||||||
"repeat_after": {
|
"repeat_after": {
|
||||||
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
"description": "An amount in seconds this task repeats itself. If this is set, when marking the task as done, it will mark itself as \"undone\" and then increase all remindes and the due date by its amount.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -8682,6 +8709,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.TaskReminder": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"relative_period": {
|
||||||
|
"description": "A period in seconds relative to another date argument. Negative values mean the reminder triggers before the date. Default: 0, tiggers when RelativeTo is due.",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"relative_to": {
|
||||||
|
"description": "The name of the date field to which the relative period refers to.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/models.ReminderRelation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reminder": {
|
||||||
|
"description": "The absolute time when the user wants to be reminded of the task.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.TaskRepeatMode": {
|
"models.TaskRepeatMode": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
@ -196,11 +196,18 @@ definitions:
|
|||||||
- $ref: '#/definitions/models.RelatedTaskMap'
|
- $ref: '#/definitions/models.RelatedTaskMap'
|
||||||
description: All related tasks, grouped by their relation kind
|
description: All related tasks, grouped by their relation kind
|
||||||
reminder_dates:
|
reminder_dates:
|
||||||
description: An array of datetimes when the user wants to be reminded of the
|
description: |-
|
||||||
task.
|
An array of datetimes when the user wants to be reminded of the task.
|
||||||
|
|
||||||
|
Deprecated: Use Reminders
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
|
reminders:
|
||||||
|
description: An array of reminders that are associated with this task.
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.TaskReminder'
|
||||||
|
type: array
|
||||||
repeat_after:
|
repeat_after:
|
||||||
description: An amount in seconds this task repeats itself. If this is set,
|
description: An amount in seconds this task repeats itself. If this is set,
|
||||||
when marking the task as done, it will mark itself as "undone" and then
|
when marking the task as done, it will mark itself as "undone" and then
|
||||||
@ -596,6 +603,16 @@ definitions:
|
|||||||
- RelationKindFollows
|
- RelationKindFollows
|
||||||
- RelationKindCopiedFrom
|
- RelationKindCopiedFrom
|
||||||
- RelationKindCopiedTo
|
- RelationKindCopiedTo
|
||||||
|
models.ReminderRelation:
|
||||||
|
enum:
|
||||||
|
- due_date
|
||||||
|
- start_date
|
||||||
|
- end_date
|
||||||
|
type: string
|
||||||
|
x-enum-varnames:
|
||||||
|
- ReminderRelationDueDate
|
||||||
|
- ReminderRelationStartDate
|
||||||
|
- ReminderRelationEndDate
|
||||||
models.Right:
|
models.Right:
|
||||||
enum:
|
enum:
|
||||||
- 0
|
- 0
|
||||||
@ -763,11 +780,18 @@ definitions:
|
|||||||
- $ref: '#/definitions/models.RelatedTaskMap'
|
- $ref: '#/definitions/models.RelatedTaskMap'
|
||||||
description: All related tasks, grouped by their relation kind
|
description: All related tasks, grouped by their relation kind
|
||||||
reminder_dates:
|
reminder_dates:
|
||||||
description: An array of datetimes when the user wants to be reminded of the
|
description: |-
|
||||||
task.
|
An array of datetimes when the user wants to be reminded of the task.
|
||||||
|
|
||||||
|
Deprecated: Use Reminders
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
|
reminders:
|
||||||
|
description: An array of reminders that are associated with this task.
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.TaskReminder'
|
||||||
|
type: array
|
||||||
repeat_after:
|
repeat_after:
|
||||||
description: An amount in seconds this task repeats itself. If this is set,
|
description: An amount in seconds this task repeats itself. If this is set,
|
||||||
when marking the task as done, it will mark itself as "undone" and then
|
when marking the task as done, it will mark itself as "undone" and then
|
||||||
@ -889,6 +913,22 @@ definitions:
|
|||||||
description: The ID of the "base" task, the task which has a relation to another.
|
description: The ID of the "base" task, the task which has a relation to another.
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
models.TaskReminder:
|
||||||
|
properties:
|
||||||
|
relative_period:
|
||||||
|
description: 'A period in seconds relative to another date argument. Negative
|
||||||
|
values mean the reminder triggers before the date. Default: 0, tiggers when
|
||||||
|
RelativeTo is due.'
|
||||||
|
type: integer
|
||||||
|
relative_to:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/models.ReminderRelation'
|
||||||
|
description: The name of the date field to which the relative period refers
|
||||||
|
to.
|
||||||
|
reminder:
|
||||||
|
description: The absolute time when the user wants to be reminded of the task.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
models.TaskRepeatMode:
|
models.TaskRepeatMode:
|
||||||
enum:
|
enum:
|
||||||
- 0
|
- 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user