feat(tasks): add GetTaskByProjectAndIndex resolver
This commit is contained in:
parent
ced7ebd97f
commit
8f9b50bdcb
|
|
@ -99,7 +99,7 @@ type Task struct {
|
|||
// The task identifier, based on the project identifier and the task's index
|
||||
Identifier string `xorm:"-" json:"identifier"`
|
||||
// The task index, calculated per project
|
||||
Index int64 `xorm:"bigint not null default 0" json:"index"`
|
||||
Index int64 `xorm:"bigint not null default 0" json:"index" param:"index"`
|
||||
|
||||
// The UID is currently not used for anything other than CalDAV, which is why we don't expose it over json
|
||||
UID string `xorm:"varchar(250) null" json:"-"`
|
||||
|
|
@ -353,6 +353,41 @@ func GetTaskByIDSimple(s *xorm.Session, taskID int64) (task Task, err error) {
|
|||
return GetTaskSimple(s, &Task{ID: taskID})
|
||||
}
|
||||
|
||||
// GetTaskByProjectAndIndex returns a task by its per-project index.
|
||||
// Returns ErrTaskDoesNotExist if nothing matches.
|
||||
func GetTaskByProjectAndIndex(s *xorm.Session, projectID, index int64) (task Task, err error) {
|
||||
if projectID < 1 || index < 1 {
|
||||
return Task{}, ErrTaskDoesNotExist{}
|
||||
}
|
||||
|
||||
has, err := s.
|
||||
Where("project_id = ? AND `index` = ?", projectID, index).
|
||||
Get(&task)
|
||||
if err != nil {
|
||||
return Task{}, err
|
||||
}
|
||||
if !has {
|
||||
return Task{}, ErrTaskDoesNotExist{}
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// resolveIDFromProjectAndIndex populates t.ID from (ProjectID, Index) for the
|
||||
// by-index route, which binds project+index from the URL but not id. No-op
|
||||
// when id is already set.
|
||||
func (t *Task) resolveIDFromProjectAndIndex(s *xorm.Session) error {
|
||||
if t.ID != 0 || t.ProjectID < 1 || t.Index < 1 {
|
||||
return nil
|
||||
}
|
||||
resolved, err := GetTaskByProjectAndIndex(s, t.ProjectID, t.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.ID = resolved.ID
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTaskSimple returns a raw task without extra data
|
||||
func GetTaskSimple(s *xorm.Session, t *Task) (task Task, err error) {
|
||||
task = *t
|
||||
|
|
@ -1933,6 +1968,9 @@ func (t *Task) ReadOne(s *xorm.Session, a web.Auth) (err error) {
|
|||
|
||||
t.Expand = append(t.Expand, t.ExpandArr...)
|
||||
expand := t.Expand
|
||||
if err = t.resolveIDFromProjectAndIndex(s); err != nil {
|
||||
return
|
||||
}
|
||||
*t, err = GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ func (t *Task) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
|||
func (t *Task) CanRead(s *xorm.Session, a web.Auth) (canRead bool, maxPermission int, err error) {
|
||||
t.Expand = append(t.Expand, t.ExpandArr...)
|
||||
expand := t.Expand
|
||||
// Get the task, error out if it doesn't exist
|
||||
if err = t.resolveIDFromProjectAndIndex(s); err != nil {
|
||||
return
|
||||
}
|
||||
*t, err = GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1303,3 +1303,63 @@ func TestGetTasksByUIDs(t *testing.T) {
|
|||
assert.Equal(t, int64(1), tasks[0].ID, "only user 1's task should be returned")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTaskByProjectAndIndex(t *testing.T) {
|
||||
t.Run("existing task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task, err := GetTaskByProjectAndIndex(s, 1, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), task.ID)
|
||||
assert.Equal(t, "task #1", task.Title)
|
||||
})
|
||||
|
||||
t.Run("nonexistent index", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
_, err := GetTaskByProjectAndIndex(s, 1, 99999)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
|
||||
t.Run("wrong project", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Project 4 has no tasks at all.
|
||||
_, err := GetTaskByProjectAndIndex(s, 4, 1)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
|
||||
t.Run("index exists only in another project", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Project 2 has indexes 1 and 2; index 5 lives under project 1 (task 5).
|
||||
// A non-scoped WHERE clause would leak task 5 here.
|
||||
_, err := GetTaskByProjectAndIndex(s, 2, 5)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
|
||||
t.Run("invalid input", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
_, err := GetTaskByProjectAndIndex(s, 0, 1)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
|
||||
_, err = GetTaskByProjectAndIndex(s, 1, 0)
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue