As apart of making services easier to monitor and maintain I would like to create a tool to create the New Relic Insight dashboards for us. I have already authored a program to deal with the updating SLI alarm thresholds. This program will use the configurations from there to build the dashboard.

How to get Dashboards

I feel like the documentation on Dashboards is a bit vague. Their blog post at least provides an example. Since us humans use names and not ids (well, with the exception of bookmarks) to find Dashboards the first step will be to check if the dashboard exists. This means paging through the list resource.

async function listDashboards(logger, apiKey ){
    let hasMore, page = 1; //Page must start at 1
    do {
        const target = "/dashboard";
        const qs = {
            page,
            per_page: 100 //Maxmimum
        };
        const response = await rp({
            uri: "https://api.newrelic.com/v2" + target,
            qs: qs,
            headers: {
                "X-Api-Key" : apiKey,
                "Accept": "application/json"
            },
            json: true,
            method: "GET"
        });
        hasMore = response.dashboards.length != 0;
        page++;

        logger.info("Dashboards", response.dashboards.map( (d) =>{
            return {id: d.id, title: d.title };
        }));
    }while(hasMore);
}

Cool! Now we have the specific ID for the dashboard. There is also a URL embedded into the response entity for the list operation, which might be useful in higher throughput scenarios.

Next up is trying to grab the actual dashboard. With the ID this just gets tacked onto the URL. So the following should successfully pull the full entity. From here I can copy the JSON entity instead of guessing what it would look like from the documentation.

async function showDashboard( apiKey, id ){
    const response = await rp({
        uri: "https://api.newrelic.com/v2/dashboards/" + id,
        qs: {},
        headers: {
            "X-Api-Key" : apiKey,
            "Accept": "application/json"
        },
        json: true,
        method: "GET"
    });
    return response.dashboard;
}

This gives us an entity like the following. This structure is a bit unwieldy but I think I can build what I need with it. Layouts are not simple, neither are charts. So I guess the union between the two shouldn’t be simple either.

{
  "id": 007,
  "title": "SLIs",
  "description": null,
  "icon": "bar-chart",
  "created_at": "2018-12-07T16:15:57Z",
  "updated_at": "2018-12-17T23:04:17Z",
  "visibility": "all",
  "editable": "editable_by_all",
  "ui_url": "https://insights.newrelic.com/accounts/42/dashboards/007",
  "api_url": "https://api.newrelic.com/v2/dashboards/007",
  "owner_email": "mark.eschbach",
  "metadata": {
    "version": 1
  },
  "widgets": [
    {
      "visualization": "line_chart",
      "layout": {
        "width": 3,
        "height": 1,
        "row": 1,
        "column": 1
      },
      "widget_id": 12,
      "account_id": 42,
      "data": [
        {
          "nrql": "SELECT percentile(duration, 50) * 1000 as p50, percentile(duration, 90) * 1000 as p90, percentile(duration, 99) * 1000 as p99 FROM Transaction SINCE '2018-12-01' TIMESERIES WHERE appName = 'App'"
        }
      ],
      "presentation": {
        "title": "Total Request Time",
        "notes": "p50: <100ms\np90: <200ms\np99: <2000ms"
      }
    },
    {
      "visualization": "line_chart",
      "layout": {
        "width": 3,
        "height": 1,
        "row": 2,
        "column": 1
      },
      "widget_id": 13,
      "account_id": 42,
      "data": [
        {
          "nrql": "SELECT count(*) as errored FROM Transaction SINCE '2018-12-01 00:00-0700' TIMESERIES WHERE appName = 'App' AND `response.status` like '5%'"
        }
      ],
      "presentation": {
        "title": "Error Count",
        "notes": "<1% over the quarter"
      }
    }
  ],
  "filter": null
}

Hmm, this one needs a part 2!