Files
gitea-tea/internal/services/lookups.go
T
bircni 5fd4be9803 refactor(errors): explain failed requests through the SDK
The hints for a missing repository, a pull request number that names an
issue and an existing pull request read the API with raw GETs from the app
layer. Read them through SDK lookups in services, which reuse the failed
request's instance and credential, and let transport only hand out that
client.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-12 09:57:12 +02:00

82 lines
3.1 KiB
Go

// 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
}