Add admin email endpoints
Some checks are pending
Test / test (push) Waiting to run
Security Scan / security (push) Successful in 12s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 00:47:58 +00:00
parent 1f6cfbfd8b
commit 572a1f1ddc
3 changed files with 84 additions and 0 deletions

View file

@ -70,6 +70,26 @@ func (s *AdminService) IterOrgs(ctx context.Context) iter.Seq2[types.Organizatio
return ListIter[types.Organization](ctx, s.client, "/api/v1/admin/orgs", nil)
}
// ListEmails returns all email addresses (admin only).
func (s *AdminService) ListEmails(ctx context.Context) ([]types.Email, error) {
return ListAll[types.Email](ctx, s.client, "/api/v1/admin/emails", nil)
}
// IterEmails returns an iterator over all email addresses (admin only).
func (s *AdminService) IterEmails(ctx context.Context) iter.Seq2[types.Email, error] {
return ListIter[types.Email](ctx, s.client, "/api/v1/admin/emails", nil)
}
// SearchEmails searches all email addresses by keyword (admin only).
func (s *AdminService) SearchEmails(ctx context.Context, q string) ([]types.Email, error) {
return ListAll[types.Email](ctx, s.client, "/api/v1/admin/emails/search", map[string]string{"q": q})
}
// IterSearchEmails returns an iterator over all email addresses matching a keyword (admin only).
func (s *AdminService) IterSearchEmails(ctx context.Context, q string) iter.Seq2[types.Email, error] {
return ListIter[types.Email](ctx, s.client, "/api/v1/admin/emails/search", map[string]string{"q": q})
}
// RunCron runs a cron task by name (admin only).
func (s *AdminService) RunCron(ctx context.Context, task string) error {
path := ResolvePath("/api/v1/admin/cron/{task}", Params{"task": task})

View file

@ -195,6 +195,66 @@ func TestAdminService_ListOrgs_Good(t *testing.T) {
}
}
func TestAdminService_ListEmails_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Errorf("expected GET, got %s", r.Method)
}
if r.URL.Path != "/api/v1/admin/emails" {
t.Errorf("wrong path: %s", r.URL.Path)
}
w.Header().Set("X-Total-Count", "2")
json.NewEncoder(w).Encode([]types.Email{
{Email: "alice@example.com", Primary: true},
{Email: "bob@example.com", Verified: true},
})
}))
defer srv.Close()
f := NewForge(srv.URL, "tok")
emails, err := f.Admin.ListEmails(context.Background())
if err != nil {
t.Fatal(err)
}
if len(emails) != 2 {
t.Errorf("got %d emails, want 2", len(emails))
}
if emails[0].Email != "alice@example.com" || !emails[0].Primary {
t.Errorf("got first email=%+v, want primary alice@example.com", emails[0])
}
}
func TestAdminService_SearchEmails_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Errorf("expected GET, got %s", r.Method)
}
if r.URL.Path != "/api/v1/admin/emails/search" {
t.Errorf("wrong path: %s", r.URL.Path)
}
if got := r.URL.Query().Get("q"); got != "alice" {
t.Errorf("got q=%q, want %q", got, "alice")
}
w.Header().Set("X-Total-Count", "1")
json.NewEncoder(w).Encode([]types.Email{
{Email: "alice@example.com", Primary: true},
})
}))
defer srv.Close()
f := NewForge(srv.URL, "tok")
emails, err := f.Admin.SearchEmails(context.Background(), "alice")
if err != nil {
t.Fatal(err)
}
if len(emails) != 1 {
t.Errorf("got %d emails, want 1", len(emails))
}
if emails[0].Email != "alice@example.com" {
t.Errorf("got email=%q, want %q", emails[0].Email, "alice@example.com")
}
}
func TestAdminService_ListCron_Good(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {

View file

@ -72,11 +72,15 @@ Coverage notes: rows list direct tests when a symbol is named in test names or r
| method | AdminService.EditUser | `func (s *AdminService) EditUser(ctx context.Context, username string, opts map[string]any) error` | EditUser edits an existing user (admin only). | `TestAdminService_Good_EditUser` |
| method | AdminService.GenerateRunnerToken | `func (s *AdminService) GenerateRunnerToken(ctx context.Context) (string, error)` | GenerateRunnerToken generates an actions runner registration token. | `TestAdminService_Good_GenerateRunnerToken` |
| method | AdminService.IterCron | `func (s *AdminService) IterCron(ctx context.Context) iter.Seq2[types.Cron, error]` | IterCron returns an iterator over all cron tasks (admin only). | No direct tests. |
| method | AdminService.IterEmails | `func (s *AdminService) IterEmails(ctx context.Context) iter.Seq2[types.Email, error]` | IterEmails returns an iterator over all email addresses (admin only). | No direct tests. |
| method | AdminService.IterOrgs | `func (s *AdminService) IterOrgs(ctx context.Context) iter.Seq2[types.Organization, error]` | IterOrgs returns an iterator over all organisations (admin only). | No direct tests. |
| method | AdminService.IterSearchEmails | `func (s *AdminService) IterSearchEmails(ctx context.Context, q string) iter.Seq2[types.Email, error]` | IterSearchEmails returns an iterator over all email addresses matching a keyword (admin only). | No direct tests. |
| method | AdminService.IterUsers | `func (s *AdminService) IterUsers(ctx context.Context) iter.Seq2[types.User, error]` | IterUsers returns an iterator over all users (admin only). | No direct tests. |
| method | AdminService.ListCron | `func (s *AdminService) ListCron(ctx context.Context) ([]types.Cron, error)` | ListCron returns all cron tasks (admin only). | `TestAdminService_Good_ListCron` |
| method | AdminService.ListEmails | `func (s *AdminService) ListEmails(ctx context.Context) ([]types.Email, error)` | ListEmails returns all email addresses (admin only). | `TestAdminService_ListEmails_Good` |
| method | AdminService.ListOrgs | `func (s *AdminService) ListOrgs(ctx context.Context) ([]types.Organization, error)` | ListOrgs returns all organisations (admin only). | `TestAdminService_Good_ListOrgs` |
| method | AdminService.ListUsers | `func (s *AdminService) ListUsers(ctx context.Context) ([]types.User, error)` | ListUsers returns all users (admin only). | `TestAdminService_Good_ListUsers` |
| method | AdminService.SearchEmails | `func (s *AdminService) SearchEmails(ctx context.Context, q string) ([]types.Email, error)` | SearchEmails searches all email addresses by keyword (admin only). | `TestAdminService_SearchEmails_Good` |
| method | AdminService.RenameUser | `func (s *AdminService) RenameUser(ctx context.Context, username, newName string) error` | RenameUser renames a user (admin only). | `TestAdminService_Good_RenameUser` |
| method | AdminService.RunCron | `func (s *AdminService) RunCron(ctx context.Context, task string) error` | RunCron runs a cron task by name (admin only). | `TestAdminService_Good_RunCron` |
| method | BranchService.CreateBranchProtection | `func (s *BranchService) CreateBranchProtection(ctx context.Context, owner, repo string, opts *types.CreateBranchProtectionOption) (*types.BranchProtection, error)` | CreateBranchProtection creates a new branch protection rule. | `TestBranchService_Good_CreateProtection` |