// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package services import ( "context" "net/http" gitea "gitea.dev/sdk" "gitea.dev/tea/internal/transport" ) // lookup repeats a read on the instance of the failed request apiErr, with its credential, to // explain the failure. It returns the response status, or 0 when apiErr was not recorded from a // request, the read got no response, or a successful response did not decode. func lookup[T any](ctx context.Context, apiErr *transport.APIError, repository string, op repoOperation[T]) (T, int) { var zero T httpClient := apiErr.LookupClient() if httpClient == nil { return zero, 0 } owner, repo, err := splitRepository(repository) if err != nil { return zero, 0 } sdk, err := gitea.NewClient(apiErr.Instance, gitea.SetHTTPClient(httpClient), gitea.SetGiteaVersion("")) if err != nil { return zero, 0 } result, resp, err := op(ctx, sdk, owner, repo) switch { case resp == nil || resp.Response == nil: return zero, 0 case err == nil: return result, resp.StatusCode case resp.StatusCode >= 200 && resp.StatusCode < 300: return zero, 0 } return zero, resp.StatusCode } // RepositoryState holds the repository facts that explain a failed request on it. type RepositoryState struct { Empty, Issues, PullRequests, Wiki, Releases, Actions, Projects bool } // LookupRepository reads the state of repo for the failed request apiErr; see lookup for the status. func LookupRepository(ctx context.Context, apiErr *transport.APIError, repo string) (RepositoryState, int) { item, status := lookup(ctx, apiErr, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Repository, *gitea.Response, error) { return c.Repositories.GetRepo(ctx, owner, name) }) if item == nil { return RepositoryState{}, status } return RepositoryState{ Empty: item.Empty, Issues: item.HasIssues, PullRequests: item.HasPullRequests, Wiki: item.HasWiki, Releases: item.HasReleases, Actions: item.HasActions, Projects: item.HasProjects, }, status } // LookupIsIssue reports whether number names an issue rather than a pull request in repo, for the // failed request apiErr. func LookupIsIssue(ctx context.Context, apiErr *transport.APIError, repo string, number int64) bool { item, status := lookup(ctx, apiErr, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Issue, *gitea.Response, error) { return c.Issues.GetIssue(ctx, owner, name, number) }) return status == http.StatusOK && item != nil && item.PullRequest == nil } // LookupPullURL returns the web URL of the pull request from head into base in repo, for the failed // request apiErr, or "". func LookupPullURL(ctx context.Context, apiErr *transport.APIError, repo, base, head string) string { item, status := lookup(ctx, apiErr, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.PullRequest, *gitea.Response, error) { return c.PullRequests.GetPullRequestByBaseHead(ctx, owner, name, base, head) }) if status != http.StatusOK || item == nil { return "" } return item.HTMLURL }