diff --git a/internal/requests/requests.go b/internal/requests/requests.go index 999b26a82..70cb9d407 100644 --- a/internal/requests/requests.go +++ b/internal/requests/requests.go @@ -216,24 +216,22 @@ func (c *Client) Do(p *RequestParams) (*HTTPResponse, error) { c.Logger.Info("############### THIS IS A DRY-RUN ###############") c.Logger.Info("the request would have been sent to: %s", req.URL) - // Check the content type to determine what to log - contentType := req.Header.Get("Content-Type") - if strings.Contains(contentType, "multipart/form-data") { - // Log only the JSON fields for multipart/form-data - c.Logger.Info("this is the payload data that would be sent in a real run:") - for key, value := range jsonFields { - c.Logger.Info("Field: %s, Value: %+v", key, string(value.([]byte))) - } - } else if req.Body != nil { - // For non-multipart requests, log the full JSON body - reqBody, err := io.ReadAll(req.Body) - if err != nil { - return nil, fmt.Errorf("failed to read request body to %s : %v", req.URL, err) - } - c.Logger.Info("this is the payload that would be sent in a real run: \n %+v", string(reqBody)) + // log the payload + err := c.PayloadOutput(req, jsonFields, "this is the payload that would be sent in a real run:") + if err != nil { + return nil, err } return nil, nil } else { + if c.Debug && req.Body != nil { + // log the payload + c.Logger.Info("############### PAYLOAD ###############") + c.Logger.Info("payload sent to: %s", req.URL) + err := c.PayloadOutput(req, jsonFields, "this is the payload being sent:") + if err != nil { + c.Logger.Error("failed to log payload: %v \nContinuing with the request...", err) + } + } resp, err := c.HttpClient.Do(req) if err != nil { // err from retryable client is detailed enough @@ -279,3 +277,26 @@ func (c *Client) Do(p *RequestParams) (*HTTPResponse, error) { return &HTTPResponse{string(body), resp}, nil } } + +func (c *Client) PayloadOutput(req *http.Request, jsonFields map[string]interface{}, message string) error { + // Check the content type to determine what to log + contentType := req.Header.Get("Content-Type") + if strings.Contains(contentType, "multipart/form-data") { + // Log only the JSON fields for multipart/form-data + c.Logger.Info(message) + for key, value := range jsonFields { + c.Logger.Info("Field: %s, Value: %+v", key, string(value.([]byte))) + } + } else if req.Body != nil { + // For non-multipart requests, log the full JSON body + // Create a copy of the body to avoid consuming the original stream + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + return fmt.Errorf("failed to read request body to %s : %v", req.URL, err) + } + // Restore the body for the actual request + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + c.Logger.Info("%s \n %+v", message, string(bodyBytes)) + } + return nil +} diff --git a/internal/requests/requests_test.go b/internal/requests/requests_test.go index e348b734a..c71777012 100644 --- a/internal/requests/requests_test.go +++ b/internal/requests/requests_test.go @@ -386,6 +386,63 @@ func (suite *RequestsTestSuite) TestDo() { } } +func (suite *RequestsTestSuite) TestDebugPayloadOutput() { + for _, t := range []struct { + name string + params *RequestParams + wantError bool + expectedLog string + expectedErrorMsg string + expectedBody string + }{ + { + name: "PUT request with debug logs payload", + params: &RequestParams{ + Method: http.MethodPut, + URL: "http://localhost:8001/api/v2/environments/docs-cmd-test-user-shared", + DryRun: false, + Token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY", + Payload: struct { + Name string `json:"name"` + Type string `json:"type"` + Description string `json:"description"` + IncludeScaling bool `json:"include_scaling"` + RequireProvenance bool `json:"require_provenance"` + }{ + Name: "test-environment", + Type: "K8S", + Description: "test-environment", + IncludeScaling: true, + RequireProvenance: true, + }, + }, + expectedLog: "############### PAYLOAD ###############\npayload sent to: http://localhost:8001/api/v2/environments/docs-cmd-test-user-shared\nthis is the payload being sent: \n {\n \"name\": \"test-environment\",\n \"type\": \"K8S\",\n \"description\": \"test-environment\",\n \"include_scaling\": true,\n \"require_provenance\": true\n}\n", + }, + { + name: "GET request with debug does not log non-existent payload", + params: &RequestParams{ + Method: http.MethodGet, + URL: "https://app.kosli.com/api/v2/environments/cyber-dojo", + DryRun: false, + }, + }, + } { + suite.Suite.Run(t.name, func() { + buf := new(bytes.Buffer) + client, err := NewKosliClient("", 1, true, logger.NewLogger(buf, buf, false)) + require.NoError(suite.Suite.T(), err) + resp, err := client.Do(t.params) + require.NoError(suite.Suite.T(), err) + + output := buf.String() + require.Equal(suite.Suite.T(), t.expectedLog, output) + if t.expectedBody != "" { + require.Equal(suite.Suite.T(), t.expectedBody, resp.Body) + } + }) + } +} + func (suite *RequestsTestSuite) TestCreateMultipartRequestBody() { for _, t := range []struct { name string