diff --git a/oxide/lib.go b/oxide/lib.go index c3fa175..9607bbd 100644 --- a/oxide/lib.go +++ b/oxide/lib.go @@ -301,3 +301,22 @@ func (c *Client) buildRequest(ctx context.Context, body io.Reader, method, uri s return req, nil } + +type Request struct { + Method string + Path string + Body io.Reader + Params map[string]string + Query map[string]string +} + +// MakeRequest takes a `Request` that defines the desired API request, builds +// the URI using the configured API host, and sends the request to the API. +func (c *Client) MakeRequest(ctx context.Context, req Request) (*http.Response, error) { + uri := resolveRelative(c.host, req.Path) + httpReq, err := c.buildRequest(ctx, req.Body, req.Method, uri, req.Params, req.Query) + if err != nil { + return nil, fmt.Errorf("building request failed: %v", err) + } + return c.client.Do(httpReq) +} diff --git a/oxide/lib_test.go b/oxide/lib_test.go index 096d13f..971872a 100644 --- a/oxide/lib_test.go +++ b/oxide/lib_test.go @@ -10,6 +10,7 @@ import ( "encoding/json" "io" "net/http" + "net/http/httptest" "net/url" "os" "path/filepath" @@ -470,3 +471,66 @@ user = "other-user" return tmpDir } + +func Test_MakeRequest(t *testing.T) { + tests := []struct { + name string + request Request + expectedQuery string + }{ + { + name: "request without optional fields", + request: Request{ + Method: http.MethodGet, + Path: "/v1/projects", + }, + expectedQuery: "", + }, + { + name: "request with all fields", + request: Request{ + Method: http.MethodPost, + Path: "/v1/projects", + Body: strings.NewReader(`{"name":"my-project"}`), + Params: map[string]string{ + "project": "my-project", + }, + Query: map[string]string{ + "project": "my-project", + }, + }, + expectedQuery: "project=my-project", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var capturedRequest *http.Request + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + capturedRequest = r + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"status":"ok"}`)) + require.NoError(t, err) + })) + defer server.Close() + + client, err := NewClient(&Config{ + Host: server.URL, + Token: "test-token", + }) + require.NoError(t, err) + + resp, err := client.MakeRequest(context.Background(), tc.request) + require.NoError(t, err) + require.NotNil(t, resp) + defer resp.Body.Close() + + require.NotNil(t, capturedRequest) + assert.Equal(t, tc.request.Method, capturedRequest.Method) + assert.Equal(t, tc.request.Path, capturedRequest.URL.Path) + assert.Equal(t, tc.expectedQuery, capturedRequest.URL.RawQuery) + + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + } +}