From a543de60dbac4eda50d7fc33e7bc7237812ce623 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:30:52 -0500 Subject: [PATCH] feat: add get by id method for apps endpoint --- apps.go | 39 +++++++++++++++--- apps_test.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 5 deletions(-) diff --git a/apps.go b/apps.go index 9887148..1752db4 100644 --- a/apps.go +++ b/apps.go @@ -2,13 +2,13 @@ package onspring import ( "context" + "fmt" "iter" "net/http" ) const ( - appsPath = "/apps" - appsBatchPath = "/apps/batch-get" + appsPath = "/apps" ) // AppsEndpoint provides access to apps in an Onspring instance. @@ -105,13 +105,14 @@ func (a *AppsEndpoint) ListAll(ctx context.Context, pagingOpts ...PagingOption) // // Parameters: // - ctx: The context for the request -// - appIds: The ids of the apps to retrieve +// - ids: The ids of the apps to retrieve // // Returns: // - AppBatch: A batch of apps // - error: An error if the request fails -func (a *AppsEndpoint) GetMany(ctx context.Context, appIds []int) (AppBatch, error) { - req, requestCreationErr := a.client.newRequest(ctx, http.MethodPost, appsBatchPath, nil, appIds) +func (a *AppsEndpoint) GetMany(ctx context.Context, ids []int) (AppBatch, error) { + path := fmt.Sprintf("%s/batch-get", appsPath) + req, requestCreationErr := a.client.newRequest(ctx, http.MethodPost, path, nil, ids) var appBatch AppBatch @@ -128,6 +129,34 @@ func (a *AppsEndpoint) GetMany(ctx context.Context, appIds []int) (AppBatch, err return appBatch, nil } +// Get retrieves an app from the Onspring API. +// +// Parameters: +// - ctx: The context for the request +// - id: The id of the app to retrieve +// +// Returns: +// - App: An app +// - error: An error if the request fails +func (a *AppsEndpoint) Get(ctx context.Context, id int) (App, error) { + path := fmt.Sprintf("%s/id/%d", appsPath, id) + req, requestCreationErr := a.client.newRequest(ctx, http.MethodGet, path, nil, nil) + + var app App + + if requestCreationErr != nil { + return app, requestCreationErr + } + + responseErr := a.client.doWithJsonResponse(req, &app) + + if responseErr != nil { + return app, responseErr + } + + return app, nil +} + func createPagingRequest(pagingOpts []PagingOption) *PagingRequest { pagingRequest := &PagingRequest{ PageNumber: 1, diff --git a/apps_test.go b/apps_test.go index 8dacb23..7edb9e2 100644 --- a/apps_test.go +++ b/apps_test.go @@ -3,6 +3,7 @@ package onspring_test import ( "context" "encoding/json" + "fmt" "net/http" "reflect" "slices" @@ -536,4 +537,112 @@ func TestApps(t *testing.T) { } }) }) + + t.Run("Get", func(t *testing.T) { + t.Run("it should return an error if context is nil", func(t *testing.T) { + _, client := setupMockServer(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + var nilContext context.Context = nil + + _, err := client.Apps.Get(nilContext, 0) + + if err == nil { + t.Errorf("Expected error for nil context, got nil") + } + }) + + t.Run("it should return an error if context is canceled", func(t *testing.T) { + _, client := setupMockServer(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + ctx, cancel := context.WithCancel(t.Context()) + + cancel() + + _, err := client.Apps.Get(ctx, 0) + + if err == nil { + t.Errorf("Expected error for canceled context, got nil") + } + }) + + t.Run("it should return an error if encounters a network error", func(t *testing.T) { + client := onspring.NewClient( + "test-api-key", + onspring.WithBaseURL("http://invalid-url"), + onspring.WithHTTPClient(&http.Client{Transport: &ErrorTransport{}}), + ) + + _, err := client.Apps.Get(t.Context(), 0) + + if err == nil { + t.Errorf("Expected network error, got nil") + } + }) + + t.Run("it should return an error if create a request fails", func(t *testing.T) { + _, client := setupMockServer(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + invalidClient := onspring.NewClient( + "test-api-key", + onspring.WithBaseURL("http://[::1]:namedport"), + onspring.WithHTTPClient(client.HTTPClient()), + ) + + _, err := invalidClient.Apps.Get(t.Context(), 0) + + if err == nil { + t.Errorf("Expected request creation error, got nil") + } + }) + + t.Run("it should return an error if the /apps/id/:id endpoint returns a non-200 status code", func(t *testing.T) { + _, client := setupMockServer(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + }) + + _, err := client.Apps.Get(t.Context(), 0) + + if err == nil { + t.Errorf("Expected error, got nil") + } + }) + + t.Run("it should return an app if the /apps/id/:id endpoint returns a 200 status code", func(t *testing.T) { + expectedApp := onspring.App{ + Href: "https://test.com", + Id: 1, + Name: "App", + } + + _, client := setupMockServer(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("Expected GET method, got %s", r.Method) + } + + expectedPath := fmt.Sprintf("/apps/id/%d", expectedApp.Id) + + if r.URL.Path != expectedPath { + t.Errorf("Expected %s endpoint, got %s", expectedPath, r.URL.Path) + } + + jsonData, _ := json.Marshal(expectedApp) + + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + w.Write(jsonData) + }) + + app, _ := client.Apps.Get(t.Context(), expectedApp.Id) + + if !reflect.DeepEqual(expectedApp, app) { + t.Errorf("Expected %v but got %v", expectedApp, app) + } + }) + }) }