HTTP call error to shopify when sending array in json request - go

This is an http post response to create a new shopify blog.
blog := Blog{
Title: fileNameWithoutExtSliceNotation(doc.Name()),
Template_suffix: "hit_parader_issue_template",
Metafields: []BlogMeta{
{
Key: "filter_tag_list",
Value: "Popular",
Type: "single_line_text_field",
Namespace: "global",
},
},
}
blogJSON, err := json.Marshal(blog)
blogReq, err := http.NewRequest("POST", newBlogQuery, bytes.NewBuffer(blogJSON))
blogReq.Header.Add("X-Shopify-Access-Token", TOKEN)
blogReq.Header.Add("Content-Type", "application/json")
resp1, err := client.Do(blogReq)
my structs are like below
type Blog struct {
Title string `json:"title"`
Template_suffix string `json:"template_suffix"`
Metafields []BlogMeta
}
type BlogMeta struct {
Key string
Value string
Type string
Namespace string
}
im getting an error saying
{"errors":{"blog":"Required parameter missing or invalid"}}
These are my blow metafield definitions.
I have all the necessary fields listed in the following api call. Please help.
Im using below API
https://shopify.dev/api/admin-rest/2022-10/resources/blog#post-blogs
NOTE: the API documentation says that I need to present a array for the metafields. But I think my code is correct. IS there something im missing? Any help would be appreciated.

Looking at the API docs it seems that the blog JSON object should be nested in a root object, i.e. it should be {"blog": {"title": ...,}} rather than {"title": ...,}. If that's the case then that would also explain the error message.
To fix that you can do something like this:
blog := &Blog{
// ...
}
data, err := json.Marshal(map[string]any{"blog": blog})
if err != nil {
return err
}
r, err := http.NewRequest("POST", url, bytes.NewReader(data))
if err != nil {
return err
}
r.Header.Add("X-Shopify-Access-Token", TOKEN)
r.Header.Add("Content-Type", "application/json")
resp, err := client.Do(r)
if err != nil {
return err
}
// ...

Related

Converting a complex http json response array to a simple struct slice without creating several structs to match the response using Go

If an http response comes in a format that is not directly a list of objects the only way I could figure out how to convert them to structs is by creating two structs to match the exact format of the response. Is there anyway to do this cleaner where I can just create a Product struct and don't need to create the ProductRes wrapper struct?
Below is an example of what the response from the api I am calling looks like:
{
"items": [
{
"name": "Product 1",
"price": 20.45
},
{
"name": "Product 2",
"price": 31.24
}
]
}
Here are the two structs I create to convert the api response to a slice of Product:
type Product struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
type ProductRes struct {
Items []Product `json:"items"`
}
Here is part of the code to make the api request and convert the response to a slice of Product:
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatalln(err)
}
resp, err := c.client.Do(req)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
products := ProductRes{}
// This line makes me think I actually do need multiple structs, unless I can modify body somehow prior to sending it in here
json.Unmarshal(body, &products)
You can eliminate the declared type ProductRes by using an anonymous type:
var wrapper struct { Items []Product }
err := json.Unmarshal(body, &wrapper)
if err != nil {
// TODO: handle error
}
products := wrapper.Items
You can also use a map:
var m map[string][]Product
err := json.Unmarshal(body, &m)
if err != nil {
// TODO: handle error
}
products := m["items"]

json.Marshal for http post request with echo

