zorkian / go-datadog-api

A Go implementation of the Datadog API.
BSD 3-Clause "New" or "Revised" License
183 stars 156 forks source link

GetDashboard Fails Fatal When Scatterplot Exists in Dashboard #281

Open blaines opened 4 years ago

blaines commented 4 years ago

When a Dashboard in Datadog contains a scatterplot, the resulting API response from GetDashboard cannot be parsed.

Error:

2019/11/01 01:32:13 GetDashboard fatal: json: cannot unmarshal object into Go struct field GraphDefinition.requests of type []datadog.GraphDefinitionRequest

Related code: https://github.com/zorkian/go-datadog-api/blob/master/dashboards.go#L163

Requests []GraphDefinitionRequest `json:"requests,omitempty"`

A scatterplot in the API response looks like the following, note the viz type of scatterplot:

{
    "definition": {
        "yaxis": {
            "scale": "linear",
            "min": "auto",
            "max": "auto",
            "label": "",
            "includeZero": true
        },
        "color_by_groups": [],
        "xaxis": {
            "scale": "log",
            "includeZero": false
        },
        "viz": "scatterplot",
        "requests": {
            "y": {
                "q": "max:example.stat{*} by {example_tag}",
                "aggregator": "max"
            },
            "x": {
                "q": "max:example.stat{*} by {example_tag}",
                "aggregator": "max"
            }
        }
    }
}

As you can see "requests" is no longer a slice, causing deserialization issues.

Unfortunately I can't come up with a backwards compatible way to support scatterplots. Although I'm working on parsing this in a fork with a custom unmarshaler.

blaines commented 4 years ago

Here's the functioning change I've come up with thus far.

type GraphDefinition struct {
    ...
    RequestsRawMessage  json.RawMessage          `json:"requests,omitempty"`
    Requests            []GraphDefinitionRequest `json:"-"`
    ScatterplotRequests struct {
        Y struct {
            Q          string `json:"q"`
            Aggregator string `json:"aggregator"`
        } `json:"y"`
        X struct {
            Q          string `json:"q"`
            Aggregator string `json:"aggregator"`
        } `json:"x"`
    } `json:"-"`
    ...
}

Further below I added a custom unmarshaler for GraphDefinition data:

func (gd *GraphDefinition) UnmarshalJSON(data []byte) error {
    type Alias GraphDefinition
    alias := &struct {
        *Alias
    }{
        Alias: (*Alias)(gd),
    }

    json.Unmarshal(data, &alias)

    if len(alias.RequestsRawMessage) != 0 {
        if *alias.Viz == "scatterplot" {
            err := json.Unmarshal(alias.RequestsRawMessage, &alias.ScatterplotRequests)
            if err != nil {
                panic(err)
            }
        } else {
            err := json.Unmarshal(alias.RequestsRawMessage, &alias.Requests)
            if err != nil {
                panic(err)
            }
        }
    }
    return nil
}
iancullinane commented 4 years ago

I am also having this problem, and an additional problem with GetDashboard the following similar output:

cannot unmarshal string into Go struct field GraphDefinition.dash.graphs.definition.group of type []string

bkabrda commented 4 years ago

I think that using the custom unmarshaller is a reasonable way to go forward in this issue - I'd accept a PR that implements that.

However, there is another way - using the /api/v1/dashboard API and the related *Board (GetBoard, ...) methods that seem to be handling this case fine. Would that be an option for you?

blaines commented 4 years ago

@bkabrda please double check me, but when I used /api/v1/dashboard the result was not equivalent. I'll see if I can get specific details on that again.

blaines commented 4 years ago

Am I missing something /api/v1/dashboard isn't supported yet by this lib? I added my own functionality in a fork.