package gitea import ( "context" "log/slog" "net/http" "slices" "strconv" "strings" "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 } // GetProject implements port.Forge. func (f *Forge) GetProject(ctx context.Context, rawProjectID string) (*model.Project, 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 &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) { 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) } 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) { projectID, err := strconv.ParseInt(rawProjectID, 10, 64) if err != nil { return "", errors.WithStack(err) } project, _, err := f.client.GetRepoByID(projectID) 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) { projectID, err := strconv.ParseInt(rawProjectID, 10, 64) if err != nil { return "", errors.WithStack(err) } project, _, err := f.client.GetRepoByID(projectID) if err != nil { return "", errors.WithStack(err) } data, res, err := f.client.GetFile(project.Owner.UserName, project.Name, project.DefaultBranch, ".gitea/issue_template.md") if err != nil { if res.StatusCode == http.StatusNotFound { return "", errors.WithStack(port.ErrFileNotFound) } 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) { 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) } 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 } // GetAllProjects implements port.Forge. func (f *Forge) GetAllProjects(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, }) } } slices.SortFunc(projects, func(p1 *model.Project, p2 *model.Project) int { return strings.Compare(p1.Name, p2.Name) }) return projects, nil } func NewForge(client *gitea.Client) *Forge { return &Forge{client: client} } var _ port.Forge = &Forge{}