I have two golang servers running on localhost.
They are using different ports.
I want to create a post request on one that sends a JSON object to the other one.
I am using the echo framework (if this matters)
The error I am getting is when I try to marshal the object for the post object:
2-valued json.Marshal(data) (value of type ([]byte, error)) where single value is expected
server 1:
type SendEmail struct {
SenderName string `json:"senderName,omitempty" bson:"senderName,omitempty" validate:"required,min=3,max=128"`
SenderEmail string `json:"senderEmail" bson:"senderEmail" validate:"required,min=10,max=128"`
Subject string `json:"subject" bson:"subject" validate:"required,min=10,max=128"`
RecipientName string `json:"recipientName" bson:"recipientName" validate:"required,min=3,max=128"`
RecipientEmail string `json:"recipientEmail" bson:"recipientEmail" validate:"required,min=10,max=128"`
PlainTextContent string `json:"plainTextContent" bson:"plainTextContent" validate:"required,min=10,max=512"`
}
func resetPassword(c echo.Context) error {
email := c.Param("email")
if email == "" {
return c.String(http.StatusNotFound, "You have not supplied a valid email")
}
data := SendEmail{
RecipientEmail: email,
RecipientName: email,
SenderEmail: “test#test”,
SenderName: “name”,
Subject: "Reset Password",
PlainTextContent: "Here is your code to reset your password, if you did not request this email then please ignore.",
}
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
if err != nil {
fmt.Println(err)
}
defer req.Body.Close()
return c.JSON(http.StatusOK, email)
}
server 2:
e.GET("/", defaultRoute)
func defaultRoute(c echo.Context) (err error) {
u := SendEmail{}
if err = c.Bind(u); err != nil {
return
}
return c.JSON(http.StatusOK, u)
}
It's always nice to meet a Gopher. A few things you might want to know, Go supports multi-value returns in that a function can return more than one value.
byteInfo, err := json.Marshal(data) // has two values returned
// check if there was an error returned first
if err != nil{
// handle your error here
}
Now the line below in your code
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
Will become this
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", bytes.NewBuffer(byteInfo))
And you can continue with the rest of your code. Happy Coding!
json.Marshal returns []byte and error which means you're passing 4 values to http.NewRequest.
You should call json.Marshal first and then use the result for http.NewRequest.
body, err := json.Marshal(data)
if err != nil {
// deal with error
}
req, err := http.NewRequest("POST", "127.0.0.1:8081/", body)

Post API getting invalid character '-' in numeric literal

I'm trying to make a POST function to insert data in my database using go.
My Model:
type Funcstruct {
Name string `json:"Name"`
Status string `json:"Status"`
}
My post function:
//POST FUNC
func (app *App) createFunc(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(r.Body)
var data Func
err := decoder.Decode(&data)
if err != nil {
panic(err)
}
Name := data.Name
Status := data.Status
database, err := db.CreateDatabase()
if err != nil {
log.Fatal("Not connect to db")
}
_, err = database.Exec("INSERT INTO `func` (Name, Status) VALUES ($1, $2)", Name, Status)
if err != nil {
log.Fatal("Error inserting data")
}
log.Println("Alright!")
w.WriteHeader(http.StatusOK)
}
So i make one go build and after i make a ./nameproject
So, i go in the PostMan and make a post http request passing the Name and Status in form-data but i receive:
2019/07/12 22:20:38 http: panic serving [::1]:54600: invalid character
'-' in numeric literal
I experience the same issue while using form data in postman,I finally resolved to using json and it worked
You should change form-data to raw->JSON and send like this:
{
"Name" : "morefeizi",
"Status" : "1"
}
Send JSON Data from PostMan to unmarshal

Calling SOAP with Golang

