Skip to content

Commit c480be6

Browse files
authored
Payload for debug (#522)
* Add payload logging for when --debug flag set * Fix type in log message * Fix issue with consuming request body before sending request on debug * Add missing format string * Add tests for debug payload
1 parent e4e2851 commit c480be6

File tree

2 files changed

+93
-15
lines changed

2 files changed

+93
-15
lines changed

internal/requests/requests.go

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,22 @@ func (c *Client) Do(p *RequestParams) (*HTTPResponse, error) {
216216
c.Logger.Info("############### THIS IS A DRY-RUN ###############")
217217
c.Logger.Info("the request would have been sent to: %s", req.URL)
218218

219-
// Check the content type to determine what to log
220-
contentType := req.Header.Get("Content-Type")
221-
if strings.Contains(contentType, "multipart/form-data") {
222-
// Log only the JSON fields for multipart/form-data
223-
c.Logger.Info("this is the payload data that would be sent in a real run:")
224-
for key, value := range jsonFields {
225-
c.Logger.Info("Field: %s, Value: %+v", key, string(value.([]byte)))
226-
}
227-
} else if req.Body != nil {
228-
// For non-multipart requests, log the full JSON body
229-
reqBody, err := io.ReadAll(req.Body)
230-
if err != nil {
231-
return nil, fmt.Errorf("failed to read request body to %s : %v", req.URL, err)
232-
}
233-
c.Logger.Info("this is the payload that would be sent in a real run: \n %+v", string(reqBody))
219+
// log the payload
220+
err := c.PayloadOutput(req, jsonFields, "this is the payload that would be sent in a real run:")
221+
if err != nil {
222+
return nil, err
234223
}
235224
return nil, nil
236225
} else {
226+
if c.Debug && req.Body != nil {
227+
// log the payload
228+
c.Logger.Info("############### PAYLOAD ###############")
229+
c.Logger.Info("payload sent to: %s", req.URL)
230+
err := c.PayloadOutput(req, jsonFields, "this is the payload being sent:")
231+
if err != nil {
232+
c.Logger.Error("failed to log payload: %v \nContinuing with the request...", err)
233+
}
234+
}
237235
resp, err := c.HttpClient.Do(req)
238236
if err != nil {
239237
// err from retryable client is detailed enough
@@ -279,3 +277,26 @@ func (c *Client) Do(p *RequestParams) (*HTTPResponse, error) {
279277
return &HTTPResponse{string(body), resp}, nil
280278
}
281279
}
280+
281+
func (c *Client) PayloadOutput(req *http.Request, jsonFields map[string]interface{}, message string) error {
282+
// Check the content type to determine what to log
283+
contentType := req.Header.Get("Content-Type")
284+
if strings.Contains(contentType, "multipart/form-data") {
285+
// Log only the JSON fields for multipart/form-data
286+
c.Logger.Info(message)
287+
for key, value := range jsonFields {
288+
c.Logger.Info("Field: %s, Value: %+v", key, string(value.([]byte)))
289+
}
290+
} else if req.Body != nil {
291+
// For non-multipart requests, log the full JSON body
292+
// Create a copy of the body to avoid consuming the original stream
293+
bodyBytes, err := io.ReadAll(req.Body)
294+
if err != nil {
295+
return fmt.Errorf("failed to read request body to %s : %v", req.URL, err)
296+
}
297+
// Restore the body for the actual request
298+
req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
299+
c.Logger.Info("%s \n %+v", message, string(bodyBytes))
300+
}
301+
return nil
302+
}

internal/requests/requests_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,63 @@ func (suite *RequestsTestSuite) TestDo() {
386386
}
387387
}
388388

389+
func (suite *RequestsTestSuite) TestDebugPayloadOutput() {
390+
for _, t := range []struct {
391+
name string
392+
params *RequestParams
393+
wantError bool
394+
expectedLog string
395+
expectedErrorMsg string
396+
expectedBody string
397+
}{
398+
{
399+
name: "PUT request with debug logs payload",
400+
params: &RequestParams{
401+
Method: http.MethodPut,
402+
URL: "http://localhost:8001/api/v2/environments/docs-cmd-test-user-shared",
403+
DryRun: false,
404+
Token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY",
405+
Payload: struct {
406+
Name string `json:"name"`
407+
Type string `json:"type"`
408+
Description string `json:"description"`
409+
IncludeScaling bool `json:"include_scaling"`
410+
RequireProvenance bool `json:"require_provenance"`
411+
}{
412+
Name: "test-environment",
413+
Type: "K8S",
414+
Description: "test-environment",
415+
IncludeScaling: true,
416+
RequireProvenance: true,
417+
},
418+
},
419+
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",
420+
},
421+
{
422+
name: "GET request with debug does not log non-existent payload",
423+
params: &RequestParams{
424+
Method: http.MethodGet,
425+
URL: "https://app.kosli.com/api/v2/environments/cyber-dojo",
426+
DryRun: false,
427+
},
428+
},
429+
} {
430+
suite.Suite.Run(t.name, func() {
431+
buf := new(bytes.Buffer)
432+
client, err := NewKosliClient("", 1, true, logger.NewLogger(buf, buf, false))
433+
require.NoError(suite.Suite.T(), err)
434+
resp, err := client.Do(t.params)
435+
require.NoError(suite.Suite.T(), err)
436+
437+
output := buf.String()
438+
require.Equal(suite.Suite.T(), t.expectedLog, output)
439+
if t.expectedBody != "" {
440+
require.Equal(suite.Suite.T(), t.expectedBody, resp.Body)
441+
}
442+
})
443+
}
444+
}
445+
389446
func (suite *RequestsTestSuite) TestCreateMultipartRequestBody() {
390447
for _, t := range []struct {
391448
name string

0 commit comments

Comments
 (0)