Added http endpoint to list all users on a list (#87)

This commit is contained in:
konrad 2019-07-18 16:38:21 +00:00 committed by Gitea
parent b63928850a
commit 12eaddc8ee
21 changed files with 722 additions and 56 deletions

View file

@ -55,7 +55,7 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [x] Wir brauchen noch ne gute idee, wie man die listen kriegt, auf die man nur so Zugriff hat (ohne namespace) * [x] Wir brauchen noch ne gute idee, wie man die listen kriegt, auf die man nur so Zugriff hat (ohne namespace)
* Dazu am Besten nen pseudonamespace anlegen (id -1 oder so), der hat das dann alles * Dazu am Besten nen pseudonamespace anlegen (id -1 oder so), der hat das dann alles
* [x] Testing mit locust: https://locust.io/ * [x] Testing mit locust: https://locust.io/
* [ ] Endpoint to get all users who have access to a list - regardless of via team, user share or via namespace * [x] Endpoint to get all users who have access to a list - regardless of via team, user share or via namespace
#### Userstuff #### Userstuff
@ -188,8 +188,8 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [x] Adding users to a team should also use uuid * [x] Adding users to a team should also use uuid
* [x] Check if the team/user really exist before updating them on lists/namespaces * [x] Check if the team/user really exist before updating them on lists/namespaces
* [x] Refactor config handling: Custom type "key" or so which holds the viper const and then mixins on that type to get the values from viper * [x] Refactor config handling: Custom type "key" or so which holds the viper const and then mixins on that type to get the values from viper
* [x] Less files, but with some kind of logic
* [ ] Have extra functions for logging to call so it is possible to call `log.Info` instead of `log.Log.Info` * [ ] Have extra functions for logging to call so it is possible to call `log.Info` instead of `log.Log.Info`
* [ ] Less files, but with some kind of logic
### Linters ### Linters

View file

@ -3,7 +3,7 @@ POST http://localhost:8080/api/v1/login
Content-Type: application/json Content-Type: application/json
{ {
"username": "user6", "username": "user3",
"password": "1234" "password": "1234"
} }

View file

@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}}
### ###
# Get one list # Get one list
GET http://localhost:8080/api/v1/lists/1172 GET http://localhost:8080/api/v1/lists/3
Authorization: Bearer {{auth_token}} Authorization: Bearer {{auth_token}}
### ###
@ -82,11 +82,11 @@ Authorization: Bearer {{auth_token}}
### ###
# Give a user access to that list # Give a user access to that list
PUT http://localhost:8080/api/v1/lists/1172/users PUT http://localhost:8080/api/v1/lists/3/users
Authorization: Bearer {{auth_token}} Authorization: Bearer {{auth_token}}
Content-Type: application/json Content-Type: application/json
{"user_id":1, "right":1} {"userID":"user4", "right":1}
### ###
@ -169,3 +169,9 @@ Content-Type: application/json
} }
### ###
# Get all users who have access to a list
GET http://localhost:8080/api/v1/lists/3/users
Authorization: Bearer {{auth_token}}
###

View file

@ -134,3 +134,21 @@
namespace_id: 12 namespace_id: 12
updated: 0 updated: 0
created: 0 created: 0
# This list is owned by user 7, and several other users have access to it via different methods.
# It is used to test the listUsers method.
-
id: 18
title: Test18
description: Lorem Ipsum
owner_id: 7
namespace_id: 13
updated: 0
created: 0
-
id: 19
title: Test19
description: Lorem Ipsum
owner_id: 7
namespace_id: 14
updated: 0
created: 0

View file

@ -58,3 +58,15 @@
owner_id: 6 owner_id: 6
updated: 0 updated: 0
created: 0 created: 0
- id: 13
name: testnamespace13
description: Lorem Ipsum
owner_id: 7
updated: 0
created: 0
- id: 14
name: testnamespace14
description: Lorem Ipsum
owner_id: 7
updated: 0
created: 0

View file

