PUT request adding Reader in Body

Hello - I am having some issues with sending a PUT request to FreshService for adding a private note to a ticket. Every time I try to send either PUT or POST, the Body introduces some sort of Reader: key. This is especially troublesome when according to their documentation all that should be included in the body is the note itself: https://api.freshservice.com/#create_a_note.

Here is the error specifically I am getting:

{"code":"invalid_json","message":"Request body has invalid json format"}

Here is the code I’m working with:
https://play.golang.org/p/nte_4rYDcbb

package main

// PUTRequest sends a PUT request to /api/v2/ at the specified endpoint
func PUTRequest(endpoint, apiKey string, jsonStr []byte) ([]byte, error) {
	url := "https://example.freshservice.com/api/v2/"
	req, _ := http.NewRequest(http.MethodPut, url+endpoint, bytes.NewBuffer(jsonStr))
	log.Printf("[req:] %+v\n", req)

	key := base64.URLEncoding.EncodeToString([]byte(apiKey + ":X"))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Add("Authorization", "Basic "+key)

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("could not client.Do:\n%v", err)
	}

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	return body, nil

}

// CreateNote creates a private note on *TicketList with the message specified
func (ticketList *TicketList) CreateNote(message, apiKey string) ([]byte, error) {
	payload, _ := json.Marshal(message)
	var responses []byte
	for i := range ticketList.Tickets {
		ticketUrl := fmt.Sprintf("tickets/%v", ticketList.Tickets[i].ID)
		resp, err := PUTRequest(ticketUrl, apiKey, payload)
		if err != nil {
			return nil, fmt.Errorf("could not update ticket:\n%v", err)
		}
		responses = append(responses, resp...)
	}
	return responses, nil
}

func main() {
response, err := ticketList.CreateNote("This is a test", "<ApiKey>")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("[response:] %s", response)
}

With the log.Printfs here is the output:

2021/09/03 09:20:07 [resp:] &{Method:GET URL:https://example.freshservice.com/api/v2/tickets/<Ticket#>/requested_items Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Authorization:[Basic <base64Key>=] Content-Type:[application/json]] Body:{} GetBody:0x11f37c0 ContentLength:0 TransferEncoding:[] Close:false Host:example.freshservice.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00001e0b8}
2021/09/03 09:20:07 [reqlist:] {RequestedItems:[{OnboardingItems:{StartDate:2021-08-25 FirstName:test LastName:UserMan Address:1234 Main ST, nowhere USA PhoneNumber:123-456-7890 OfficeLocation:Remote Department:Engineering LaptopType:Mac LaptopExceptions: ReportingManagerEmail:manager.name@example.com IsManager:false IsContractor:false Monitor:false KeyboardAndMouse:false Mouse:false}}]}
2021/09/03 09:20:07 [req:] &{Method:PUT URL:https://example.freshservice.com/api/v2/tickets/<ticket#> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[] Body:{Reader:"This is a test"} GetBody:0x11dc8a0 ContentLength:16 TransferEncoding:[] Close:false Host:example.freshservice.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00001e0b8}
2021/09/03 09:20:07 [response:] {"code":"invalid_json","message":"Request body has invalid json format"}

As you can see on line 3 ([req:]), somehow Body is Body:{Reader:"This is a test"}. All I want is to send Body{"this is a test"}. My current theory is that in http.NewRequest it uses body io.Reader and io.Reader is somehow adding Reader to the body.

This hasn’t been an issue if I create a user in FreshService, which uses POST, and still includes the Body: {Reader: {key:value}} as well. But with PUT, for some reason having Body:{Reader:} proves to be an issue.

I have also tried running:

payload, _ := json.Marshal(map[string]string{"body": message})

within PUTRequest, however that still proves to be an issue regardless. Here is the request that it sends:

Body:{Reader:{"body":"This is a test"}}

However, still gets the same error:

{"code":"invalid_json","message":"Request body has invalid json format"}

Let me know what else I can try, still learning Go as I go.

Problem #1: I could not create a note
Resolution: Incorrect endpoint and method.

On line 41 and 42 I have:

ticketUrl := fmt.Sprintf("tickets/%v", ticketList.Tickets[i].ID)
resp, err := PUTRequest(ticketUrl, apiKey, payload)

This should be:

ticketUrl := fmt.Sprintf("tickets/%v/notes", ticketList.Tickets[i].ID)
resp, err := MethodRequest("POST", ticketUrl, apiKey, payload)

Note the added /notes at the end. I also need to make a POST request and not PUT.

Problem #2: I could not assign the tasks
Resolution: I forgot to add responder_id to the payload :man_facepalming: and I needed to add a description as well.

While not within the original post, I realized I needed to add a struct for assigning tasks:

type AssignTasks struct {
	ResponderID int    `json:"responder_id"`
	Description string `json:"description"`
}

This then was used in my methods/functions to assign tasks:

payload, _ := json.Marshal(&AssignTasks{
		ResponderID: agentsList.Agents[0].Id,
		Description: "new hire",
})

I’ve added the changes here: https://play.golang.org/p/8Kx4NFCF5Ki
Didn’t include the assigning tasks info. If interested I can update with the couple of functions/methods I made for that.

Edit:
Forgot to include a few other things -
The body still was Body:{Reader}, however didn’t seem to affect the responses.
Here is the request that was sent for notes:

POST URL:https://example.freshservice.com/api/v2/tickets/<ticket#>/notes Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Authorization:[Basic <base64apiKey>=] Content-Type:[application/json]] Body:{Reader:{"body":"This is a test"}} GetBody:0x11dc8a0 ContentLength:25 TransferEncoding:[] Close:false Host:example.freshservice.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00001e0b8}

and the request sent for assigning the ticket:

{Method:PUT URL:https://example.freshservice.com/api/v2/tickets/<ticket#> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Authorization:[Basic <base64apiKey>=] Content-Type:[application/json]] Body:{Reader:} GetBody:0x11dc8a0 ContentLength:53 TransferEncoding:[] Close:false example.freshservice.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00001e0b8

Both responded okay. So not really something wrong Reader being added to Body, but rather a problem of wrong endpoints and forgetting to add the proper payload.