Repository: go-gitea/gitea
File: routers/api/v1/repo/issue.go (Go)

Code before:
	"code.gitea.io/gitea/routers/common"
	"code.gitea.io/gitea/services/context"
	"code.gitea.io/gitea/services/convert"
	issue_service "code.gitea.io/gitea/services/issue"
)

// ErrOwnerOrgRequired represents an error when owner organisation is required for filtering on team
type ErrOwnerOrgRequired struct{}

// IsErrOwnerOrgRequired checks if an error is an ErrOwnerOrgRequired
func IsErrOwnerOrgRequired(err error) bool {
	_, ok := err.(ErrOwnerOrgRequired)
	return ok
}

func (err ErrOwnerOrgRequired) Error() string {
	return "owner organisation is required for filtering on team"
}

// parseIssueIsClosed parses the "state" query parameter and returns the corresponding isClosed option
func parseIssueIsClosed(ctx *context.APIContext) optional.Option[bool] {
	switch ctx.FormString("state") {
	case "closed":
		return optional.Some(true)
	case "all":
		return optional.None[bool]()
	default:
		return optional.Some(false)
	}
}

// parseIssueIsPull parses the "type" query parameter and returns the corresponding isPull option
func parseIssueIsPull(ctx *context.APIContext) optional.Option[bool] {
	switch ctx.FormString("type") {
	case "pulls":
		return optional.Some(true)
	case "issues":
		return optional.Some(false)
	default:
		return optional.None[bool]()
	}
}

// buildSearchIssuesRepoIDs builds the list of repository IDs for issue search based on query parameters.
// It returns repoIDs, allPublic flag, and any error that occurred.
func buildSearchIssuesRepoIDs(ctx *context.APIContext) ([]int64, bool, error) {
	var repoIDs []int64
	var allPublic bool

	opts := repo_model.SearchRepoOptions{
		Private:     false,

Review comment (bug):
This seems not right, the "owner" can only be "UserTypeIndividual" now

Related to Add new user types `reserved`, `bot`, and `remote` (#24026), that PR seems wrong.

Code after:
	"code.gitea.io/gitea/routers/api/v1/utils"
	"code.gitea.io/gitea/routers/common"
	"code.gitea.io/gitea/services/context"
	"code.gitea.io/gitea/services/convert"
	issue_service "code.gitea.io/gitea/services/issue"
)

// buildSearchIssuesRepoIDs builds the list of repository IDs for issue search based on query parameters.
// It returns repoIDs, allPublic flag, and any error that occurred.
func buildSearchIssuesRepoIDs(ctx *context.APIContext) (repoIDs []int64, allPublic bool, err error) {
	opts := repo_model.SearchRepoOptions{
		Private:     false,
		AllPublic:   true,
		TopicOnly:   false,
		Collaborate: optional.None[bool](),
		// This needs to be a column that is not nil in fixtures or
		// MySQL will return different results when sorting by null in some cases
		OrderBy: db.SearchOrderByAlphabetically,
		Actor:   ctx.Doer,
	}
	if ctx.IsSigned {
		opts.Private = !ctx.PublicOnly
		opts.AllLimited = true
	}
	if ctx.FormString("owner") != "" {
		owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
		if err != nil {
			return nil, false, err
		}
		opts.OwnerID = owner.ID
		opts.AllLimited = false
		opts.AllPublic = false
		opts.Collaborate = optional.Some(false)
	}
	if ctx.FormString("team") != "" {
		if ctx.FormString("owner") == "" {
			return nil, false, util.NewInvalidArgumentErrorf("owner organisation is required for filtering on team")
		}
		team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
		if err != nil {
			return nil, false, err
		}
		opts.TeamID = team.ID
	}

	if opts.AllPublic {
		allPublic = true
		opts.AllPublic = false // set it false to avoid returning too many repos, we could filter by indexer
	}
	repoIDs, _, err = repo_model.SearchRepositoryIDs(ctx, opts)
	if err != nil {