@ -4,7 +4,6 @@
right: 0 right: 0
updated: 0 updated: 0
created: 0 created: 0
# This team has read only access on list 6 # This team has read only access on list 6
- id: 2 - id: 2
team_id: 2 team_id: 2
@ -12,7 +11,6 @@
right: 0 right: 0
updated: 0 updated: 0
created: 0 created: 0
# This team has write access on list 7 # This team has write access on list 7
- id: 3 - id: 3
team_id: 3 team_id: 3
@ -20,11 +18,31 @@
right: 1 right: 1
updated: 0 updated: 0
created: 0 created: 0
# This team has admin access on list 8 # This team has admin access on list 8
- id: 4 - id: 4
team_id: 4 team_id: 4
list_id: 8 list_id: 8
right: 2 right: 2
updated: 0 updated: 0
created: 0
# Readonly acces on list 19
- id: 5
team_id: 8
list_id: 19
right: 2
updated: 0
created: 0
# Write acces on list 19
- id: 6
team_id: 9
list_id: 19
right: 1
updated: 0
created: 0
# Admin acces on list 19
- id: 7
team_id: 10
list_id: 19
right: 2
updated: 0
created: 0 created: 0

View file

@ -31,3 +31,27 @@
team_id: 7 team_id: 7
user_id: 1 user_id: 1
created: 0 created: 0
-
team_id: 8
user_id: 1
created: 0
-
team_id: 9
user_id: 2
created: 0
-
team_id: 10
user_id: 3
created: 0
-
team_id: 11
user_id: 8
created: 0
-
team_id: 12
user_id: 9
created: 0
-
team_id: 13
user_id: 10
created: 0

View file

@ -32,3 +32,21 @@
right: 2 right: 2
updated: 0 updated: 0
created: 0 created: 0
- id: 6
team_id: 11
namespace_id: 14
right: 0
updated: 0
created: 0
- id: 7
team_id: 12
namespace_id: 14
right: 1
updated: 0
created: 0
- id: 8
team_id: 13
namespace_id: 14
right: 2
updated: 0
created: 0

View file

@ -19,4 +19,22 @@
created_by_id: 1 created_by_id: 1
- id: 7 - id: 7
name: testteam4_admin_on_namespace9 name: testteam4_admin_on_namespace9
created_by_id: 1 created_by_id: 1
- id: 8
name: testteam8
created_by_id: 7
- id: 9
name: testteam9
created_by_id: 7
- id: 10
name: testteam10
created_by_id: 7
- id: 11
name: testteam11
created_by_id: 7
- id: 12
name: testteam12
created_by_id: 7
- id: 13
name: testteam13
created_by_id: 7

View file

@ -46,3 +46,52 @@
is_active: true is_active: true
updated: 0 updated: 0
created: 0 created: 0
- id: 7
username: 'user7'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user7@example.com'
is_active: true
updated: 0
created: 0
- id: 8
username: 'user8'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user8@example.com'
is_active: true
updated: 0
created: 0
- id: 9
username: 'user9'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user9@example.com'
is_active: true
updated: 0
created: 0
- id: 10
username: 'user10'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user10@example.com'
is_active: true
updated: 0
created: 0
- id: 11
username: 'user11'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user11@example.com'
is_active: true
updated: 0
created: 0
- id: 12
username: 'user12'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user12@example.com'
is_active: true
updated: 0
created: 0
- id: 13
username: 'user13'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user14@example.com'
is_active: true
updated: 0
created: 0

View file

@ -4,31 +4,45 @@
right: 0 right: 0
updated: 0 updated: 0
created: 0 created: 0
- id: 2 - id: 2
user_id: 2 user_id: 2
list_id: 3 list_id: 3
right: 0 right: 0
updated: 0 updated: 0
created: 0 created: 0
- id: 3 - id: 3
user_id: 1 user_id: 1
list_id: 9 list_id: 9
right: 0 right: 0
updated: 0 updated: 0
created: 0 created: 0
- id: 4 - id: 4
user_id: 1 user_id: 1
list_id: 10 list_id: 10
right: 1 right: 1
updated: 0 updated: 0
created: 0 created: 0
- id: 5 - id: 5
user_id: 1 user_id: 1
list_id: 11 list_id: 11
right: 2 right: 2
updated: 0 updated: 0
created: 0 created: 0
- id: 6
user_id: 4
list_id: 19
right: 0
updated: 0
created: 0
- id: 7
user_id: 5
list_id: 19
right: 1
updated: 0
created: 0
- id: 8
user_id: 6
list_id: 19
right: 2
updated: 0
created: 0

View file

