How should I pass argument in function in my case? - go

There is a function that requests a token. I have a problem when I call it again to try to get the token again. In the body I see the following:
{"error":"invalid_request","error_description":"Missing form parameter: grant_type"}
I figured out that the problem lies in passing the payload argument. If I don't pass it, but just copy it inside function, everything is ok. Apparently, it has to be passed in some other way than payload *strings.Reader What's the reason for this behavior of the function and how to correctly pass payload argument into it? I need to pass it, not copy it inside because of reusing this function in my code.
fmt.Println(reflect.TypeOf(payload)) >> *strings.Reader
func getTokenAgain(payload *strings.Reader) string {
//payload := strings.NewReader("grant_type=password&username=admin&password=secret&client_id=admin-cli")
url := "https://my-api-url.com/auth/realms/CP/protocol/openid-connect/token"
method := "POST"
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
accessToken := gjson.Get(string(body), "access_token")
return accessToken.String()
}

Related

What do empty returns in the main function mean?

I copy-pasted the code from an API (https://api.magiceden.dev/). This code gets the link and prints a slice. Here's the code:
func main() {
url := "https://api-mainnet.magiceden.dev/v2/wallets/6xX3z7uxTNB68izZW2GHKnzno49dizqeVVc5ncVzdjFM/activities?offset=0&limit=100"
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
I'm new to Go, and I know about empty return statements in other functions, but what is returned in main function? That's the question and I still haven't found the answer.
I tried googling it, but I couldn't find any info or examples of empty return statements in main function.
When there is no return type in the function signature the return in such a function just stops the processing of the function at this point. No further statement are run then, but the registered defer functions are processed in the reverse order they have been registered.

How can I orginize code in the right way in my case? Golang

There are almost two identical functions that do approximately the same thing. What would be the right way to organize the code to avoid repetition in this case? The httpGetter() function accesses cloud platform API and gets JSON response, which I then parsed in another function and based on it I form terraform manifest from the template. The getToken() function does almost the same thing, just gets a token, which is then used in the httpGetter() function.
var accessToken = getToken()
func httpGetter(method, url string) (*http.Response, []byte) {
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
}
req.Header.Add("Accept", "application/json;version=35.0")
req.Header.Add("Authorization", "Bearer "+accessToken)
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
}
return res, body
}
func getToken() string {
url := "https://cloud-platform-api.com/api/sessions"
method := "POST"
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
}
req.Header.Add("Accept", "application/*+xml;version=35.0")
req.Header.Add("Authorization", "Basic <auth-hash>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
}
defer res.Body.Close()
if err != nil {
fmt.Println(err)
}
accessToken := res.Header.Get("x-vmware-vcloud-access-token")
return accessToken
}
First thing first if you know the method is a getter then you don't need to pass the method param so the signature would become something like below. Also, you are already returning a *http.Response back to the caller now it will be the callers decision on what to do with the response + the caller should decide what to do in case of if the HTTP call fails so return error and let the caller decide.
func HttpGet(url string) (*http.Response, error)
Now you also want POST method with body (in some cases) so have another function
func HttpPost(URL string, body []byte) (*http.Response, error)
Now to manage both signature and have a common code you could have a private method that will be just used in this file or you could also expose that method (it is up to you)
type Headers map[string]string
func http(method, URL string, body []byte, headers Headers) (*http.Response, error) { // we pass headers here so that the caller can pass custom headers for request
client := &http.Client{}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/json;version=35.0") // common static header you can keep as it is
for key, value := range headers {
req.Header.Add(key, value)
}
return client.Do(req)
}
Using this your call from the two methods would look like
func HttpGet(url string, headers Headers) (*http.Response, error) {
return http(http.MethodGet, URL, nil, headers)
}
func HttpPost(url string, body []byte, headers Headers) (*http.Response, error) {
return http(http.MethodPost, url, body, headers)
}
Now you can use this to pass the auth token from the caller like:
func getToken() {
res, err := httpPost("https://cloud-platform-api.com/api/sessions", nil,
map[string]string{
"Authorization": "Basic <auth-hash>",
}
if err != nil {
// do something with error
}
if res.StatusCode == http.StatusCreated {
// do what you want with the success response like unmarshalling to JSON
}
}
and for cases where you don't need to pass header, you can do
res, err := httpGet("some-url", nil) // you pass header as nil

How to read from array json response in Go

I have an API request that returns a refresh_token inside array, which looks something like this:
[
{
"refresh_token" : "C61551CEA183EDB767AA506926F423B339D78E2E2537B4AC7F8FEC0C29988819"
}
]
I need to access this refresh_token's value, and use it to query another API.
To do this, I'm attempting to first 'ReadAll' the response body, and then access the key inside of it by calling 'refreshToken'.
However, it's not working. Does anyone know how to resolve this as I can't figure it out?
Here's my code:
func Refresh(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
// q := url.Values{}
fetchUrl := "https://greatapiurl.com"
req, err := http.NewRequest("GET", fetchUrl, nil)
if err != nil {
fmt.Println("Errorrrrrrrrr")
os.Exit(1)
}
req.Header.Add("apikey", os.Getenv("ENV"))
req.Header.Add("Authorization", "Bearer "+os.Getenv("ENV"))
resp, err := client.Do(req)
if err != nil {
fmt.Println("Ahhhhhhhhhhhhh")
os.Exit(1)
}
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println(respBody["refresh_token"])
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}
If you do not need it as custom type you can cast it as []map[string]string
respBody, _ := ioutil.ReadAll(resp.Body)
var body []map[string]string
json.Unmarshal(respBody, &body)
fmt.Println(body[0]["refresh_token"])

How to return a body from another request

req, err := http.NewRequest("GET", "https://api.github.com/repos/octocat/Hello-World/pulls/1347", nil)
req.Header.Set("Accept", "application/vnd.github.v3.patch")
if err != nil {
check(err)
}
body, err := ioutil.ReadAll(req.Body)
ctxt.JSON(http.StatusOK, body)
Here I need to send api response from body of github api. But here I'm getting the following error:
"runtime error: invalid memory address or nil pointer dereference"
You're creating a new GET request with a nil body. See the function signature for http.NewRequest
func NewRequest(method, url string, body io.Reader) (*Request, error)
so when you access resp.Body, of course it's going to be nil.
Also, http.NewRequest just returns a request, it doesn't actually perform it.
To actually make the GET request with your request, you need to pass it to a http client's Do method. Like so:
response, err := http.DefaultClient.Do(resp)
EDIT: I would also add that naming your request as resp is confusing. I would recommend renaming the variable to req or request
this code will solve your issues.
client := &http.Client{}
apiURL := "https://api.github.com/repos/octocat/Hello-World/pulls/1347"
req, err := http.NewRequest("GET", apiURL, nil)
if err != nil {
check(err)
}
req.Header.Add("Accept", "application/vnd.github.v3.patch")
response, err := client.Do(req)
if err != nil {
check(err)
}
defer response.Body.Close()
contents, err := ioutil.ReadAll(response.Body)
if err != nil {
check(err)
}
ctxt.JSON(http.StatusOK, string(contents))

How to send new request with object instead of bytes?

I need to send an object of data e.g. {hello: "world", goodbye: "world"} to an API. I'm doing it like this right now:
inputs := form.GetElementsByTagName("input")
var data = make(map[string]interface{}) // after adding values this looks like this: {hello: "world", goodbye: "world"}
for value := range inputs {
// Append all values from the inputs to a new array, with the key named by the input name attribute
if inputs[value] != nil && inputs[value].(*dom.HTMLInputElement).Value != "" {
data[inputs[value].(*dom.HTMLInputElement).Name] = inputs[value].(*dom.HTMLInputElement).Value
}
}
parsedData, _ := json.Marshal(data)
req, _ := http.NewRequest(method, url, bytes.NewBuffer(parsedData))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
go func() { // Must be a goroutine
response, _ := client.Do(req)
defer response.Body.Close()
}()
The problem I'm having is since we're sending it as a byte, the server always returns error responses as it's expecting to deal with an object.
How can I make sure it's sending an object instead of bytes?
You are setting the content type to application/x-www-form-urlencoded while you are sending the data in json format, so change your content-type when setting the request headers, along with that do not skip the error to check what is the error returned:
parsedData, err := json.Marshal(data)
if err != nil{
fmt.Println(err)
}
req, err := http.NewRequest(method, url, parsedData) // send the parseData which are bytes returned from the marshal.
if err != nil{
fmt.Println(err)
}
req.Header.Set("Content-Type", "application/json") // set the content type to json
go func() { // Must be a goroutine
response, err := client.Do(req)
if err != nil{
fmt.Println(err)
}
defer response.Body.Close()
}()
// you should check for response status to verify the details as
fmt.Println("response Status:", response.Status)
fmt.Println("response Headers:", response.Header)
body, _ := ioutil.ReadAll(response.Body)
fmt.Println("response Body:", string(body))
One thing that should be taken into consideration is that you have not exported your struct fields. That can be the reason your json string becomes empty. Make your struct fields exportable by making their first letter of each field in caps.
I solved this using the "net/url" package.
data := url.Values{}
for value := range inputs {
// Append all values from the inputs to a new array, with the key named by the input name attribute
if inputs[value] != nil && inputs[value].(*dom.HTMLInputElement).Value != "" {
data.Add(inputs[value].(*dom.HTMLInputElement).Name, inputs[value].(*dom.HTMLInputElement).Value)
}
}
req, _ := http.NewRequest(method, actionUrl, strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

Resources