From 72cbefe63e5b52212c52ee9fd70b125bed0d0a48 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Sat, 23 Nov 2024 10:33:55 +0100 Subject: [PATCH 1/2] Improve Swagger documentation for user endpoints (cherry picked from commit b074e08f34b107a1a5251fa2a96657ef15fd8834) --- routers/api/v1/org/org.go | 4 + routers/api/v1/org/team.go | 4 + routers/api/v1/repo/issue_stopwatch.go | 4 + routers/api/v1/repo/issue_tracked_time.go | 4 + routers/api/v1/repo/repo.go | 4 + routers/api/v1/user/action.go | 28 ++ routers/api/v1/user/app.go | 24 ++ routers/api/v1/user/avatar.go | 8 + routers/api/v1/user/email.go | 12 + routers/api/v1/user/follower.go | 22 +- routers/api/v1/user/gpg_key.go | 22 ++ routers/api/v1/user/hook.go | 20 ++ routers/api/v1/user/key.go | 14 + routers/api/v1/user/quota.go | 10 + routers/api/v1/user/repo.go | 4 + routers/api/v1/user/runners.go | 4 + routers/api/v1/user/settings.go | 8 + routers/api/v1/user/star.go | 16 + routers/api/v1/user/user.go | 16 + routers/api/v1/user/watch.go | 4 + services/context/api.go | 11 + templates/swagger/v1_json.tmpl | 362 ++++++++++++++++++++++ tests/integration/swagger_test.go | 100 ++++++ 23 files changed, 703 insertions(+), 2 deletions(-) create mode 100644 tests/integration/swagger_test.go diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 95c8c25d8e..3623b85b96 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -68,6 +68,10 @@ func ListMyOrgs(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/OrganizationList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index da4fc13ea1..bf28d54c42 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -91,6 +91,10 @@ func ListUserTeams(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/TeamList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{ ListOptions: utils.GetListOptions(ctx), diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index d9054e8f77..dd61967ed0 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -217,6 +217,10 @@ func GetStopwatches(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/StopWatchList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" sws, err := issues_model.GetUserStopwatches(ctx, ctx.Doer.ID, utils.GetListOptions(ctx)) if err != nil { diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index f83855efac..3d8abfa5f3 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -599,6 +599,10 @@ func ListMyTrackedTimes(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/TrackedTimeList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" opts := &issues_model.FindTrackedTimesOptions{ ListOptions: utils.GetListOptions(ctx), diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index e25593cf6d..f39e58231b 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -306,6 +306,10 @@ func Create(ctx *context.APIContext) { // "$ref": "#/responses/Repository" // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "409": // description: The repository with the same name already exists. // "413": diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go index bf78c2c864..ec5289fdb0 100644 --- a/routers/api/v1/user/action.go +++ b/routers/api/v1/user/action.go @@ -44,6 +44,10 @@ func CreateOrUpdateSecret(ctx *context.APIContext) { // description: response when updating a secret // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -88,6 +92,10 @@ func DeleteSecret(ctx *context.APIContext) { // description: delete one secret of the user // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -132,6 +140,10 @@ func CreateVariable(ctx *context.APIContext) { // description: response when creating a variable // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -191,6 +203,10 @@ func UpdateVariable(ctx *context.APIContext) { // description: response when updating a variable // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -244,6 +260,10 @@ func DeleteVariable(ctx *context.APIContext) { // description: response when deleting a variable // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -279,6 +299,10 @@ func GetVariable(ctx *context.APIContext) { // "$ref": "#/responses/ActionVariable" // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -326,6 +350,10 @@ func ListVariables(ctx *context.APIContext) { // "$ref": "#/responses/VariableList" // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index d5b20f7703..c4fb2ea38d 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -46,6 +46,8 @@ func ListAccessTokens(ctx *context.APIContext) { // "$ref": "#/responses/AccessTokenList" // "403": // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)} @@ -95,6 +97,8 @@ func CreateAccessToken(ctx *context.APIContext) { // "$ref": "#/responses/error" // "403": // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" form := web.GetForm(ctx).(*api.CreateAccessTokenOption) @@ -224,6 +228,10 @@ func CreateOauth2Application(ctx *context.APIContext) { // "$ref": "#/responses/OAuth2Application" // "400": // "$ref": "#/responses/error" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) @@ -266,6 +274,10 @@ func ListOauth2Applications(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/OAuth2ApplicationList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" apps, total, err := db.FindAndCount[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ ListOptions: utils.GetListOptions(ctx), @@ -303,6 +315,10 @@ func DeleteOauth2Application(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" appID := ctx.ParamsInt64(":id") @@ -335,6 +351,10 @@ func GetOauth2Application(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/OAuth2Application" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" appID := ctx.ParamsInt64(":id") @@ -379,6 +399,10 @@ func UpdateOauth2Application(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/OAuth2Application" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" appID := ctx.ParamsInt64(":id") diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go index 30ccb63587..d3833a32bb 100644 --- a/routers/api/v1/user/avatar.go +++ b/routers/api/v1/user/avatar.go @@ -28,6 +28,10 @@ func UpdateAvatar(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" form := web.GetForm(ctx).(*api.UpdateUserAvatarOption) content, err := base64.StdEncoding.DecodeString(form.Image) @@ -55,6 +59,10 @@ func DeleteAvatar(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" err := user_service.DeleteAvatar(ctx, ctx.Doer) if err != nil { ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err) diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 33aa851a80..af5d355ecd 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -26,6 +26,10 @@ func ListEmails(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/EmailList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" emails, err := user_model.GetEmailAddresses(ctx, ctx.Doer.ID) if err != nil { @@ -54,6 +58,10 @@ func AddEmail(ctx *context.APIContext) { // responses: // '201': // "$ref": "#/responses/EmailList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" @@ -111,6 +119,10 @@ func DeleteEmail(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 1ba7346b8c..784e2325a3 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -53,6 +53,10 @@ func ListMyFollowers(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/UserList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" listUserFollowers(ctx, ctx.Doer) } @@ -117,6 +121,10 @@ func ListMyFollowing(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/UserList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" listUserFollowing(ctx, ctx.Doer) } @@ -173,6 +181,10 @@ func CheckMyFollowing(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -222,10 +234,12 @@ func Follow(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - // "404": - // "$ref": "#/responses/notFound" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" if err := user_model.FollowUser(ctx, ctx.Doer.ID, ctx.ContextUser.ID); err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { @@ -252,6 +266,10 @@ func Unfollow(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 5a2f995e1b..2fe4eb8e78 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -92,6 +92,10 @@ func ListMyGPGKeys(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/GPGKeyList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" listGPGKeys(ctx, ctx.Doer.ID, utils.GetListOptions(ctx)) } @@ -113,6 +117,10 @@ func GetGPGKey(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/GPGKey" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -164,6 +172,10 @@ func GetVerificationToken(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/string" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -183,6 +195,10 @@ func VerifyUserGPGKey(ctx *context.APIContext) { // responses: // "201": // "$ref": "#/responses/GPGKey" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": @@ -244,6 +260,10 @@ func CreateGPGKey(ctx *context.APIContext) { // responses: // "201": // "$ref": "#/responses/GPGKey" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": @@ -270,6 +290,8 @@ func DeleteGPGKey(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" // "404": diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go index 9d9ca5bf01..47b6498d85 100644 --- a/routers/api/v1/user/hook.go +++ b/routers/api/v1/user/hook.go @@ -32,6 +32,10 @@ func ListHooks(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/HookList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" utils.ListOwnerHooks( ctx, @@ -56,6 +60,10 @@ func GetHook(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/Hook" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" hook, err := utils.GetOwnerHook(ctx, ctx.Doer.ID, ctx.ParamsInt64("id")) if err != nil { @@ -93,6 +101,10 @@ func CreateHook(ctx *context.APIContext) { // responses: // "201": // "$ref": "#/responses/Hook" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" utils.AddOwnerHook( ctx, @@ -124,6 +136,10 @@ func EditHook(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/Hook" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" utils.EditOwnerHook( ctx, @@ -150,6 +166,10 @@ func DeleteHook(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" utils.DeleteOwnerHook( ctx, diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index d9456e7ec6..1b4ba0a40f 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -121,6 +121,10 @@ func ListMyPublicKeys(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/PublicKeyList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" listPublicKeys(ctx, ctx.Doer) } @@ -176,6 +180,10 @@ func GetPublicKey(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/PublicKey" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -240,6 +248,10 @@ func CreatePublicKey(ctx *context.APIContext) { // responses: // "201": // "$ref": "#/responses/PublicKey" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" @@ -264,6 +276,8 @@ func DeletePublicKey(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" // "404": diff --git a/routers/api/v1/user/quota.go b/routers/api/v1/user/quota.go index 573d7b7fbc..ab2881b355 100644 --- a/routers/api/v1/user/quota.go +++ b/routers/api/v1/user/quota.go @@ -18,6 +18,8 @@ func GetQuota(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/QuotaInfo" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" @@ -34,6 +36,8 @@ func CheckQuota(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/boolean" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" // "422": @@ -61,6 +65,8 @@ func ListQuotaAttachments(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/QuotaUsedAttachmentList" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" @@ -86,6 +92,8 @@ func ListQuotaPackages(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/QuotaUsedPackageList" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" @@ -111,6 +119,8 @@ func ListQuotaArtifacts(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/QuotaUsedArtifactList" + // "401": + // "$ref": "#/responses/unauthorized" // "403": // "$ref": "#/responses/forbidden" diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 86716ff44f..f2e11e4562 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -106,6 +106,10 @@ func ListMyRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" diff --git a/routers/api/v1/user/runners.go b/routers/api/v1/user/runners.go index 899218473e..dc4c187ffe 100644 --- a/routers/api/v1/user/runners.go +++ b/routers/api/v1/user/runners.go @@ -21,6 +21,10 @@ func GetRegistrationToken(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RegistrationToken" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0) } diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go index bfd24013db..173f06e474 100644 --- a/routers/api/v1/user/settings.go +++ b/routers/api/v1/user/settings.go @@ -24,6 +24,10 @@ func GetUserSettings(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/UserSettings" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" ctx.JSON(http.StatusOK, convert.User2UserSettings(ctx.Doer)) } @@ -42,6 +46,10 @@ func UpdateUserSettings(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/UserSettings" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" form := web.GetForm(ctx).(*api.UserSettingsOptions) diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index cb9e05f791..be84b13204 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -96,6 +96,10 @@ func GetMyStarredRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" repos, err := getStarredRepos(ctx, ctx.Doer, true, utils.GetListOptions(ctx)) if err != nil { @@ -125,6 +129,10 @@ func IsStarring(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -154,6 +162,10 @@ func Star(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" @@ -185,6 +197,10 @@ func Unstar(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 4efcb7cc4f..6c8cde71d3 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -136,6 +136,10 @@ func GetAuthenticatedUser(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/User" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.Doer, ctx.Doer)) } @@ -243,6 +247,10 @@ func ListBlockedUsers(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/BlockedUserList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" utils.ListUserBlockedUsers(ctx, ctx.Doer) } @@ -263,6 +271,10 @@ func BlockUser(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": @@ -292,6 +304,10 @@ func UnblockUser(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 706f4cc66b..dc27a38a03 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -91,6 +91,10 @@ func GetMyWatchedRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" + // "401": + // "$ref": "#/responses/unauthorized" + // "403": + // "$ref": "#/responses/forbidden" repos, total, err := getWatchedRepos(ctx, ctx.Doer, true, utils.GetListOptions(ctx)) if err != nil { diff --git a/services/context/api.go b/services/context/api.go index c560994258..396ceb520f 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -99,6 +99,17 @@ type swaggerAPIInvalidTopicsError struct { // swagger:response empty type APIEmpty struct{} +type APIUnauthorizedError struct { + APIError +} + +// APIUnauthorizedError is a unauthorized error response +// swagger:response unauthorized +type swaggerAPUnauthorizedError struct { + // in:body + Body APIUnauthorizedError `json:"body"` +} + type APIForbiddenError struct { APIError } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d47f965286..c7c444107f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -17177,6 +17177,12 @@ "responses": { "200": { "$ref": "#/responses/User" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17194,6 +17200,12 @@ "responses": { "200": { "$ref": "#/responses/RegistrationToken" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17237,6 +17249,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17270,6 +17288,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17307,6 +17331,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17339,6 +17369,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17382,6 +17418,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17425,6 +17467,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17458,6 +17506,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17491,6 +17545,12 @@ "responses": { "200": { "$ref": "#/responses/OAuth2ApplicationList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -17519,6 +17579,12 @@ }, "400": { "$ref": "#/responses/error" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17547,6 +17613,12 @@ "200": { "$ref": "#/responses/OAuth2Application" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17575,6 +17647,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17611,6 +17689,12 @@ "200": { "$ref": "#/responses/OAuth2Application" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17639,6 +17723,12 @@ "responses": { "204": { "$ref": "#/responses/empty" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -17654,6 +17744,12 @@ "responses": { "204": { "$ref": "#/responses/empty" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17681,6 +17777,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" }, @@ -17703,6 +17805,12 @@ "responses": { "200": { "$ref": "#/responses/EmailList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -17728,6 +17836,12 @@ "201": { "$ref": "#/responses/EmailList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "422": { "$ref": "#/responses/validationError" } @@ -17755,6 +17869,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17788,6 +17908,12 @@ "responses": { "200": { "$ref": "#/responses/UserList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17819,6 +17945,12 @@ "responses": { "200": { "$ref": "#/responses/UserList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17843,6 +17975,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17867,6 +18005,9 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" }, @@ -17894,6 +18035,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17914,6 +18061,12 @@ "200": { "$ref": "#/responses/string" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17937,6 +18090,12 @@ "201": { "$ref": "#/responses/GPGKey" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" }, @@ -17973,6 +18132,12 @@ "responses": { "200": { "$ref": "#/responses/GPGKeyList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18001,6 +18166,12 @@ "201": { "$ref": "#/responses/GPGKey" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" }, @@ -18034,6 +18205,12 @@ "200": { "$ref": "#/responses/GPGKey" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18062,6 +18239,9 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" }, @@ -18098,6 +18278,12 @@ "responses": { "200": { "$ref": "#/responses/HookList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18126,6 +18312,12 @@ "responses": { "201": { "$ref": "#/responses/Hook" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18153,6 +18345,12 @@ "responses": { "200": { "$ref": "#/responses/Hook" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18178,6 +18376,12 @@ "responses": { "204": { "$ref": "#/responses/empty" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18213,6 +18417,12 @@ "responses": { "200": { "$ref": "#/responses/Hook" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18250,6 +18460,12 @@ "responses": { "200": { "$ref": "#/responses/PublicKeyList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18278,6 +18494,12 @@ "201": { "$ref": "#/responses/PublicKey" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "422": { "$ref": "#/responses/validationError" } @@ -18308,6 +18530,12 @@ "200": { "$ref": "#/responses/PublicKey" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18336,6 +18564,9 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" }, @@ -18372,6 +18603,12 @@ "responses": { "200": { "$ref": "#/responses/BlockedUserList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18404,6 +18641,12 @@ "200": { "$ref": "#/responses/OrganizationList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18424,6 +18667,9 @@ "200": { "$ref": "#/responses/QuotaInfo" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" } @@ -18458,6 +18704,9 @@ "200": { "$ref": "#/responses/QuotaUsedArtifactList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" } @@ -18492,6 +18741,9 @@ "200": { "$ref": "#/responses/QuotaUsedAttachmentList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" } @@ -18512,6 +18764,9 @@ "200": { "$ref": "#/responses/boolean" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" }, @@ -18549,6 +18804,9 @@ "200": { "$ref": "#/responses/QuotaUsedPackageList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, "403": { "$ref": "#/responses/forbidden" } @@ -18589,6 +18847,12 @@ "200": { "$ref": "#/responses/RepositoryList" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "422": { "$ref": "#/responses/validationError" } @@ -18623,6 +18887,12 @@ "400": { "$ref": "#/responses/error" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "409": { "description": "The repository with the same name already exists." }, @@ -18648,6 +18918,12 @@ "responses": { "200": { "$ref": "#/responses/UserSettings" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -18672,6 +18948,12 @@ "responses": { "200": { "$ref": "#/responses/UserSettings" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18703,6 +18985,12 @@ "responses": { "200": { "$ref": "#/responses/RepositoryList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18734,6 +19022,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18765,6 +19059,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18796,6 +19096,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18832,6 +19138,12 @@ "responses": { "200": { "$ref": "#/responses/StopWatchList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18863,6 +19175,12 @@ "responses": { "200": { "$ref": "#/responses/RepositoryList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18894,6 +19212,12 @@ "responses": { "200": { "$ref": "#/responses/TeamList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18939,6 +19263,12 @@ "responses": { "200": { "$ref": "#/responses/TrackedTimeList" + }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -18966,6 +19296,12 @@ "204": { "$ref": "#/responses/empty" }, + "401": { + "$ref": "#/responses/unauthorized" + }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" }, @@ -19588,6 +19924,9 @@ }, "403": { "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" } } }, @@ -19628,6 +19967,9 @@ }, "403": { "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" } } } @@ -19774,6 +20116,20 @@ }, "x-go-package": "code.gitea.io/gitea/services/context" }, + "APIUnauthorizedError": { + "type": "object", + "properties": { + "message": { + "type": "string", + "x-go-name": "Message" + }, + "url": { + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-package": "code.gitea.io/gitea/services/context" + }, "APIValidationError": { "type": "object", "properties": { @@ -28340,6 +28696,12 @@ "type": "string" } }, + "unauthorized": { + "description": "APIUnauthorizedError is a unauthorized error response", + "schema": { + "$ref": "#/definitions/APIUnauthorizedError" + } + }, "validationError": { "description": "APIValidationError is error format response related to input validation", "schema": { diff --git a/tests/integration/swagger_test.go b/tests/integration/swagger_test.go new file mode 100644 index 0000000000..584601f7f9 --- /dev/null +++ b/tests/integration/swagger_test.go @@ -0,0 +1,100 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "strings" + "testing" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/tests" + + swagger_spec "github.com/go-openapi/spec" + "github.com/stretchr/testify/require" +) + +func getSwagger(t *testing.T) *swagger_spec.Swagger { + t.Helper() + + resp := MakeRequest(t, NewRequest(t, "GET", "/swagger.v1.json"), http.StatusOK) + + swagger := new(swagger_spec.Swagger) + + decoder := json.NewDecoder(resp.Body) + require.NoError(t, decoder.Decode(swagger)) + + return swagger +} + +func checkSwaggerMethodResponse(t *testing.T, path string, method *swagger_spec.Operation, name string, statusCode int, responseType string) { + t.Helper() + + if method == nil { + return + } + + val, ok := method.Responses.StatusCodeResponses[statusCode] + if !ok { + t.Errorf("%s %s is missing response status code %d in swagger", name, path, statusCode) + return + } + + if responseType != val.Ref.String() { + t.Errorf("%s %s has %s response type for %d in swagger (expected %s)", name, path, val.Ref.String(), statusCode, responseType) + } +} + +func checkSwaggerPathResponse(t *testing.T, paths map[string]swagger_spec.PathItem, pathMatch string, statusCode int, responseType string) { + t.Helper() + + for pathName, pathData := range paths { + if pathName != pathMatch { + continue + } + + checkSwaggerMethodResponse(t, pathName, pathData.Get, "GET", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Put, "PUT", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Post, "POST", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Patch, "PATCH", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Delete, "DELETE", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Options, "OPTIONS", statusCode, responseType) + + return + } +} + +func checkSwaggerRouteResponse(t *testing.T, paths map[string]swagger_spec.PathItem, prefix string, statusCode int, responseType string) { + t.Helper() + + for pathName, pathData := range paths { + if !strings.HasPrefix(pathName, prefix) { + continue + } + + checkSwaggerMethodResponse(t, pathName, pathData.Get, "GET", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Put, "PUT", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Post, "POST", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Patch, "PATCH", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Delete, "DELETE", statusCode, responseType) + checkSwaggerMethodResponse(t, pathName, pathData.Options, "OPTIONS", statusCode, responseType) + } +} + +func TestSwaggerUserRoute(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + swagger := getSwagger(t) + + checkSwaggerPathResponse(t, swagger.Paths.Paths, "/user", http.StatusUnauthorized, "#/responses/unauthorized") + checkSwaggerRouteResponse(t, swagger.Paths.Paths, "/user/", http.StatusUnauthorized, "#/responses/unauthorized") +} + +func TestSwaggerUsersRoute(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + swagger := getSwagger(t) + + checkSwaggerRouteResponse(t, swagger.Paths.Paths, "/users/{username}", http.StatusNotFound, "#/responses/notFound") +} From d3e5d887ee61b5d84c67f3942e54d3fc92f288be Mon Sep 17 00:00:00 2001 From: JakobDev Date: Thu, 28 Nov 2024 19:36:55 +0100 Subject: [PATCH 2/2] Run make tidy (cherry picked from commit 76fb2afc40c52beeed0551454c04335f48ecf0c1) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4b5696410b..764d3213a6 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e github.com/go-git/go-git/v5 v5.11.0 github.com/go-ldap/ldap/v3 v3.4.6 + github.com/go-openapi/spec v0.20.14 github.com/go-sql-driver/mysql v1.8.1 github.com/go-swagger/go-swagger v0.30.5 github.com/go-testfixtures/testfixtures/v3 v3.12.0 @@ -187,7 +188,6 @@ require ( github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/go-openapi/loads v0.21.5 // indirect github.com/go-openapi/runtime v0.26.2 // indirect - github.com/go-openapi/spec v0.20.14 // indirect github.com/go-openapi/strfmt v0.22.0 // indirect github.com/go-openapi/swag v0.22.7 // indirect github.com/go-openapi/validate v0.22.6 // indirect