You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collaborators.go 10KB

Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
4 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
4 years ago
API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath <art27@cantab.net> * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick <matti@mdranta.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
4 years ago
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
1 year ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
3 years ago
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
1 year ago
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
1 year ago
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "errors"
  7. "net/http"
  8. "code.gitea.io/gitea/models/perm"
  9. access_model "code.gitea.io/gitea/models/perm/access"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. user_model "code.gitea.io/gitea/models/user"
  12. "code.gitea.io/gitea/modules/context"
  13. repo_module "code.gitea.io/gitea/modules/repository"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/web"
  16. "code.gitea.io/gitea/routers/api/v1/utils"
  17. "code.gitea.io/gitea/services/convert"
  18. repo_service "code.gitea.io/gitea/services/repository"
  19. )
  20. // ListCollaborators list a repository's collaborators
  21. func ListCollaborators(ctx *context.APIContext) {
  22. // swagger:operation GET /repos/{owner}/{repo}/collaborators repository repoListCollaborators
  23. // ---
  24. // summary: List a repository's collaborators
  25. // produces:
  26. // - application/json
  27. // parameters:
  28. // - name: owner
  29. // in: path
  30. // description: owner of the repo
  31. // type: string
  32. // required: true
  33. // - name: repo
  34. // in: path
  35. // description: name of the repo
  36. // type: string
  37. // required: true
  38. // - name: page
  39. // in: query
  40. // description: page number of results to return (1-based)
  41. // type: integer
  42. // - name: limit
  43. // in: query
  44. // description: page size of results
  45. // type: integer
  46. // responses:
  47. // "200":
  48. // "$ref": "#/responses/UserList"
  49. // "404":
  50. // "$ref": "#/responses/notFound"
  51. count, err := repo_model.CountCollaborators(ctx, ctx.Repo.Repository.ID)
  52. if err != nil {
  53. ctx.InternalServerError(err)
  54. return
  55. }
  56. collaborators, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
  57. if err != nil {
  58. ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
  59. return
  60. }
  61. users := make([]*api.User, len(collaborators))
  62. for i, collaborator := range collaborators {
  63. users[i] = convert.ToUser(ctx, collaborator.User, ctx.Doer)
  64. }
  65. ctx.SetTotalCountHeader(count)
  66. ctx.JSON(http.StatusOK, users)
  67. }
  68. // IsCollaborator check if a user is a collaborator of a repository
  69. func IsCollaborator(ctx *context.APIContext) {
  70. // swagger:operation GET /repos/{owner}/{repo}/collaborators/{collaborator} repository repoCheckCollaborator
  71. // ---
  72. // summary: Check if a user is a collaborator of a repository
  73. // produces:
  74. // - application/json
  75. // parameters:
  76. // - name: owner
  77. // in: path
  78. // description: owner of the repo
  79. // type: string
  80. // required: true
  81. // - name: repo
  82. // in: path
  83. // description: name of the repo
  84. // type: string
  85. // required: true
  86. // - name: collaborator
  87. // in: path
  88. // description: username of the collaborator
  89. // type: string
  90. // required: true
  91. // responses:
  92. // "204":
  93. // "$ref": "#/responses/empty"
  94. // "404":
  95. // "$ref": "#/responses/notFound"
  96. // "422":
  97. // "$ref": "#/responses/validationError"
  98. user, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator"))
  99. if err != nil {
  100. if user_model.IsErrUserNotExist(err) {
  101. ctx.Error(http.StatusUnprocessableEntity, "", err)
  102. } else {
  103. ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
  104. }
  105. return
  106. }
  107. isColab, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, user.ID)
  108. if err != nil {
  109. ctx.Error(http.StatusInternalServerError, "IsCollaborator", err)
  110. return
  111. }
  112. if isColab {
  113. ctx.Status(http.StatusNoContent)
  114. } else {
  115. ctx.NotFound()
  116. }
  117. }
  118. // AddCollaborator add a collaborator to a repository
  119. func AddCollaborator(ctx *context.APIContext) {
  120. // swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator
  121. // ---
  122. // summary: Add a collaborator to a repository
  123. // produces:
  124. // - application/json
  125. // parameters:
  126. // - name: owner
  127. // in: path
  128. // description: owner of the repo
  129. // type: string
  130. // required: true
  131. // - name: repo
  132. // in: path
  133. // description: name of the repo
  134. // type: string
  135. // required: true
  136. // - name: collaborator
  137. // in: path
  138. // description: username of the collaborator to add
  139. // type: string
  140. // required: true
  141. // - name: body
  142. // in: body
  143. // schema:
  144. // "$ref": "#/definitions/AddCollaboratorOption"
  145. // responses:
  146. // "204":
  147. // "$ref": "#/responses/empty"
  148. // "404":
  149. // "$ref": "#/responses/notFound"
  150. // "422":
  151. // "$ref": "#/responses/validationError"
  152. form := web.GetForm(ctx).(*api.AddCollaboratorOption)
  153. collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator"))
  154. if err != nil {
  155. if user_model.IsErrUserNotExist(err) {
  156. ctx.Error(http.StatusUnprocessableEntity, "", err)
  157. } else {
  158. ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
  159. }
  160. return
  161. }
  162. if !collaborator.IsActive {
  163. ctx.Error(http.StatusInternalServerError, "InactiveCollaborator", errors.New("collaborator's account is inactive"))
  164. return
  165. }
  166. if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil {
  167. ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
  168. return
  169. }
  170. if form.Permission != nil {
  171. if err := repo_model.ChangeCollaborationAccessMode(ctx, ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil {
  172. ctx.Error(http.StatusInternalServerError, "ChangeCollaborationAccessMode", err)
  173. return
  174. }
  175. }
  176. ctx.Status(http.StatusNoContent)
  177. }
  178. // DeleteCollaborator delete a collaborator from a repository
  179. func DeleteCollaborator(ctx *context.APIContext) {
  180. // swagger:operation DELETE /repos/{owner}/{repo}/collaborators/{collaborator} repository repoDeleteCollaborator
  181. // ---
  182. // summary: Delete a collaborator from a repository
  183. // produces:
  184. // - application/json
  185. // parameters:
  186. // - name: owner
  187. // in: path
  188. // description: owner of the repo
  189. // type: string
  190. // required: true
  191. // - name: repo
  192. // in: path
  193. // description: name of the repo
  194. // type: string
  195. // required: true
  196. // - name: collaborator
  197. // in: path
  198. // description: username of the collaborator to delete
  199. // type: string
  200. // required: true
  201. // responses:
  202. // "204":
  203. // "$ref": "#/responses/empty"
  204. // "404":
  205. // "$ref": "#/responses/notFound"
  206. // "422":
  207. // "$ref": "#/responses/validationError"
  208. collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator"))
  209. if err != nil {
  210. if user_model.IsErrUserNotExist(err) {
  211. ctx.Error(http.StatusUnprocessableEntity, "", err)
  212. } else {
  213. ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
  214. }
  215. return
  216. }
  217. if err := repo_service.DeleteCollaboration(ctx, ctx.Repo.Repository, collaborator.ID); err != nil {
  218. ctx.Error(http.StatusInternalServerError, "DeleteCollaboration", err)
  219. return
  220. }
  221. ctx.Status(http.StatusNoContent)
  222. }
  223. // GetRepoPermissions gets repository permissions for a user
  224. func GetRepoPermissions(ctx *context.APIContext) {
  225. // swagger:operation GET /repos/{owner}/{repo}/collaborators/{collaborator}/permission repository repoGetRepoPermissions
  226. // ---
  227. // summary: Get repository permissions for a user
  228. // produces:
  229. // - application/json
  230. // parameters:
  231. // - name: owner
  232. // in: path
  233. // description: owner of the repo
  234. // type: string
  235. // required: true
  236. // - name: repo
  237. // in: path
  238. // description: name of the repo
  239. // type: string
  240. // required: true
  241. // - name: collaborator
  242. // in: path
  243. // description: username of the collaborator
  244. // type: string
  245. // required: true
  246. // responses:
  247. // "200":
  248. // "$ref": "#/responses/RepoCollaboratorPermission"
  249. // "404":
  250. // "$ref": "#/responses/notFound"
  251. // "403":
  252. // "$ref": "#/responses/forbidden"
  253. if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.Params(":collaborator") && !ctx.IsUserRepoAdmin() {
  254. ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
  255. return
  256. }
  257. collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator"))
  258. if err != nil {
  259. if user_model.IsErrUserNotExist(err) {
  260. ctx.Error(http.StatusNotFound, "GetUserByName", err)
  261. } else {
  262. ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
  263. }
  264. return
  265. }
  266. permission, err := access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, collaborator)
  267. if err != nil {
  268. ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
  269. return
  270. }
  271. ctx.JSON(http.StatusOK, convert.ToUserAndPermission(ctx, collaborator, ctx.ContextUser, permission.AccessMode))
  272. }
  273. // GetReviewers return all users that can be requested to review in this repo
  274. func GetReviewers(ctx *context.APIContext) {
  275. // swagger:operation GET /repos/{owner}/{repo}/reviewers repository repoGetReviewers
  276. // ---
  277. // summary: Return all users that can be requested to review in this repo
  278. // produces:
  279. // - application/json
  280. // parameters:
  281. // - name: owner
  282. // in: path
  283. // description: owner of the repo
  284. // type: string
  285. // required: true
  286. // - name: repo
  287. // in: path
  288. // description: name of the repo
  289. // type: string
  290. // required: true
  291. // responses:
  292. // "200":
  293. // "$ref": "#/responses/UserList"
  294. // "404":
  295. // "$ref": "#/responses/notFound"
  296. reviewers, err := repo_model.GetReviewers(ctx, ctx.Repo.Repository, ctx.Doer.ID, 0)
  297. if err != nil {
  298. ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
  299. return
  300. }
  301. ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, reviewers))
  302. }
  303. // GetAssignees return all users that have write access and can be assigned to issues
  304. func GetAssignees(ctx *context.APIContext) {
  305. // swagger:operation GET /repos/{owner}/{repo}/assignees repository repoGetAssignees
  306. // ---
  307. // summary: Return all users that have write access and can be assigned to issues
  308. // produces:
  309. // - application/json
  310. // parameters:
  311. // - name: owner
  312. // in: path
  313. // description: owner of the repo
  314. // type: string
  315. // required: true
  316. // - name: repo
  317. // in: path
  318. // description: name of the repo
  319. // type: string
  320. // required: true
  321. // responses:
  322. // "200":
  323. // "$ref": "#/responses/UserList"
  324. // "404":
  325. // "$ref": "#/responses/notFound"
  326. assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
  327. if err != nil {
  328. ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
  329. return
  330. }
  331. ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, assignees))
  332. }