@ -32,3 +32,21 @@
right: 2 right: 2
updated: 0 updated: 0
created: 0 created: 0
- id: 6
user_id: 11
namespace_id: 14
right: 0
updated: 0
created: 0
- id: 7
user_id: 12
namespace_id: 14
right: 1
updated: 0
created: 0
- id: 8
user_id: 13
namespace_id: 14
right: 2
updated: 0
created: 0

View file

@ -40,7 +40,7 @@ func TestList_ReadAll(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice) assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
s := reflect.ValueOf(lists3) s := reflect.ValueOf(lists3)
assert.Equal(t, 15, s.Len()) assert.Equal(t, 16, s.Len())
// Try getting lists for a nonexistant user // Try getting lists for a nonexistant user
_, err = lists2.ReadAll("", &User{ID: 984234}, 1) _, err = lists2.ReadAll("", &User{ID: 984234}, 1)

View file

@ -59,7 +59,7 @@ func TestTeam_Create(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice) assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice)
s := reflect.ValueOf(ts) s := reflect.ValueOf(ts)
assert.Equal(t, 8, s.Len()) assert.Equal(t, 9, s.Len())
// Check inserting it with an empty name // Check inserting it with an empty name
dummyteam.Name = "" dummyteam.Name = ""

View file

