在字符串中插入变量

I have the hostname and a json formated string. I want to insert the hostname inside the value string of a key in that json formated string.

My full code:

func pager() string {
token := "xxxxxxxxxxx"
url := "https://api.pagerduty.com/incidents"
hostname, err := os.Hostname()
fmt.Println(hostname, err)

jsonStr := []byte(`{
    "incident": {
        "type": "incident",
        **"title": "Docker is down on."+hostname,**
        "service": {
          "id": "PWIXJZS",
          "type": "service_reference"
        },
        "priority": {
          "id": "P53ZZH5",
          "type": "priority_reference"
        },
        "urgency": "high",
        "incident_key": "baf7cf21b1da41b4b0221008339ff357",
        "body": {
          "type": "incident_body",
          "details": "A disk is getting full on this machine. You should investigate what is causing the disk to fill, and ensure that there is an automated process in place for ensuring data is rotated (eg. logs should have logrotate around them). If data is expected to stay on this disk forever, you should start planning to scale up to a larger disk."
        },
        "escalation_policy": {
          "id": "PT20YPA",
          "type": "escalation_policy_reference"
        }
    }
}`)

req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")
req.Header.Set("From", "shinoda@wathever.com")
req.Header.Set("Authorization", "Token token="+token)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
    panic(err)
}

return resp.Status
}

func main() {
    fmt.Println(pager())

}

I am not very familiar with go, in python I can do this easily and I dont know the proper way to do this in golang.

If someone could explain me, I would be grateful.

thanks in advance.

make a struct in go to represent the json

type 
    Incident struct {
        Type    string `json:"type"`
        Title   string `json:"title"`
        Service struct {
            ID   string `json:"id"`
            Type string `json:"type"`
        } `json:"service"`
        Priority struct {
            ID   string `json:"id"`
            Type string `json:"type"`
        } `json:"priority"`
        Urgency     string `json:"urgency"`
        IncidentKey string `json:"incident_key"`
        Body        struct {
            Type    string `json:"type"`
            Details string `json:"details"`
        } `json:"body"`
        EscalationPolicy struct {
            ID   string `json:"id"`
            Type string `json:"type"`
        } `json:"escalation_policy"`
}

then do something like

hostname,err:=os.Hostname()
if (err !=nil) {
   panic(err)
}
incident:=Incident{ Type: "incident",
                    Title: fmt.Sprintf("Docker is down on %s", hostname),
                    //...etc etc add all other fields

req, err := http.NewRequest("POST", url, json.Marshal(incident))

The workaround for declaring structs inside structs seems a bit clumsy (sorry)

Service: struct {
            ID   string `json:"id"`
            Type string `json:"type"`
        }{
            ID: "asdf",
            Type: "ABC",
       },

This other answer https://stackoverflow.com/a/53255390/1153938 shows how to split the structs inside the Incident struct and is a cleaner way of doing it

I'll leave this answer here because it might be of interest how to declare it in this way

If you are only calling json.Unmarshal then this way would be fine but for declaring stuff in a program as you need to do, perhaps not the best

The problem is as simple as properly quoting strings. This should solve your immediate problem:

jsonStr := []byte(`{
    "incident": {
        "type": "incident",
        "title": "Docker is down on `+hostname+`",
        "service": {
          "id": "PWIXJZS",
          "type": "service_reference"
        },
        "priority": {
          "id": "P53ZZH5",
          "type": "priority_reference"
        },
        "urgency": "high",
        "incident_key": "baf7cf21b1da41b4b0221008339ff357",
        "body": {
          "type": "incident_body",
          "details": "A disk is getting full on this machine. You should investigate what is causing the disk to fill, and ensure that there is an automated process in place for ensuring data is rotated (eg. logs should have logrotate around them). If data is expected to stay on this disk forever, you should start planning to scale up to a larger disk."
        },
        "escalation_policy": {
          "id": "PT20YPA",
          "type": "escalation_policy_reference"
        }
    }
}`)

But a much better approach, as suggested by @Vorsprung, is to use a proper Go data structure, and marshal it to JSON.

Try this solution:

hostname, err := os.Hostname()
if err != nil {
    // handle err
}

indent := fmt.Sprintf(`{
    "incident": {
        "type": "incident",
        "title": "Docker is down on %s",
        "service": {
          "id": "PWIXJZS",
          "type": "service_reference"
        },
        "priority": {
          "id": "P53ZZH5",
          "type": "priority_reference"
        },
        "urgency": "high",
        "incident_key": "baf7cf21b1da41b4b0221008339ff357",
        "body": {
          "type": "incident_body",
          "details": "A disk is getting full on this machine. You should investigate what is causing the disk to fill, and ensure that there is an automated process in place for ensuring data is rotated (eg. logs should have logrotate around them). If data is expected to stay on this disk forever, you should start planning to scale up to a larger disk."
        },
        "escalation_policy": {
          "id": "PT20YPA",
          "type": "escalation_policy_reference"
        }
    }
}`, hostname)
jsonStr := []byte(indent)

Building on @Vorsprung's response, unless I'm mistaken you may want to define the structs a little differently to avoid the error you are getting based on your response Once you've initialized and populated required properties on incident you should be able to POST the object after performing a json.Marshal() on the object.

jsonObject, _ := json.MarshalIndent(someIncident, "", "\t")

If the structs are to be used outside of this package you may want to change the names to uppercase to allow exportability.

type incident struct {
    Type             string           `json:"type"`
    Title            string           `json:"title"`
    Service          service          `json:"service"`
    Priority         priority         `json:"priority"`
    Urgency          string           `json:"urgency"`
    IncidentKey      string           `json:"incident_key"`
    Body             body             `json:"body"`
    EscalationPolicy escalationPolicy `json:"escalation_policy"`
}

type service struct {
    ID   string `json:"id"`
    Type string `json:"type"`
}

type priority struct {
    ID   string `json:"id"`
    Type string `json:"type"`
}

type body struct {
    Type    string `json:"type"`
    Details string `json:"details"`
}

type escalationPolicy struct {
    ID   string `json:"id"`
    Type string `json:"type"`
}

Initialization of the object:

someIncident := incident{
    Type:  "SomeType",
    Title: "SomeTitle",
    Service: service{
        ID:   "SomeId",
        Type: "SomeType"},
    Priority: priority{
        ID:   "SomeId",
        Type: "SomeType"},
    Urgency:     "SomeUrgency",
    IncidentKey: "SomeKey",
    Body: body{
        Type:    "SomeType",
        Details: "SomeDetails"},
    EscalationPolicy: escalationPolicy{
        ID:   "SomeId",
        Type: "SomeType"}}