I am new to golang and trying make a soap call with gowsdl .
I have generated the wsdl code and installed it as a package. I am however struggling to understand the syntax for call the method from it.
When I examine the package, this is what I want in the soap body:
type AccountUser struct {
XMLName xml.Name `xml:"http://exacttarget.com/wsdl/partnerAPI AccountUser"`
*APIObject
AccountUserID int32 `xml:"AccountUserID,omitempty"`
UserID string `xml:"UserID,omitempty"`
Password string `xml:"Password,omitempty"`
Name string `xml:"Name,omitempty"`
Email string `xml:"Email,omitempty"`
MustChangePassword bool `xml:"MustChangePassword,omitempty"`
ActiveFlag bool `xml:"ActiveFlag,omitempty"`
ChallengePhrase string `xml:"ChallengePhrase,omitempty"`
ChallengeAnswer string `xml:"ChallengeAnswer,omitempty"`
UserPermissions []*UserAccess `xml:"UserPermissions,omitempty"`
Delete int32 `xml:"Delete,omitempty"`
LastSuccessfulLogin time.Time `xml:"LastSuccessfulLogin,omitempty"`
IsAPIUser bool `xml:"IsAPIUser,omitempty"`
NotificationEmailAddress string `xml:"NotificationEmailAddress,omitempty"`
IsLocked bool `xml:"IsLocked,omitempty"`
Unlock bool `xml:"Unlock,omitempty"`
BusinessUnit int32 `xml:"BusinessUnit,omitempty"`
DefaultBusinessUnit int32 `xml:"DefaultBusinessUnit,omitempty"`
DefaultApplication string `xml:"DefaultApplication,omitempty"`
Locale *Locale `xml:"Locale,omitempty"`
TimeZone *TimeZone `xml:"TimeZone,omitempty"`
DefaultBusinessUnitObject *BusinessUnit `xml:"DefaultBusinessUnitObject,omitempty"`
AssociatedBusinessUnits struct {
BusinessUnit []*BusinessUnit `xml:"BusinessUnit,omitempty"`
} `xml:"AssociatedBusinessUnits,omitempty"`
Roles struct {
Role []*Role `xml:"Role,omitempty"`
} `xml:"Roles,omitempty"`
LanguageLocale *Locale `xml:"LanguageLocale,omitempty"`
SsoIdentities struct {
SsoIdentity []*SsoIdentity `xml:"SsoIdentity,omitempty"`
} `xml:"SsoIdentities,omitempty"`
}
And the method to call the SOAP is :
func (s *SOAPClient) Call(soapAction string, request, response interface{}) error {
envelope := SOAPEnvelope{
//Header: SoapHeader{},
}
envelope.Body.Content = request
buffer := new(bytes.Buffer)
encoder := xml.NewEncoder(buffer)
//encoder.Indent(" ", " ")
if err := encoder.Encode(envelope); err != nil {
return err
}
if err := encoder.Flush(); err != nil {
return err
}
log.Println(buffer.String())
req, err := http.NewRequest("POST", s.url, buffer)
if err != nil {
return err
}
if s.auth != nil {
req.SetBasicAuth(s.auth.Login, s.auth.Password)
}
req.Header.Add("Content-Type", "text/xml; charset=\"utf-8\"")
if soapAction != "" {
req.Header.Add("SOAPAction", soapAction)
}
req.Header.Set("User-Agent", "gowsdl/0.1")
req.Close = true
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: s.tls,
},
Dial: dialTimeout,
}
client := &http.Client{Transport: tr}
res, err := client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
rawbody, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
if len(rawbody) == 0 {
log.Println("empty response")
return nil
}
log.Println(string(rawbody))
respEnvelope := new(SOAPEnvelope)
respEnvelope.Body = SOAPBody{Content: response}
err = xml.Unmarshal(rawbody, respEnvelope)
if err != nil {
return err
}
fault := respEnvelope.Body.Fault
if fault != nil {
return fault
}
return nil
}
I have imported the package into my go file , and would love pointers on how to call this.
To use the generated code you obviously will have to first initialize the soap client with one of the generated "constructor" functions NewSOAPClient or NewSOAPClientWithTLSConfig.
After that you'll need to prepare two values that you can use as the request and response arguments to the Call method, they represent the body content of the soap request/response payloads.
The types of those two values will depend on what kind of call you want to make, for example the hypothetical calls create_account, update_account, and delete_account would usually require different types. Basically, the type of the request value should be marshalable into an xml that matches the xml expected by the soap service for the specified action, and the type of the response should be unmarshalable from an xml that matches the soap service's documented response for the specified action.
Consider this contrived example:
There is a SOAP service that allows you to create users. For you to be able to create a user with the service it requires you to send an email and a password, and if everyting's ok it will return an id. In such a case your two request/response types would look like this:
type CreateUserRequest struct {
Email string `xml:"Email,omitempty"`
Password string `xml:"Password,omitempty"`
}
type CreateUserResponse struct {
ID string `xml:"ID"`
}
Then the client code would look like this:
client := NewSOAPClient("https://soap.example.com/call", true, nil)
req := &CreateUserRequest{
Email: "jdoe#example.com",
Password: "1234567890",
}
res := &CreateUserResponse{}
if err := client.Call("create_user", req, res); err != nil {
panic(err)
}
// if everything went well res.ID should have its
// value set with the one returned by the service.
fmt.Println(res.ID)

HTTP POST in GO Lang throws error

I am trying to make a Post request using in GO Lang. The API URL is expecting a username and password field to be passed to it in body. However I keep getting the below error ?
I am not sure what am i doing wrong here ?
Error
url.Values undefined (type string has no field or method Values)
Go Function
func makeHttpPostReq(url string, username string, password string){
client := http.Client{}
req, err := http.NewRequest("POST", url, url.Values{"username": {username}, "password": {password}})
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Unable to reach the server.")
} else {
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("body=", string(body))
}
}
The argument to makeHttpPostReq states url is a string but you are treating it as a struct url.Values, thus the error
has no field or method
You are reusing the url word.
url is in your case resolving to url as a string not a net/url

Resources