feat(forge): add org iterator
Some checks failed
Security Scan / security (push) Failing after 11s
Test / test (push) Successful in 2m16s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 08:47:55 +00:00
parent b94caf0a9d
commit d852087c45
3 changed files with 80 additions and 1 deletions

View file

@ -3,6 +3,8 @@
package forge
import (
"iter"
forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"dappco.re/go/core/log"
@ -33,6 +35,35 @@ func (c *Client) ListMyOrgs() ([]*forgejo.Organization, error) {
return all, nil
}
// ListMyOrgsIter returns an iterator over organisations for the authenticated user.
// Usage: ListMyOrgsIter(...)
func (c *Client) ListMyOrgsIter() iter.Seq2[*forgejo.Organization, error] {
return func(yield func(*forgejo.Organization, error) bool) {
page := 1
for {
orgs, resp, err := c.api.ListMyOrgs(forgejo.ListOrgsOptions{
ListOptions: forgejo.ListOptions{Page: page, PageSize: 50},
})
if err != nil {
yield(nil, log.E("forge.ListMyOrgs", "failed to list orgs", err))
return
}
for _, org := range orgs {
if !yield(org, nil) {
return
}
}
if resp == nil || page >= resp.LastPage {
break
}
page++
}
}
}
// GetOrg returns a single organisation by name.
// Usage: GetOrg(...)
func (c *Client) GetOrg(name string) (*forgejo.Organization, error) {

View file

@ -3,6 +3,8 @@
package forge
import (
"net/http"
"net/http/httptest"
"testing"
forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
@ -21,6 +23,41 @@ func TestClient_ListMyOrgs_Good(t *testing.T) {
assert.Equal(t, "test-org", orgs[0].UserName)
}
func TestClient_ListMyOrgsIter_Good_Paginates_Good(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) {
jsonResponse(w, map[string]string{"version": "1.21.0"})
})
mux.HandleFunc("/api/v1/user/orgs", func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Query().Get("page") {
case "2":
jsonResponse(w, []map[string]any{
{"id": 101, "login": "second-org", "username": "second-org", "full_name": "Second Organisation"},
})
default:
w.Header().Set("Link", "<http://"+r.Host+"/api/v1/user/orgs?page=2>; rel=\"next\", <http://"+r.Host+"/api/v1/user/orgs?page=2>; rel=\"last\"")
jsonResponse(w, []map[string]any{
{"id": 100, "login": "test-org", "username": "test-org", "full_name": "Test Organisation"},
})
}
})
srv := httptest.NewServer(mux)
defer srv.Close()
client, err := New(srv.URL, "test-token")
require.NoError(t, err)
var names []string
for org, err := range client.ListMyOrgsIter() {
require.NoError(t, err)
names = append(names, org.UserName)
}
require.Len(t, names, 2)
assert.Equal(t, []string{"test-org", "second-org"}, names)
}
func TestClient_ListMyOrgs_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
@ -30,6 +67,17 @@ func TestClient_ListMyOrgs_Bad_ServerError_Good(t *testing.T) {
assert.Contains(t, err.Error(), "failed to list orgs")
}
func TestClient_ListMyOrgsIter_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
for _, err := range client.ListMyOrgsIter() {
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to list orgs")
break
}
}
func TestClient_GetOrg_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()

View file

@ -90,7 +90,7 @@ func TestClient_ListPRReviewsIter_Good_Paginates_Good(t *testing.T) {
var states []string
for review, err := range client.ListPRReviewsIter("test-org", "org-repo", 1) {
require.NoError(t, err)
states = append(states, review.State)
states = append(states, string(review.State))
}
require.Equal(t, []string{"APPROVED", "REQUEST_CHANGES"}, states)