309 lines
7.8 KiB
Go

package gitea
import (
"context"
"log/slog"
"net/http"
"strconv"
"code.gitea.io/sdk/gitea"
"forge.cadoles.com/wpetit/clearcase/internal/core/model"
"forge.cadoles.com/wpetit/clearcase/internal/core/port"
"github.com/pkg/errors"
)
type Forge struct {
client *gitea.Client
}
// UpdatePullRequest implements port.Forge.
func (f *Forge) UpdatePullRequest(ctx context.Context, rawProjectID string, rawPullRequestID string, title string, body string) (string, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return "", errors.WithStack(err)
}
pullRequestID, err := strconv.ParseInt(rawPullRequestID, 10, 64)
if err != nil {
return "", errors.Wrapf(err, "could not parse pull request id '%v'", rawPullRequestID)
}
pr, _, err := f.client.EditPullRequest(project.Owner.UserName, project.Name, pullRequestID, gitea.EditPullRequestOption{
Title: title,
Body: body,
})
if err != nil {
return "", errors.WithStack(err)
}
return pr.HTMLURL, nil
}
// GetPullRequestTemplate implements port.Forge.
func (f *Forge) GetPullRequestTemplate(ctx context.Context, rawProjectID string) (string, error) {
data, err := f.GetFile(ctx, rawProjectID, ".gitea/pull_request_template.md")
if err != nil {
return "", errors.WithStack(err)
}
return string(data), nil
}
// GetPullRequests implements port.Forge.
func (f *Forge) GetPullRequests(ctx context.Context, rawProjectID string, pullRequestIDs ...string) ([]*model.PullRequest, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
pullRequests := make([]*model.PullRequest, 0)
for _, rawID := range pullRequestIDs {
id, err := strconv.ParseInt(rawID, 10, 64)
if err != nil {
return nil, errors.Wrapf(err, "could not parse pull request id '%v'", rawID)
}
pr, _, err := f.client.GetPullRequest(project.Owner.UserName, project.Name, id)
if err != nil {
return nil, errors.WithStack(err)
}
pullRequests = append(pullRequests, &model.PullRequest{
ID: strconv.FormatInt(pr.ID, 10),
Title: pr.Title,
Body: pr.Body,
})
}
return pullRequests, nil
}
// GetPullRequestDiff implements port.Forge.
func (f *Forge) GetPullRequestDiff(ctx context.Context, rawProjectID string, rawPullRequestID string) (string, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return "", errors.WithStack(err)
}
pullRequestID, err := strconv.ParseInt(rawPullRequestID, 10, 64)
if err != nil {
return "", errors.Wrapf(err, "could not parse pull request id '%v'", rawPullRequestID)
}
diff, _, err := f.client.GetPullRequestDiff(project.Owner.UserName, project.Name, pullRequestID, gitea.PullRequestDiffOptions{
Binary: false,
})
if err != nil {
return "", errors.WithStack(err)
}
return string(diff), nil
}
// ListOpenedPullRequests implements port.Forge.
func (f *Forge) ListOpenedPullRequests(ctx context.Context, rawProjectID string) ([]*model.PullRequest, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
pullRequests := make([]*model.PullRequest, 0)
page := 1
for {
repoPullRequests, res, err := f.client.ListRepoPullRequests(project.Owner.UserName, project.Name, gitea.ListPullRequestsOptions{
State: gitea.StateOpen,
ListOptions: gitea.ListOptions{
Page: page,
PageSize: 100,
},
})
if err != nil {
return nil, errors.WithStack(err)
}
for _, pr := range repoPullRequests {
pullRequests = append(pullRequests, &model.PullRequest{
ID: strconv.FormatInt(pr.Index, 10),
Title: pr.Title,
Body: pr.Body,
})
}
if res.NextPage == 0 {
return pullRequests, nil
}
page = res.NextPage
}
}
// GetFile implements port.Forge.
func (f *Forge) GetFile(ctx context.Context, rawProjectID string, path string) ([]byte, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
data, res, err := f.client.GetFile(project.Owner.UserName, project.Name, project.DefaultBranch, path)
if err != nil {
if res.StatusCode == http.StatusNotFound {
return nil, errors.WithStack(port.ErrFileNotFound)
}
return nil, errors.WithStack(err)
}
return data, nil
}
// GetProject implements port.Forge.
func (f *Forge) GetProject(ctx context.Context, rawProjectID string) (*model.Project, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
return &model.Project{
ID: rawProjectID,
Name: project.FullName,
Description: project.Description,
}, nil
}
// GetProjectLanguages implements port.Forge.
func (f *Forge) GetProjectLanguages(ctx context.Context, rawProjectID string) ([]string, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
mappedLanguages, _, err := f.client.GetRepoLanguages(project.Owner.UserName, project.Name)
if err != nil {
return nil, errors.WithStack(err)
}
languages := make([]string, 0, len(mappedLanguages))
for l := range mappedLanguages {
languages = append(languages, l)
}
return languages, nil
}
// CreateIssue implements port.Forge.
func (f *Forge) CreateIssue(ctx context.Context, rawProjectID string, title string, body string) (string, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return "", errors.WithStack(err)
}
issue, _, err := f.client.CreateIssue(project.Owner.UserName, project.Name, gitea.CreateIssueOption{
Title: title,
Body: body,
})
if err != nil {
return "", errors.WithStack(err)
}
return issue.HTMLURL, nil
}
// GetIssueTemplate implements port.Forge.
func (f *Forge) GetIssueTemplate(ctx context.Context, rawProjectID string) (string, error) {
data, err := f.GetFile(ctx, rawProjectID, ".gitea/issue_template.md")
if err != nil {
return "", errors.WithStack(err)
}
return string(data), nil
}
// GetIssues implements port.Forge.
func (f *Forge) GetIssues(ctx context.Context, rawProjectID string, issueIDs ...string) ([]*model.Issue, error) {
project, err := f.getProject(rawProjectID)
if err != nil {
return nil, errors.WithStack(err)
}
issues := make([]*model.Issue, 0)
for _, rawIssueID := range issueIDs {
issueID, err := strconv.ParseInt(rawIssueID, 10, 64)
if err != nil {
slog.ErrorContext(ctx, "could not parse issue id", slog.Any("error", errors.WithStack(err)))
issues = append(issues, nil)
continue
}
issue, _, err := f.client.GetIssue(project.Owner.UserName, project.Name, issueID)
if err != nil {
slog.ErrorContext(ctx, "could not get issue", slog.String("project", project.FullName), slog.Int64("issueID", issueID), slog.Any("error", errors.WithStack(err)))
issues = append(issues, nil)
continue
}
issues = append(issues, &model.Issue{
ID: rawIssueID,
Title: issue.Title,
Body: issue.Body,
})
}
return issues, nil
}
// ListProjects implements port.Forge.
func (f *Forge) ListProjects(ctx context.Context) ([]*model.Project, error) {
projects := make([]*model.Project, 0)
page := 1
for {
repositories, res, err := f.client.ListMyRepos(gitea.ListReposOptions{
ListOptions: gitea.ListOptions{
Page: page,
PageSize: 100,
},
})
if err != nil {
return nil, errors.WithStack(err)
}
page = res.NextPage
if res.NextPage == 0 {
break
}
for _, r := range repositories {
projects = append(projects, &model.Project{
ID: strconv.FormatInt(r.ID, 10),
Name: r.FullName,
})
}
}
return projects, nil
}
func (f *Forge) getProject(rawProjectID string) (*gitea.Repository, error) {
projectID, err := strconv.ParseInt(rawProjectID, 10, 64)
if err != nil {
return nil, errors.WithStack(err)
}
project, _, err := f.client.GetRepoByID(projectID)
if err != nil {
return nil, errors.WithStack(err)
}
return project, nil
}
func NewForge(client *gitea.Client) *Forge {
return &Forge{client: client}
}
var _ port.Forge = &Forge{}