@ -16,6 +16,8 @@
package models package models
import "github.com/go-xorm/builder"
// ListUsers returns a list with all users, filtered by an optional searchstring // ListUsers returns a list with all users, filtered by an optional searchstring
func ListUsers(searchterm string) (users []User, err error) { func ListUsers(searchterm string) (users []User, err error) {
@ -33,3 +35,89 @@ func ListUsers(searchterm string) (users []User, err error) {
return users, nil return users, nil
} }
// ListUIDs hold all kinds of user IDs from accounts who have somehow access to a list
type ListUIDs struct {
ListOwnerID int64 `xorm:"listOwner"`
NamespaceUserID int64 `xorm:"unID"`
ListUserID int64 `xorm:"ulID"`
NamespaceOwnerUserID int64 `xorm:"nOwner"`
TeamNamespaceUserID int64 `xorm:"tnUID"`
TeamListUserID int64 `xorm:"tlUID"`
}
// ListUsersFromList returns a list with all users who have access to a list, regardless of the method which gave them access
func ListUsersFromList(l *List, search string) (users []*User, err error) {
userids := []*ListUIDs{}
err = x.
Select(`l.owner_id as listOwner,
un.user_id as unID,
ul.user_id as ulID,
n.owner_id as nOwner,
tm.user_id as tnUID,
tm2.user_id as tlUID`).
Table("list").
Alias("l").
// User stuff
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
Join("LEFT", []string{"namespaces", "n"}, "n.id = l.namespace_id").
// Team stuff
Join("LEFT", []string{"team_namespaces", "tn"}, " l.namespace_id = tn.namespace_id").
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tn.team_id").
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
// The actual condition
Where(
builder.Or(
builder.Or(builder.Eq{"ul.right": RightRead}),
builder.Or(builder.Eq{"un.right": RightRead}),
builder.Or(builder.Eq{"tl.right": RightRead}),
builder.Or(builder.Eq{"tn.right": RightRead}),
builder.Or(builder.Eq{"ul.right": RightWrite}),
builder.Or(builder.Eq{"un.right": RightWrite}),
builder.Or(builder.Eq{"tl.right": RightWrite}),
builder.Or(builder.Eq{"tn.right": RightWrite}),
builder.Or(builder.Eq{"ul.right": RightAdmin}),
builder.Or(builder.Eq{"un.right": RightAdmin}),
builder.Or(builder.Eq{"tl.right": RightAdmin}),
builder.Or(builder.Eq{"tn.right": RightAdmin}),
),
builder.Eq{"l.id": l.ID},
).
Find(&userids)
if err != nil {
return
}
// Remove duplicates from the list of ids and make it a slice
uidmap := make(map[int64]bool)
uidmap[l.OwnerID] = true
for _, u := range userids {
uidmap[u.ListUserID] = true
uidmap[u.NamespaceOwnerUserID] = true
uidmap[u.NamespaceUserID] = true
uidmap[u.TeamListUserID] = true
uidmap[u.TeamNamespaceUserID] = true
}
uids := make([]int64, len(uidmap))
for id := range uidmap {
uids = append(uids, id)
}
// Get all users
err = x.
Table("users").
Select("*").
In("id", uids).
And("username LIKE ?", "%"+search+"%").
GroupBy("id").
OrderBy("id").
Find(&users)
return
}

View file

@ -0,0 +1,159 @@
package models
import (
"github.com/stretchr/testify/assert"
"gopkg.in/d4l3k/messagediff.v1"
"testing"
)
func TestListUsersFromList(t *testing.T) {
err := LoadFixtures()
assert.NoError(t, err)
testuser1 := &User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "111d68d06e2d317b5a59c2c6c5bad808",
}
testuser2 := &User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "ab53a2911ddf9b4817ac01ddcd3d975f",
}
testuser3 := &User{
ID: 3,
Username: "user3",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "97d6d9441ff85fdc730e02a6068d267b",
PasswordResetToken: "passwordresettesttoken",
}
testuser4 := &User{
ID: 4,
Username: "user4",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: false,
AvatarURL: "7e65550957227bd38fe2d7fbc6fd2f7b",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
}
testuser5 := &User{
ID: 5,
Username: "user5",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: false,
AvatarURL: "cfa35b8cd2ec278026357769582fa563",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
}
testuser6 := &User{
ID: 6,
Username: "user6",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "3efbe51f864c6666bc27caf4c6ff90ed",
}
testuser7 := &User{
ID: 7,
Username: "user7",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "e80a711d4de44c30054806ebbd488464",
}
testuser8 := &User{
ID: 8,
Username: "user8",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "2b9b320416cd31020bb6844c3fadefd1",
}
testuser9 := &User{
ID: 9,
Username: "user9",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "f784fdb21d26dd2c64f5135f35ec401f",
}
testuser10 := &User{
ID: 10,
Username: "user10",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "fce8ff4ff56d75ad587d1bbaa5ef0563",
}
testuser11 := &User{
ID: 11,
Username: "user11",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "ad6d67d0c4495e186010732a7d360028",
}
testuser12 := &User{
ID: 12,
Username: "user12",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "ef1debc1364806281c42eeedfdeb943b",
}
testuser13 := &User{
ID: 13,
Username: "user13",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "b9e3f76032af53c9ff2df52d51ada717",
}
type args struct {
l *List
search string
}
tests := []struct {
name string
args args
wantUsers []*User
wantErr bool
}{
{
name: "Check owner only",
args: args{l: &List{ID: 18, OwnerID: 7}},
wantUsers: []*User{testuser7},
},
{
// This list has another different user shared for each possible method
name: "Check with owner and other users",
args: args{l: &List{ID: 19, OwnerID: 7}},
wantUsers: []*User{
testuser1, // Shared Via Team readonly
testuser2, // Shared Via Team write
testuser3, // Shared Via Team admin
testuser4, // Shared Via User readonly
testuser5, // Shared Via User write
testuser6, // Shared Via User admin
testuser7, // Owner
testuser8, // Shared Via NamespaceTeam readonly
testuser9, // Shared Via NamespaceTeam write
testuser10, // Shared Via NamespaceTeam admin
testuser11, // Shared Via NamespaceUser readonly
testuser12, // Shared Via NamespaceUser write
testuser13, // Shared Via NamespaceUser admin
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotUsers, err := ListUsersFromList(tt.args.l, tt.args.search)
if (err != nil) != tt.wantErr {
t.Errorf("ListUsersFromList() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff, equal := messagediff.PrettyDiff(tt.wantUsers, gotUsers); !equal {
t.Errorf("Test %s, LabelTask.ReadAll() = %v, want %v, \ndiff: %v", tt.name, gotUsers, tt.wantUsers, diff)
}
})
}
}

View file

@ -21,6 +21,7 @@ import (
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
"strconv"
) )
// UserList gets all information about a user // UserList gets all information about a user
@ -49,3 +50,45 @@ func UserList(c echo.Context) error {
return c.JSON(http.StatusOK, users) return c.JSON(http.StatusOK, users)
} }
// ListUsersForList returns a list with all users who have access to a list, regardless of the method the list was shared with them.
// @Summary Get users
// @Description Lists all users (without emailadresses). Also possible to search for a specific user.
// @tags list
// @Accept json
// @Produce json
// @Param s query string false "Search for a user by its name."
// @Security JWTKeyAuth
// @Param id path int true "List ID"
// @Success 200 {array} models.User "All (found) users."
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
// @Failure 401 {object} code.vikunja.io/web.HTTPError "The user does not have the right to see the list."
// @Failure 500 {object} models.Message "Internal server error."
// @Router /lists/{id}/listusers [get]
func ListUsersForList(c echo.Context) error {
listID, err := strconv.ParseInt(c.Param("list"), 10, 64)
if err != nil {
return handler.HandleHTTPError(err, c)
}
list := models.List{ID: listID}
currentUser, err := models.GetCurrentUser(c)
if err != nil {
return handler.HandleHTTPError(err, c)
}
canRead, err := list.CanRead(currentUser)
if err != nil {
return handler.HandleHTTPError(err, c)
}
if !canRead {
return echo.ErrForbidden
}
s := c.QueryParam("s")
users, err := models.ListUsersFromList(&list, s)
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, users)
}

