2019-09-03 18:46:24 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2019-09-03 18:46:24 +03:00
package repo
import (
"net/http"
"strings"
2021-12-12 18:48:20 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2019-09-03 18:46:24 +03:00
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2020-01-24 22:00:29 +03:00
"code.gitea.io/gitea/routers/api/v1/utils"
2022-12-29 05:57:15 +03:00
"code.gitea.io/gitea/services/convert"
2019-09-03 18:46:24 +03:00
)
// ListTopics returns list of current topics for repo
func ListTopics ( ctx * context . APIContext ) {
// swagger:operation GET /repos/{owner}/{repo}/topics repository repoListTopics
// ---
// summary: Get list of topics that a repository has
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
2020-01-24 22:00:29 +03:00
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 07:57:38 +03:00
// description: page size of results
2020-01-24 22:00:29 +03:00
// type: integer
2019-09-03 18:46:24 +03:00
// responses:
// "200":
// "$ref": "#/responses/TopicNames"
2023-09-13 05:37:54 +03:00
// "404":
// "$ref": "#/responses/notFound"
2019-09-03 18:46:24 +03:00
2021-12-12 18:48:20 +03:00
opts := & repo_model . FindTopicOptions {
2020-01-24 22:00:29 +03:00
ListOptions : utils . GetListOptions ( ctx ) ,
RepoID : ctx . Repo . Repository . ID ,
2021-08-12 15:43:08 +03:00
}
2023-09-16 17:39:12 +03:00
topics , total , err := repo_model . FindTopics ( ctx , opts )
2019-09-03 18:46:24 +03:00
if err != nil {
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
topicNames := make ( [ ] string , len ( topics ) )
for i , topic := range topics {
topicNames [ i ] = topic . Name
}
2021-08-12 15:43:08 +03:00
ctx . SetTotalCountHeader ( total )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2019-09-03 18:46:24 +03:00
"topics" : topicNames ,
} )
}
// UpdateTopics updates repo with a new set of topics
2021-01-26 18:36:53 +03:00
func UpdateTopics ( ctx * context . APIContext ) {
2019-09-03 18:46:24 +03:00
// swagger:operation PUT /repos/{owner}/{repo}/topics repository repoUpdateTopics
// ---
// summary: Replace list of topics for a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/RepoTopicOptions"
// responses:
// "204":
// "$ref": "#/responses/empty"
2023-09-13 05:37:54 +03:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-20 20:07:12 +03:00
// "422":
// "$ref": "#/responses/invalidTopicsError"
2019-09-03 18:46:24 +03:00
2021-01-26 18:36:53 +03:00
form := web . GetForm ( ctx ) . ( * api . RepoTopicOptions )
2019-09-03 18:46:24 +03:00
topicNames := form . Topics
2021-12-12 18:48:20 +03:00
validTopics , invalidTopics := repo_model . SanitizeAndValidateTopics ( topicNames )
2019-09-03 18:46:24 +03:00
if len ( validTopics ) > 25 {
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] any {
2019-09-03 18:46:24 +03:00
"invalidTopics" : nil ,
"message" : "Exceeding maximum number of topics per repo" ,
} )
return
}
if len ( invalidTopics ) > 0 {
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] any {
2019-09-03 18:46:24 +03:00
"invalidTopics" : invalidTopics ,
"message" : "Topic names are invalid" ,
} )
return
}
2023-09-16 17:39:12 +03:00
err := repo_model . SaveTopics ( ctx , ctx . Repo . Repository . ID , validTopics ... )
2019-09-03 18:46:24 +03:00
if err != nil {
log . Error ( "SaveTopics failed: %v" , err )
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
ctx . Status ( http . StatusNoContent )
}
// AddTopic adds a topic name to a repo
func AddTopic ( ctx * context . APIContext ) {
2021-08-01 19:28:05 +03:00
// swagger:operation PUT /repos/{owner}/{repo}/topics/{topic} repository repoAddTopic
2019-09-03 18:46:24 +03:00
// ---
// summary: Add a topic to a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: topic
// in: path
// description: name of the topic to add
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
2023-09-13 05:37:54 +03:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-20 20:07:12 +03:00
// "422":
// "$ref": "#/responses/invalidTopicsError"
2019-09-03 18:46:24 +03:00
topicName := strings . TrimSpace ( strings . ToLower ( ctx . Params ( ":topic" ) ) )
2021-12-12 18:48:20 +03:00
if ! repo_model . ValidateTopic ( topicName ) {
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] any {
2019-12-20 20:07:12 +03:00
"invalidTopics" : topicName ,
"message" : "Topic name is invalid" ,
} )
2019-09-03 18:46:24 +03:00
return
}
// Prevent adding more topics than allowed to repo
2023-09-16 17:39:12 +03:00
count , err := repo_model . CountTopics ( ctx , & repo_model . FindTopicOptions {
2019-09-03 18:46:24 +03:00
RepoID : ctx . Repo . Repository . ID ,
} )
if err != nil {
2021-08-12 15:43:08 +03:00
log . Error ( "CountTopics failed: %v" , err )
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
2021-08-12 15:43:08 +03:00
if count >= 25 {
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] any {
2019-09-03 18:46:24 +03:00
"message" : "Exceeding maximum allowed topics per repo." ,
} )
return
}
2023-09-16 17:39:12 +03:00
_ , err = repo_model . AddTopic ( ctx , ctx . Repo . Repository . ID , topicName )
2019-09-03 18:46:24 +03:00
if err != nil {
log . Error ( "AddTopic failed: %v" , err )
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
ctx . Status ( http . StatusNoContent )
}
// DeleteTopic removes topic name from repo
func DeleteTopic ( ctx * context . APIContext ) {
// swagger:operation DELETE /repos/{owner}/{repo}/topics/{topic} repository repoDeleteTopic
// ---
// summary: Delete a topic from a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: topic
// in: path
// description: name of the topic to delete
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
2023-09-13 05:37:54 +03:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-20 20:07:12 +03:00
// "422":
// "$ref": "#/responses/invalidTopicsError"
2019-09-03 18:46:24 +03:00
topicName := strings . TrimSpace ( strings . ToLower ( ctx . Params ( ":topic" ) ) )
2021-12-12 18:48:20 +03:00
if ! repo_model . ValidateTopic ( topicName ) {
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] any {
2019-12-20 20:07:12 +03:00
"invalidTopics" : topicName ,
"message" : "Topic name is invalid" ,
} )
2019-09-03 18:46:24 +03:00
return
}
2023-09-16 17:39:12 +03:00
topic , err := repo_model . DeleteTopic ( ctx , ctx . Repo . Repository . ID , topicName )
2019-09-03 18:46:24 +03:00
if err != nil {
log . Error ( "DeleteTopic failed: %v" , err )
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
if topic == nil {
ctx . NotFound ( )
2022-07-13 20:39:19 +03:00
return
2019-09-03 18:46:24 +03:00
}
ctx . Status ( http . StatusNoContent )
}
// TopicSearch search for creating topic
2020-01-24 22:00:29 +03:00
func TopicSearch ( ctx * context . APIContext ) {
2019-09-03 18:46:24 +03:00
// swagger:operation GET /topics/search repository topicSearch
// ---
// summary: search topics via keyword
// produces:
// - application/json
// parameters:
// - name: q
// in: query
// description: keywords to search
// required: true
// type: string
2020-01-24 22:00:29 +03:00
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
2020-06-09 07:57:38 +03:00
// description: page size of results
2020-01-24 22:00:29 +03:00
// type: integer
2019-09-03 18:46:24 +03:00
// responses:
// "200":
// "$ref": "#/responses/TopicListResponse"
2019-12-20 20:07:12 +03:00
// "403":
// "$ref": "#/responses/forbidden"
2023-09-13 05:37:54 +03:00
// "404":
// "$ref": "#/responses/notFound"
2019-12-20 20:07:12 +03:00
2021-12-12 18:48:20 +03:00
opts := & repo_model . FindTopicOptions {
2021-08-12 15:43:08 +03:00
Keyword : ctx . FormString ( "q" ) ,
ListOptions : utils . GetListOptions ( ctx ) ,
2019-09-03 18:46:24 +03:00
}
2023-09-16 17:39:12 +03:00
topics , total , err := repo_model . FindTopics ( ctx , opts )
2019-09-03 18:46:24 +03:00
if err != nil {
2019-12-20 20:07:12 +03:00
ctx . InternalServerError ( err )
2019-09-03 18:46:24 +03:00
return
}
topicResponses := make ( [ ] * api . TopicResponse , len ( topics ) )
for i , topic := range topics {
topicResponses [ i ] = convert . ToTopicResponse ( topic )
}
2021-08-12 15:43:08 +03:00
ctx . SetTotalCountHeader ( total )
2023-07-04 21:36:08 +03:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2019-09-03 18:46:24 +03:00
"topics" : topicResponses ,
} )
}