Skip to content

SDK Examples

Common usage patterns for the go-aha SDK.

Setup

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/grokify/go-aha/v3/oag7/aha"
    "github.com/grokify/go-aha/v3/oag7/client"
)

func getClient() *aha.APIClient {
    cfg, err := client.NewConfiguration(
        os.Getenv("AHA_DOMAIN"),
        os.Getenv("AHA_API_KEY"),
    )
    if err != nil {
        panic(err)
    }
    return aha.NewAPIClient(cfg)
}

Listing Ideas

Basic List

func listIdeas() {
    client := getClient()
    ctx := context.Background()

    ideas, _, err := client.IdeasAPI.ListIdeas(ctx).
        PerPage(20).
        Execute()
    if err != nil {
        panic(err)
    }

    for _, idea := range ideas.Ideas {
        fmt.Printf("%s: %s (%d votes)\n",
            idea.ReferenceNum,
            idea.Name,
            idea.GetVotes())
    }
}

Paginate All Ideas

func getAllIdeas() []aha.Idea {
    client := getClient()
    ctx := context.Background()

    var allIdeas []aha.Idea
    page := int32(1)

    for {
        ideas, _, err := client.IdeasAPI.ListIdeas(ctx).
            Page(page).
            PerPage(100).
            Execute()
        if err != nil {
            panic(err)
        }

        allIdeas = append(allIdeas, ideas.Ideas...)

        if ideas.Pagination == nil ||
           int64(page) >= ideas.Pagination.GetTotalPages() {
            break
        }
        page++
    }

    return allIdeas
}

Search and Filter

func searchIdeas(query, status string) {
    client := getClient()
    ctx := context.Background()

    req := client.IdeasAPI.ListIdeas(ctx)

    if query != "" {
        req = req.Q(query)
    }
    if status != "" {
        req = req.WorkflowStatus(status)
    }

    ideas, _, err := req.PerPage(50).Execute()
    if err != nil {
        panic(err)
    }

    fmt.Printf("Found %d ideas\n", len(ideas.Ideas))
}

Working with Features

Get Feature with Release

func getFeatureDetails(refNum string) {
    client := getClient()
    ctx := context.Background()

    resp, _, err := client.FeaturesAPI.GetFeature(ctx, refNum).Execute()
    if err != nil {
        panic(err)
    }

    f := resp.Feature
    fmt.Printf("Feature: %s\n", f.Name)

    if f.WorkflowStatus != nil {
        fmt.Printf("Status: %s\n", f.WorkflowStatus.GetName())
    }

    if f.Release != nil {
        fmt.Printf("Release: %s (due %s)\n",
            f.Release.GetName(),
            f.Release.GetReleaseDate())
    }
}

Generating Reports

Full Report to Excel

import "github.com/grokify/go-aha/v3/oag7/ideas"

func exportToExcel() {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{
        FetchAll: true,
        PerPage:  100,
    }

    reportSet, err := ideas.GetIdeaFeatureReports(ctx, client, req)
    if err != nil {
        panic(err)
    }

    err = reportSet.Table().WriteXLSX("ideas_report.xlsx", "Ideas")
    if err != nil {
        panic(err)
    }

    fmt.Printf("Exported %d ideas to ideas_report.xlsx\n", reportSet.Len())
}

Filtered Report

func promotedIdeasReport() {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{
        FetchAll:       true,
        WorkflowStatus: "Planned",
    }

    reportSet, err := ideas.GetIdeaFeatureReports(ctx, client, req)
    if err != nil {
        panic(err)
    }

    // Filter to only promoted ideas with releases
    promoted := reportSet.
        FilterByHasFeature(true).
        FilterByHasRelease(true)

    // Sort by votes
    promoted.SortByVotes()

    // Export
    promoted.Table().WriteXLSX("promoted_ideas.xlsx", "Promoted")
}

Markdown Report

func markdownReport() {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{
        FetchAll: true,
    }

    reportSet, err := ideas.GetIdeaFeatureReports(ctx, client, req)
    if err != nil {
        panic(err)
    }

    // Generate markdown with links
    table := reportSet.TableWithLinks(
        "https://portal.aha.io",
        "https://company.aha.io",
    )

    md := table.Markdown("\n", true)
    fmt.Println(md)
}

Analysis Examples

Count by Status

func countByStatus() {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{FetchAll: true}
    reportSet, _ := ideas.GetIdeaFeatureReports(ctx, client, req)

    counts := make(map[string]int)
    for _, r := range reportSet.Reports {
        counts[r.IdeaStatus]++
    }

    for status, count := range counts {
        fmt.Printf("%s: %d\n", status, count)
    }
}

Top Voted Ideas

func topVotedIdeas(n int) {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{FetchAll: true}
    reportSet, _ := ideas.GetIdeaFeatureReports(ctx, client, req)

    reportSet.SortByVotes()

    fmt.Printf("Top %d ideas by votes:\n", n)
    for i, r := range reportSet.Reports {
        if i >= n {
            break
        }
        fmt.Printf("%d. %s (%d votes) - %s\n",
            i+1, r.IdeaRefNum, r.IdeaVotes, r.IdeaName)
    }
}

Ideas Without Features

func backlogIdeas() {
    client := getClient()
    ctx := context.Background()

    req := ideas.ListIdeasRequest{FetchAll: true}
    reportSet, _ := ideas.GetIdeaFeatureReports(ctx, client, req)

    backlog := reportSet.FilterByHasFeature(false)
    backlog.SortByVotes()

    fmt.Printf("Backlog: %d ideas not yet promoted\n", backlog.Len())

    // Top 10 backlog items
    for i, r := range backlog.Reports {
        if i >= 10 {
            break
        }
        fmt.Printf("- %s: %s (%d votes)\n",
            r.IdeaRefNum, r.IdeaName, r.IdeaVotes)
    }
}

Error Handling

func robustAPICall() {
    client := getClient()
    ctx := context.Background()

    ideas, resp, err := client.IdeasAPI.ListIdeas(ctx).Execute()

    // Handle errors
    if err != nil {
        if apiErr, ok := err.(*aha.GenericOpenAPIError); ok {
            fmt.Printf("API Error Body: %s\n", apiErr.Body())
        }
        panic(err)
    }

    // Check HTTP status
    switch resp.StatusCode {
    case 401:
        panic("Unauthorized: check API key")
    case 403:
        panic("Forbidden: insufficient permissions")
    case 404:
        panic("Not found")
    case 429:
        panic("Rate limited: slow down requests")
    }

    if resp.StatusCode >= 400 {
        panic(fmt.Sprintf("HTTP error: %s", resp.Status))
    }

    // Process ideas...
    _ = ideas
}