diff --git a/api/routes.go b/api/routes.go index cc23744..67d78d9 100644 --- a/api/routes.go +++ b/api/routes.go @@ -29,6 +29,7 @@ func SetupRoutes(params RouteParams) { setupManifestsRoutes(params) setupAppRoutes(params) setupWebhookRoutes(params) + setupRestRoutes(params) } type ClientParams struct { diff --git a/api/routes_rest_api.go b/api/routes_rest_api.go new file mode 100644 index 0000000..92f73a5 --- /dev/null +++ b/api/routes_rest_api.go @@ -0,0 +1,148 @@ +package api + +import ( + structs "code.gitea.io/gitea/modules/structs" + gitea "code.gitea.io/sdk/gitea" + "encoding/json" + "fmt" + "git.lumeweb.com/LumeWeb/gitea-github-proxy/config" + "github.com/google/go-github/v59/github" + "github.com/gorilla/mux" + "go.uber.org/zap" + "net/http" + "strconv" +) + +type restApi struct { + config *config.Config + logger *zap.Logger +} + +func newRestApi(cfg *config.Config, logger *zap.Logger) *restApi { + return &restApi{config: cfg, logger: logger} +} + +func (r restApi) handlerGetPullRequestFiles(w http.ResponseWriter, request *http.Request) { + vars := mux.Vars(request) + + owner := vars["owner"] + repo := vars["repo"] + pullNumber := vars["pull_number"] + + client := r.getClientOrError(w) + + if client == nil { + return + } + parsedPullNumber, err := strconv.ParseInt(pullNumber, 10, 64) + if err != nil { + http.Error(w, "Failed to parse pull number", http.StatusBadRequest) + r.logger.Error("Failed to parse pull number", zap.Error(err)) + return + } + + files, r2, err := client.ListPullRequestFiles(owner, repo, parsedPullNumber, gitea.ListPullRequestFilesOptions{ + ListOptions: r.getPagingOptions(request), + }) + if err != nil { + http.Error(w, "Failed to get pull request files", http.StatusInternalServerError) + r.logger.Error("Failed to get pull request files", zap.Error(err)) + return + } + + githubFiles := make([]*github.CommitFile, len(files)) + + for i, file := range files { + f := structs.ChangedFile(*file) + githubFiles[i] = convertCommitFile(&f) + } + + r.sendPagingHeaders(w, r2) + r.respond(w, http.StatusOK, githubFiles) +} + +func (r restApi) getPagingOptions(request *http.Request) gitea.ListOptions { + page, _ := strconv.Atoi(request.URL.Query().Get("page")) + perPage, _ := strconv.Atoi(request.URL.Query().Get("per_page")) + + return gitea.ListOptions{ + Page: page, + PageSize: perPage, + } +} + +func (r restApi) sendPagingHeaders(w http.ResponseWriter, apiResponse *gitea.Response) { + links := []string{} + + baseURL := fmt.Sprintf("https://%s", r.config.Domain) + + joinStrings := func(elements []string, separator string) string { + if len(elements) == 0 { + return "" + } + result := elements[0] + for i := 1; i < len(elements); i++ { + result += separator + elements[i] + } + return result + } + joinLinks := func(links []string) string { + return fmt.Sprintf("%s", joinStrings(links, ", ")) + } + + if apiResponse.FirstPage > 0 { + links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="first"`, baseURL, apiResponse.FirstPage)) + } + if apiResponse.PrevPage > 0 { + links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="prev"`, baseURL, apiResponse.PrevPage)) + } + if apiResponse.NextPage > 0 { + links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="next"`, baseURL, apiResponse.NextPage)) + } + if apiResponse.LastPage > 0 { + links = append(links, fmt.Sprintf(`<%s?page=%d>; rel="last"`, baseURL, apiResponse.LastPage)) + } + + if len(links) > 0 { + w.Header().Add("Link", fmt.Sprintf("%s", joinLinks(links))) + } +} + +func (r restApi) respond(w http.ResponseWriter, status int, data interface{}) { + jsonData, err := json.Marshal(data) + if err != nil { + http.Error(w, "Failed to marshal response", http.StatusInternalServerError) + r.logger.Error("Failed to marshal response", zap.Error(err)) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + if data != nil { + _, _ = w.Write(jsonData) + } +} + +func (r restApi) getClientOrError(w http.ResponseWriter) *gitea.Client { + client, err := getClient(ClientParams{ + Config: r.config, + }) + if err != nil { + http.Error(w, "Failed to get Gitea client", http.StatusInternalServerError) + r.logger.Error("Failed to get Gitea client", zap.Error(err)) + return nil + } + + return client +} + +func setupRestRoutes(params RouteParams) { + logger := params.Logger + cfg := params.Config + r := params.R + + restApi := newRestApi(cfg, logger) + restRouter := r.PathPrefix("/api").Subrouter() + + restRouter.HandleFunc("/repos/{owner}/{repo}/pulls/{pull_number}/files", restApi.handlerGetPullRequestFiles).Methods("GET") +}