vikunja-api/pkg/models/task_relation.go

246 lines
8.6 KiB
Go
Raw Normal View History

2020-12-29 02:04:20 +01:00
// Vikunja is a to-do list application to facilitate your life.
2021-02-02 20:19:13 +01:00
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
2019-09-25 20:44:41 +02:00
//
2020-12-29 02:04:20 +01:00
// This program is free software: you can redistribute it and/or modify
2020-12-23 16:41:52 +01:00
// it under the terms of the GNU Affero General Public Licensee as published by
2019-09-25 20:44:41 +02:00
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
2020-12-29 02:04:20 +01:00
// This program is distributed in the hope that it will be useful,
2019-09-25 20:44:41 +02:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020-12-23 16:41:52 +01:00
// GNU Affero General Public Licensee for more details.
2019-09-25 20:44:41 +02:00
//
2020-12-23 16:41:52 +01:00
// You should have received a copy of the GNU Affero General Public Licensee
2020-12-29 02:04:20 +01:00
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2019-09-25 20:44:41 +02:00
package models
import (
"time"
"xorm.io/builder"
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
"xorm.io/xorm"
"code.vikunja.io/api/pkg/user"
2019-09-25 20:44:41 +02:00
"code.vikunja.io/web"
)
// RelationKind represents a kind of relation between to tasks
type RelationKind string
// All valid relation kinds
const (
RelationKindUnknown RelationKind = `unknown`
RelationKindSubtask RelationKind = `subtask`
RelationKindParenttask RelationKind = `parenttask`
RelationKindRelated RelationKind = `related`
RelationKindDuplicateOf RelationKind = `duplicateof`
RelationKindDuplicates RelationKind = `duplicates`
RelationKindBlocking RelationKind = `blocking`
RelationKindBlocked RelationKind = `blocked`
RelationKindPreceeds RelationKind = `precedes`
RelationKindFollows RelationKind = `follows`
RelationKindCopiedFrom RelationKind = `copiedfrom`
RelationKindCopiedTo RelationKind = `copiedto`
)
/*
* The direction of the relation goes _from_ task_id -> other_task_id.
* The relation kind only tells us something about the relation in that direction, and NOT
* the other way around. This means each relation exists two times in the db, one for each
* relevant direction.
* This design allows to easily do things like "Give me every relation for this task" whithout having
* to deal with each possible case of relation. Instead, it would just give me every relation record
* which has task_id set to the task ID I care about.
*
* For example, when I create a relation where I define task 2 as a subtask of task 1, it would actually
* create two relations. One from Task 2 -> Task 1 with relation kind subtask and one from Task 1 -> Task 2
* with relation kind parent task.
* When I now want to have all relations task 1 is a part of, I just ask "Give me all relations where
* task_id = 1".
*/
func (rk RelationKind) isValid() bool {
return rk == RelationKindSubtask ||
rk == RelationKindParenttask ||
rk == RelationKindRelated ||
rk == RelationKindDuplicateOf ||
rk == RelationKindDuplicates ||
rk == RelationKindBlocked ||
rk == RelationKindBlocking ||
rk == RelationKindPreceeds ||
rk == RelationKindFollows ||
rk == RelationKindCopiedFrom ||
rk == RelationKindCopiedTo
}
// TaskRelation represents a kind of relation between two tasks
type TaskRelation struct {
// The unique, numeric id of this relation.
ID int64 `xorm:"bigint autoincr not null unique pk" json:"-"`
2019-09-25 20:44:41 +02:00
// The ID of the "base" task, the task which has a relation to another.
TaskID int64 `xorm:"bigint not null" json:"task_id" param:"task"`
2019-09-25 20:44:41 +02:00
// The ID of the other task, the task which is being related.
OtherTaskID int64 `xorm:"bigint not null" json:"other_task_id" param:"otherTask"`
2019-09-25 20:44:41 +02:00
// The kind of the relation.
RelationKind RelationKind `xorm:"varchar(50) not null" json:"relation_kind" param:"relationKind"`
2019-09-25 20:44:41 +02:00
CreatedByID int64 `xorm:"bigint not null" json:"-"`
2019-09-25 20:44:41 +02:00
// The user who created this relation
CreatedBy *user.User `xorm:"-" json:"created_by"`
2019-09-25 20:44:41 +02:00
// A timestamp when this label was created. You cannot change this value.
Created time.Time `xorm:"created not null" json:"created"`
2019-09-25 20:44:41 +02:00
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
// TableName holds the table name for the task relation table
func (TaskRelation) TableName() string {
return "task_relations"
}
// RelatedTaskMap holds all relations of a single task, grouped by relation kind.
// This avoids the need for an extra type TaskWithRelation (or similar).
type RelatedTaskMap map[RelationKind][]*Task
// Create creates a new task relation
// @Summary Create a new relation between two tasks
// @Description Creates a new relation between two tasks. The user needs to have update rights on the base task and at least read rights on the other task. Both tasks do not need to be on the same list. Take a look at the docs for available task relation kinds.
// @tags task
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param relation body models.TaskRelation true "The relation object"
// @Param taskID path int true "Task ID"
2021-05-26 21:56:31 +02:00
// @Success 201 {object} models.TaskRelation "The created task relation object."
2020-06-28 16:25:46 +02:00
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
2019-09-25 20:44:41 +02:00
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/relations [put]
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error {
2019-09-25 20:44:41 +02:00
// Check if both tasks are the same
if rel.TaskID == rel.OtherTaskID {
return ErrRelationTasksCannotBeTheSame{
TaskID: rel.TaskID,
OtherTaskID: rel.OtherTaskID,
}
}
// Check if the relation already exists, in one form or the other.
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
exists, err := s.
2019-09-25 20:44:41 +02:00
Where("(task_id = ? AND other_task_id = ? AND relation_kind = ?) OR (task_id = ? AND other_task_id = ? AND relation_kind = ?)",
rel.TaskID, rel.OtherTaskID, rel.RelationKind, rel.TaskID, rel.OtherTaskID, rel.RelationKind).
Exist(rel)
if err != nil {
return err
}
if exists {
return ErrRelationAlreadyExists{
TaskID: rel.TaskID,
OtherTaskID: rel.OtherTaskID,
Kind: rel.RelationKind,
}
}
rel.CreatedBy, err = GetUserOrLinkShareUser(s, a)
if err != nil {
return err
}
rel.CreatedByID = rel.CreatedBy.ID
2019-09-25 20:44:41 +02:00
// Build up the other relation (see the comment above for explanation)
otherRelation := &TaskRelation{
TaskID: rel.OtherTaskID,
OtherTaskID: rel.TaskID,
CreatedByID: rel.CreatedByID,
2019-09-25 20:44:41 +02:00
}
switch rel.RelationKind {
case RelationKindSubtask:
otherRelation.RelationKind = RelationKindParenttask
case RelationKindParenttask:
otherRelation.RelationKind = RelationKindSubtask
case RelationKindRelated:
otherRelation.RelationKind = RelationKindRelated
case RelationKindDuplicateOf:
otherRelation.RelationKind = RelationKindDuplicates
case RelationKindDuplicates:
otherRelation.RelationKind = RelationKindDuplicateOf
case RelationKindBlocking:
otherRelation.RelationKind = RelationKindBlocked
case RelationKindBlocked:
otherRelation.RelationKind = RelationKindBlocking
case RelationKindPreceeds:
otherRelation.RelationKind = RelationKindFollows
case RelationKindFollows:
otherRelation.RelationKind = RelationKindPreceeds
case RelationKindCopiedFrom:
otherRelation.RelationKind = RelationKindCopiedTo
case RelationKindCopiedTo:
otherRelation.RelationKind = RelationKindCopiedFrom
case RelationKindUnknown:
// Nothing to do
2019-09-25 20:44:41 +02:00
}
// Finally insert everything
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
_, err = s.Insert(&[]*TaskRelation{
2019-09-25 20:44:41 +02:00
rel,
otherRelation,
})
return err
}
// Delete removes a task relation
// @Summary Remove a task relation
// @tags task
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param relation body models.TaskRelation true "The relation object"
// @Param taskID path int true "Task ID"
// @Param relationKind path string true "The kind of the relation. See the TaskRelation type for more info."
// @Param otherTaskID path int true "The id of the other task."
2019-09-25 20:44:41 +02:00
// @Success 200 {object} models.Message "The task relation was successfully deleted."
2020-06-28 16:25:46 +02:00
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
// @Failure 404 {object} web.HTTPError "The task relation was not found."
2019-09-25 20:44:41 +02:00
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/relations/{relationKind}/{otherTaskId} [delete]
func (rel *TaskRelation) Delete(s *xorm.Session, a web.Auth) error {
cond := builder.Or(
builder.And(
builder.Eq{"task_id": rel.TaskID},
builder.Eq{"other_task_id": rel.OtherTaskID},
builder.Eq{"relation_kind": rel.RelationKind},
),
builder.And(
builder.Eq{"task_id": rel.OtherTaskID},
builder.Eq{"other_task_id": rel.TaskID},
builder.Eq{"relation_kind": rel.RelationKind},
),
)
2019-09-25 20:44:41 +02:00
// Check if the relation exists
Use db sessions everywere (#750) Fix lint Fix lint Fix loading tasks with search Fix loading lists Fix loading task Fix loading lists and namespaces Fix tests Fix user commands Fix upload Fix migration handlers Fix all manual root handlers Fix session in avatar Fix session in list duplication & routes Use sessions in migration code Make sure the openid stuff uses a session Add alias for db type in db package Use sessions for file Use a session for everything in users Use a session for everything in users Make sure to use a session everywhere in models Create new session from db Add session handling for user list Add session handling for unsplash Add session handling for teams and related Add session handling for tasks and related entities Add session handling for task reminders Add session handling for task relations Add session handling for task comments Add session handling for task collections Add session handling for task attachments Add session handling for task assignees Add session handling for saved filters Add session handling for namespace and related types Add session handling for namespace and related types Add session handling for list users Add session handling for list tests Add session handling to list teams and related entities Add session handling for link shares and related entities Add session handling for labels and related entities Add session handling for kanban and related entities Add session handling for bulk task and related entities Add session handling for lists and related entities Add session configuration for web handler Update web handler Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/750 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-12-23 16:32:28 +01:00
exists, err := s.
Where(cond).
Exist(&TaskRelation{})
2019-09-25 20:44:41 +02:00
if err != nil {
return err
}
if !exists {
return ErrRelationDoesNotExist{
TaskID: rel.TaskID,
OtherTaskID: rel.OtherTaskID,
Kind: rel.RelationKind,
}
}
_, err = s.
Where(cond).
Delete(&TaskRelation{})
2019-09-25 20:44:41 +02:00
return err
}