feat(views): add crud handlers and routes for views
This commit is contained in:
parent
6bdb33fb46
commit
b39c5580c2
@ -55,19 +55,20 @@ This document describes the different errors Vikunja can return.
|
|||||||
|
|
||||||
## Project
|
## Project
|
||||||
|
|
||||||
| ErrorCode | HTTP Status Code | Description |
|
| ErrorCode | HTTP Status Code | Description |
|
||||||
|-----------|------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------|------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| 3001 | 404 | The project does not exist. |
|
| 3001 | 404 | The project does not exist. |
|
||||||
| 3004 | 403 | The user needs to have read permissions on that project to perform that action. |
|
| 3004 | 403 | The user needs to have read permissions on that project to perform that action. |
|
||||||
| 3005 | 400 | The project title cannot be empty. |
|
| 3005 | 400 | The project title cannot be empty. |
|
||||||
| 3006 | 404 | The project share does not exist. |
|
| 3006 | 404 | The project share does not exist. |
|
||||||
| 3007 | 400 | A project with this identifier already exists. |
|
| 3007 | 400 | A project with this identifier already exists. |
|
||||||
| 3008 | 412 | The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project. |
|
| 3008 | 412 | The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project. |
|
||||||
| 3009 | 412 | The project cannot belong to a dynamically generated parent project like "Favorites". |
|
| 3009 | 412 | The project cannot belong to a dynamically generated parent project like "Favorites". |
|
||||||
| 3010 | 412 | This project cannot be a child of itself. |
|
| 3010 | 412 | This project cannot be a child of itself. |
|
||||||
| 3011 | 412 | This project cannot have a cyclic relationship to a parent project. |
|
| 3011 | 412 | This project cannot have a cyclic relationship to a parent project. |
|
||||||
| 3012 | 412 | This project cannot be deleted because a user has set it as their default project. |
|
| 3012 | 412 | This project cannot be deleted because a user has set it as their default project. |
|
||||||
| 3013 | 412 | This project cannot be archived because a user has set it as their default project. |
|
| 3013 | 412 | This project cannot be archived because a user has set it as their default project. |
|
||||||
|
| 3014 | 404 | This project view does not exist. |
|
||||||
|
|
||||||
## Task
|
## Task
|
||||||
|
|
||||||
|
@ -412,6 +412,33 @@ func (err *ErrCannotArchiveDefaultProject) HTTPError() web.HTTPError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrProjectViewDoesNotExist represents an error where the default project is being deleted
|
||||||
|
type ErrProjectViewDoesNotExist struct {
|
||||||
|
ProjectViewID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrProjectViewDoesNotExist checks if an error is a project is archived error.
|
||||||
|
func IsErrProjectViewDoesNotExist(err error) bool {
|
||||||
|
_, ok := err.(*ErrProjectViewDoesNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *ErrProjectViewDoesNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("Project view does not exist [ProjectViewID: %d]", err.ProjectViewID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCodeProjectViewDoesNotExist holds the unique world-error code of this error
|
||||||
|
const ErrCodeProjectViewDoesNotExist = 3014
|
||||||
|
|
||||||
|
// HTTPError holds the http error description
|
||||||
|
func (err *ErrProjectViewDoesNotExist) HTTPError() web.HTTPError {
|
||||||
|
return web.HTTPError{
|
||||||
|
HTTPCode: http.StatusNotFound,
|
||||||
|
Code: ErrCodeProjectViewDoesNotExist,
|
||||||
|
Message: "This project view does not exist.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==============
|
// ==============
|
||||||
// Task errors
|
// Task errors
|
||||||
// ==============
|
// ==============
|
||||||
|
@ -19,6 +19,7 @@ package models
|
|||||||
import (
|
import (
|
||||||
"code.vikunja.io/web"
|
"code.vikunja.io/web"
|
||||||
"time"
|
"time"
|
||||||
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProjectViewKind int
|
type ProjectViewKind int
|
||||||
@ -57,3 +58,151 @@ type ProjectView struct {
|
|||||||
func (p *ProjectView) TableName() string {
|
func (p *ProjectView) TableName() string {
|
||||||
return "project_views"
|
return "project_views"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadAll gets all project views
|
||||||
|
// @Summary Get all project views for a project
|
||||||
|
// @Description Returns all project views for a sepcific project
|
||||||
|
// @tags project
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Success 200 {array} models.ProjectView "The project views"
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views [get]
|
||||||
|
func (p *ProjectView) ReadAll(s *xorm.Session, a web.Auth, _ string, _ int, _ int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||||
|
|
||||||
|
pp := &Project{ID: p.ProjectID}
|
||||||
|
can, _, err := pp.CanRead(s, a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, 0, err
|
||||||
|
}
|
||||||
|
if !can {
|
||||||
|
return nil, 0, 0, ErrGenericForbidden{}
|
||||||
|
}
|
||||||
|
|
||||||
|
projectViews := []*ProjectView{}
|
||||||
|
err = s.
|
||||||
|
Where("project_id = ?", p.ProjectID).
|
||||||
|
Find(&projectViews)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, err := s.
|
||||||
|
Where("project_id = ?", p.ProjectID).
|
||||||
|
Count(&ProjectView{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectViews, len(projectViews), totalCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOne implements the CRUD method to get one project view
|
||||||
|
// @Summary Get one project view
|
||||||
|
// @Description Returns a project view by its ID.
|
||||||
|
// @tags project
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Param id path int true "Project View ID"
|
||||||
|
// @Success 200 {object} models.ProjectView "The project view"
|
||||||
|
// @Failure 403 {object} web.HTTPError "The user does not have access to this project view"
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views/{id} [get]
|
||||||
|
func (p *ProjectView) ReadOne(s *xorm.Session, _ web.Auth) (err error) {
|
||||||
|
view, err := GetProjectViewByID(s, p.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = *view
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the project view
|
||||||
|
// @Summary Delete a project view
|
||||||
|
// @Description Deletes a project view.
|
||||||
|
// @tags project
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Param id path int true "Project View ID"
|
||||||
|
// @Success 200 {object} models.Message "The project view was successfully deleted."
|
||||||
|
// @Failure 403 {object} web.HTTPError "The user does not have access to the project view"
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views/{id} [delete]
|
||||||
|
func (p *ProjectView) Delete(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
_, err = s.
|
||||||
|
Where("id = ? AND projec_id = ?", p.ID, p.ProjectID).
|
||||||
|
Delete(&ProjectView{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create adds a new project view
|
||||||
|
// @Summary Create a project view
|
||||||
|
// @Description Create a project view in a specific project.
|
||||||
|
// @tags project
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Param view body models.ProjectView true "The project view you want to create."
|
||||||
|
// @Success 200 {object} models.ProjectView "The created project view"
|
||||||
|
// @Failure 403 {object} web.HTTPError "The user does not have access to create a project view"
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views [put]
|
||||||
|
func (p *ProjectView) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||||
|
_, err = s.Insert(p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is the handler to update a project view
|
||||||
|
// @Summary Updates a project view
|
||||||
|
// @Description Updates a project view.
|
||||||
|
// @tags project
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Security JWTKeyAuth
|
||||||
|
// @Param project path int true "Project ID"
|
||||||
|
// @Param id path int true "Project View ID"
|
||||||
|
// @Param view body models.ProjectView true "The project view with updated values you want to change."
|
||||||
|
// @Success 200 {object} models.ProjectView "The updated project view."
|
||||||
|
// @Failure 400 {object} web.HTTPError "Invalid project view object provided."
|
||||||
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
|
// @Router /projects/{project}/views/{id} [post]
|
||||||
|
func (p *ProjectView) Update(s *xorm.Session, _ web.Auth) (err error) {
|
||||||
|
// Check if the project view exists
|
||||||
|
_, err = GetProjectViewByID(s, p.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ID(p.ID).Update(p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProjectViewByID(s *xorm.Session, id int64) (view *ProjectView, err error) {
|
||||||
|
exists, err := s.
|
||||||
|
Where("id = ?", id).
|
||||||
|
NoAutoCondition().
|
||||||
|
Get(view)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return nil, &ErrProjectViewDoesNotExist{
|
||||||
|
ProjectViewID: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
58
pkg/models/project_view_rights.go
Normal file
58
pkg/models/project_view_rights.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Vikunja is a to-do list application to facilitate your life.
|
||||||
|
// Copyright 2018-present 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 models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *ProjectView) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||||
|
pp, err := p.getProject(s)
|
||||||
|
if err != nil {
|
||||||
|
return false, 0, err
|
||||||
|
}
|
||||||
|
return pp.CanRead(s, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProjectView) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
|
pp, err := p.getProject(s)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return pp.CanUpdate(s, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProjectView) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
|
pp, err := p.getProject(s)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return pp.CanUpdate(s, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProjectView) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||||
|
pp, err := p.getProject(s)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return pp.CanUpdate(s, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProjectView) getProject(s *xorm.Session) (pp *Project, err error) {
|
||||||
|
return GetProjectSimpleByID(s, p.ProjectID)
|
||||||
|
}
|
@ -590,6 +590,7 @@ func registerAPIRoutes(a *echo.Group) {
|
|||||||
a.GET("/webhooks/events", apiv1.GetAvailableWebhookEvents)
|
a.GET("/webhooks/events", apiv1.GetAvailableWebhookEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reactions
|
||||||
reactionProvider := &handler.WebHandler{
|
reactionProvider := &handler.WebHandler{
|
||||||
EmptyStruct: func() handler.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.Reaction{}
|
return &models.Reaction{}
|
||||||
@ -598,6 +599,19 @@ func registerAPIRoutes(a *echo.Group) {
|
|||||||
a.GET("/:entitykind/:entityid/reactions", reactionProvider.ReadAllWeb)
|
a.GET("/:entitykind/:entityid/reactions", reactionProvider.ReadAllWeb)
|
||||||
a.POST("/:entitykind/:entityid/reactions/delete", reactionProvider.DeleteWeb)
|
a.POST("/:entitykind/:entityid/reactions/delete", reactionProvider.DeleteWeb)
|
||||||
a.PUT("/:entitykind/:entityid/reactions", reactionProvider.CreateWeb)
|
a.PUT("/:entitykind/:entityid/reactions", reactionProvider.CreateWeb)
|
||||||
|
|
||||||
|
// Project views
|
||||||
|
projectViewProvider := &handler.WebHandler{
|
||||||
|
EmptyStruct: func() handler.CObject {
|
||||||
|
return &models.ProjectView{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
a.GET("/projects/:project/views", projectViewProvider.ReadAllWeb)
|
||||||
|
a.GET("/projects/:project/views/:view", projectViewProvider.ReadOneWeb)
|
||||||
|
a.PUT("/projects/:project/views", projectViewProvider.CreateWeb)
|
||||||
|
a.DELETE("/projects/:project/views/:view", projectViewProvider.DeleteWeb)
|
||||||
|
a.POST("/projects/:project/views/:view", projectViewProvider.UpdateWeb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerMigrations(m *echo.Group) {
|
func registerMigrations(m *echo.Group) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user