From 30ad010863936f9b3c1ddcdc402ea8793bf3a79c Mon Sep 17 00:00:00 2001 From: BoxBoxJason Date: Tue, 2 Jun 2026 02:03:20 +0200 Subject: [PATCH] docs: add package documentation and runnable examples Signed-off-by: BoxBoxJason --- sonar/doc.go | 53 +++++++++++++++++++++++ sonar/example_test.go | 97 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 sonar/doc.go create mode 100644 sonar/example_test.go diff --git a/sonar/doc.go b/sonar/doc.go new file mode 100644 index 0000000..3d775a2 --- /dev/null +++ b/sonar/doc.go @@ -0,0 +1,53 @@ +// Package sonar is a Go client for the SonarQube web API. +// +// The root type is Client. Create one with NewClient and reach each API area +// through a typed service field, for example client.Projects, client.Issues or +// client.Qualitygates. V2 API services are grouped under client.V2. +// +// # Authentication +// +// SonarQube supports token authentication (recommended) and basic auth. Both +// can be supplied at construction via ClientCreateOptions or with the WithToken +// and WithBasicAuth options: +// +// client, err := sonar.NewClient(nil, +// sonar.WithBaseURL("https://sonarqube.example.com/api/"), +// sonar.WithToken("my-token"), +// ) +// +// In CI/CD environments the SONAR_URL, SONAR_TOKEN, SONAR_USERNAME, and +// SONAR_PASSWORD environment variables can be used instead: +// +// client, err := sonar.NewClientFromEnv() +// +// # Requests and responses +// +// Every service method takes a context.Context and, where applicable, a typed +// *Options struct whose fields are validated before the request +// is sent. Methods return the decoded response, the raw *http.Response, and an +// error. +// +// # V1 vs V2 +// +// V1 endpoints encode parameters as URL query values (url:"" struct tags). V2 +// endpoints use JSON tags, support a request body, and live under the V2 field; +// the "v2/" path prefix is added automatically. +// +// # Errors +// +// API errors are returned as *ResponseError. Use the sentinel helpers +// (IsNotFound, IsUnauthorized, IsForbidden, IsConflict, IsRateLimited, +// IsServerError) to branch on the HTTP status without unwrapping by hand. +// +// # Pagination +// +// Paginated V1 endpoints expose both a single-page method (Search) and a +// convenience method that fetches every page (SearchAll / ListAll). +// +// # Resilience +// +// Retries with exponential backoff and jitter are opt-in via WithRetry. The +// transport can be customized with WithTransportConfig, and arbitrary +// http.RoundTripper middleware (logging, tracing, metrics) can be attached with +// WithMiddleware. +package sonar diff --git a/sonar/example_test.go b/sonar/example_test.go new file mode 100644 index 0000000..c8b3ac9 --- /dev/null +++ b/sonar/example_test.go @@ -0,0 +1,97 @@ +package sonar_test + +import ( + "context" + "fmt" + "time" + + "github.com/boxboxjason/sonarqube-client-go/sonar" +) + +// Create a client with token authentication and search for projects. +func Example() { + client, err := sonar.NewClient(nil, + sonar.WithBaseURL("https://sonarqube.example.com/api/"), + sonar.WithToken("my-token"), + ) + if err != nil { + panic(err) + } + + result, _, err := client.Projects.Search(context.Background(), &sonar.ProjectsSearchOptions{ + Query: "my-service", + }) + if err != nil { + panic(err) + } + + for _, project := range result.Components { + fmt.Println(project.Key) + } +} + +// Fetch every page of a paginated endpoint with the SearchAll helper. +func ExampleProjectsService_SearchAll() { + client, err := sonar.NewClient(nil, + sonar.WithBaseURL("https://sonarqube.example.com/api/"), + sonar.WithToken("my-token"), + ) + if err != nil { + panic(err) + } + + //nolint:exhaustruct // only the filter we care about is set + projects, _, err := client.Projects.SearchAll(context.Background(), &sonar.ProjectsSearchOptions{}) + if err != nil { + panic(err) + } + + fmt.Printf("found %d projects\n", len(projects)) +} + +// Enable retries with exponential backoff for transient server errors. +func ExampleWithRetry() { + client, err := sonar.NewClient(nil, + sonar.WithBaseURL("https://sonarqube.example.com/api/"), + sonar.WithToken("my-token"), + sonar.WithRetry(sonar.RetryOptions{ + MaxAttempts: 3, + InitialDelay: 100 * time.Millisecond, + MaxDelay: 2 * time.Second, + RetryableStatusCodes: []int{502, 503, 504}, + }), + ) + if err != nil { + panic(err) + } + + _ = client + // Output: +} + +// Branch on the HTTP status of an API error using the sentinel helpers. +func ExampleIsNotFound() { + client, err := sonar.NewClient(nil, + sonar.WithBaseURL("https://sonarqube.example.com/api/"), + sonar.WithToken("my-token"), + ) + if err != nil { + panic(err) + } + + _, err = client.Projects.Delete(context.Background(), &sonar.ProjectsDeleteOptions{Project: "missing-project"}) + if sonar.IsNotFound(err) { + fmt.Println("project not found") + } +} + +// Configure a client from SONAR_URL and SONAR_TOKEN environment variables. +func ExampleNewClientFromEnv() { + client, err := sonar.NewClientFromEnv() + if err != nil { + panic(err) + } + + _ = client + // Output: +}