View file

@ -254,6 +254,7 @@ func registerAPIRoutes(a *echo.Group) {
a.POST("/lists/:list", listHandler.UpdateWeb) a.POST("/lists/:list", listHandler.UpdateWeb)
a.DELETE("/lists/:list", listHandler.DeleteWeb) a.DELETE("/lists/:list", listHandler.DeleteWeb)
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb) a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
a.GET("/lists/:list/listusers", apiv1.ListUsersForList)
taskHandler := &handler.WebHandler{ taskHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject { EmptyStruct: func() handler.CObject {

View file

@ -1,6 +1,6 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at // This file was generated by swaggo/swag at
// 2019-07-16 00:32:48.008049583 +0200 CEST m=+0.169009519 // 2019-07-18 18:18:32.365544639 +0200 CEST m=+0.166364676
package swagger package swagger
@ -58,7 +58,7 @@ var doc = `{
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns an array with all assignees for this task.", "description": "Returns all labels which are either created by the user or associated with a task the user has at least read-access to.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -66,9 +66,9 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"assignees" "labels"
], ],
"summary": "Get all assignees for a task", "summary": "Get all labels a user has access to",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
@ -78,18 +78,18 @@ var doc = `{
}, },
{ {
"type": "string", "type": "string",
"description": "Search assignees by their username.", "description": "Search labels by label text.",
"name": "s", "name": "s",
"in": "query" "in": "query"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The assignees", "description": "The labels",
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/models.User" "$ref": "#/definitions/models.Label"
} }
} }
}, },
@ -412,7 +412,7 @@ var doc = `{
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns a list by its ID.", "description": "Returns a team by its ID.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -420,13 +420,13 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"list" "team"
], ],
"summary": "Gets one list", "summary": "Gets one team",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "List ID", "description": "Team ID",
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
@ -434,14 +434,14 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The list", "description": "The team",
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/models.List" "$ref": "#/definitions/models.Team"
} }
}, },
"403": { "403": {
"description": "The user does not have access to the list", "description": "The user does not have access to the team",
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError" "$ref": "#/definitions/code.vikunja.io.web.HTTPError"
@ -645,6 +645,73 @@ var doc = `{
} }
} }
}, },
"/lists/{id}/listusers": {
"get": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Lists all users (without emailadresses). Also possible to search for a specific user.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"list"
],
"summary": "Get users",
"parameters": [
{
"type": "string",
"description": "Search for a user by its name.",
"name": "s",
"in": "query"
},
{
"type": "integer",
"description": "List ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "All (found) users.",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/models.User"
}
}
},
"400": {
"description": "Something's invalid.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
}
},
"401": {
"description": "The user does not have the right to see the list.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io.web.HTTPError"
}
},
"500": {
"description": "Internal server error.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/lists/{id}/teams": { "/lists/{id}/teams": {
"get": { "get": {
"security": [ "security": [

View file

@ -45,7 +45,7 @@
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns an array with all assignees for this task.", "description": "Returns all labels which are either created by the user or associated with a task the user has at least read-access to.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -53,9 +53,9 @@
"application/json" "application/json"
], ],
"tags": [ "tags": [
"assignees" "labels"
], ],
"summary": "Get all assignees for a task", "summary": "Get all labels a user has access to",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
@ -65,18 +65,18 @@
}, },
{ {
"type": "string", "type": "string",
"description": "Search assignees by their username.", "description": "Search labels by label text.",
"name": "s", "name": "s",
"in": "query" "in": "query"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The assignees", "description": "The labels",
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/models.User" "$ref": "#/definitions/models.Label"
} }
} }
}, },
@ -399,7 +399,7 @@
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns a list by its ID.", "description": "Returns a team by its ID.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -407,13 +407,13 @@
"application/json" "application/json"
], ],
"tags": [ "tags": [
"list" "team"
], ],
"summary": "Gets one list", "summary": "Gets one team",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "List ID", "description": "Team ID",
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
@ -421,14 +421,14 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The list", "description": "The team",
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/models.List" "$ref": "#/definitions/models.Team"
} }
}, },
"403": { "403": {
"description": "The user does not have access to the list", "description": "The user does not have access to the team",
"schema": { "schema": {
"type": "object", "type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError" "$ref": "#/definitions/code.vikunja.io/web.HTTPError"
@ -632,6 +632,73 @@
} }
} }
}, },
"/lists/{id}/listusers": {
"get": {
"security": [
{
"JWTKeyAuth": []
}
],
"description": "Lists all users (without emailadresses). Also possible to search for a specific user.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"list"
],
"summary": "Get users",
"parameters": [
{
"type": "string",
"description": "Search for a user by its name.",
"name": "s",
"in": "query"
},
{
"type": "integer",
"description": "List ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "All (found) users.",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/models.User"
}
}
},
"400": {
"description": "Something's invalid.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
}
},
"401": {
"description": "The user does not have the right to see the list.",
"schema": {
"type": "object",
"$ref": "#/definitions/code.vikunja.io/web.HTTPError"
}
},
"500": {
"description": "Internal server error.",
"schema": {
"type": "object",
"$ref": "#/definitions/models.Message"
}
}
}
}
},
"/lists/{id}/teams": { "/lists/{id}/teams": {
"get": { "get": {
"security": [ "security": [

View file

@ -704,14 +704,15 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Returns an array with all assignees for this task. description: Returns all labels which are either created by the user or associated
with a task the user has at least read-access to.
parameters: parameters:
- description: The page number. Used for pagination. If not provided, the first - description: The page number. Used for pagination. If not provided, the first
page of results is returned. page of results is returned.
in: query in: query
name: p name: p
type: integer type: integer
- description: Search assignees by their username. - description: Search labels by label text.
in: query in: query
name: s name: s
type: string type: string
@ -719,10 +720,10 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: The assignees description: The labels
schema: schema:
items: items:
$ref: '#/definitions/models.User' $ref: '#/definitions/models.Label'
type: array type: array
"500": "500":
description: Internal error description: Internal error
@ -731,9 +732,9 @@ paths:
type: object type: object
security: security:
- JWTKeyAuth: [] - JWTKeyAuth: []
summary: Get all assignees for a task summary: Get all labels a user has access to
tags: tags:
- assignees - labels
put: put:
consumes: consumes:
- application/json - application/json
@ -977,9 +978,9 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Returns a list by its ID. description: Returns a team by its ID.
parameters: parameters:
- description: List ID - description: Team ID
in: path in: path
name: id name: id
required: true required: true
@ -988,12 +989,12 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: The list description: The team
schema: schema:
$ref: '#/definitions/models.List' $ref: '#/definitions/models.Team'
type: object type: object
"403": "403":
description: The user does not have access to the list description: The user does not have access to the team
schema: schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError' $ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object type: object
@ -1004,9 +1005,9 @@ paths:
type: object type: object
security: security:
- JWTKeyAuth: [] - JWTKeyAuth: []
summary: Gets one list summary: Gets one team
tags: tags:
- list - team
post: post:
consumes: consumes:
- application/json - application/json
@ -1097,6 +1098,51 @@ paths:
summary: Create a task summary: Create a task
tags: tags:
- task - task
/lists/{id}/listusers:
get:
consumes:
- application/json
description: Lists all users (without emailadresses). Also possible to search
for a specific user.
parameters:
- description: Search for a user by its name.
in: query
name: s
type: string
- description: List ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: All (found) users.
schema:
items:
$ref: '#/definitions/models.User'
type: array
"400":
description: Something's invalid.
schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object
"401":
description: The user does not have the right to see the list.
schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError'
type: object
"500":
description: Internal server error.
schema:
$ref: '#/definitions/models.Message'
type: object
security:
- JWTKeyAuth: []
summary: Get users
tags:
- list
/lists/{id}/teams: /lists/{id}/teams:
get: get:
consumes: consumes: