Skip to content

Create bring-grafana-dashboards-to-nr.mdx#23931

Open
mangulonr wants to merge 4 commits intonewrelic:developfrom
mangulonr:develop
Open

Create bring-grafana-dashboards-to-nr.mdx#23931
mangulonr wants to merge 4 commits intonewrelic:developfrom
mangulonr:develop

Conversation

@mangulonr
Copy link
Copy Markdown
Contributor

Please follow conventional commit standards
in your commit messages and pull request title.

Give us some context

  • What problems does this PR solve?
  • Add any context that will help us review your changes such as testing notes,
    links to related docs, screenshots, etc.
  • If your issue relates to an existing GitHub issue, please link to it.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Hi @mangulonr 👋

Thanks for your pull request! Your PR is in a queue, and a writer will take a look soon. We generally publish small edits within one business day, and larger edits within three days.

Please ensure the propsed changes look good by building it first in your local environment. Refer to this contribution guide to get the site up and running in your local.

If you really require a preview url, reach out to one of the writers and they will generate one for you.

@mangulonr mangulonr marked this pull request as draft May 4, 2026 14:47
@mangulonr
Copy link
Copy Markdown
Contributor Author

netlify build

Copy link
Copy Markdown
Contributor

@Philip-R-Beckwith Philip-R-Beckwith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would strongly suggest removing the large scripts from this doc.
While it may feel like we're helping customers by handing edge-cases, we're actually actively working against them.
If customers who don't want to use bash and/or are unfamiliar with bash are faced with these massive examples, it's much harder for them to get their own workflow up and running if we're giving them a wall of code.

What we're kind of doing with this huge example is writing for the non technical user. Which is a fallacy. We do have some non technical users, and for them, we need to put this endpoint behind a UI. But for our technical users, we have to assume that they have both the skills and the desire to take code snip-its and use them in their own workflow. By assuming that we wrote exactly the script that they will want, is to not understand the intended audience.

Almost half of this page is eaten up by scripts that our customer may not even want.

Comment on lines +149 to +267
```bash
# Translate and upload all dashboards to New Relic (default, US region)
./translate_dashboards.sh path/to/dashboards/
```

**Script (`translate_dashboards.sh`):**

```bash
#!/usr/bin/env bash
# translate_dashboards.sh
# Translates all Grafana dashboard JSON files in a directory to New Relic dashboards
# and uploads each to New Relic by default.
#
# Required env vars:
# NR_USER_KEY — Your New Relic user API key
#
# Optional env vars:
# ACCOUNT_ID — New Relic account ID to override the default query target for all dashboards.
# Required when UPLOAD=true (the default).
# REGION — "US" (default) or "EU"
# OUTPUT_DIR — Directory to save translated files (default: <input_dir>/translated)
# UPLOAD — Set to "false" to skip uploading and only save the translated JSON files locally.

set -euo pipefail

# ── Configuration ────────────────────────────────────────────────────────────
NR_USER_KEY="${NR_USER_KEY:?Please set NR_USER_KEY to your New Relic user API key}"
INPUT_DIR="${1:?Usage: $0 <path/to/dashboards/directory>}"
REGION="${REGION:-US}"
ACCOUNT_ID="${ACCOUNT_ID:-}"
UPLOAD="${UPLOAD:-true}"
OUTPUT_DIR="${OUTPUT_DIR:-${INPUT_DIR}/translated}"

if [[ "${REGION}" == "EU" ]]; then
TRANSLATOR_ENDPOINT="https://prometheus-api.eu.newrelic.com/api/v1/translate/dashboard/grafana"
NERDGRAPH_ENDPOINT="https://api.eu.newrelic.com/graphql"
else
TRANSLATOR_ENDPOINT="https://prometheus-api.newrelic.com/api/v1/translate/dashboard/grafana"
NERDGRAPH_ENDPOINT="https://api.newrelic.com/graphql"
fi

mkdir -p "${OUTPUT_DIR}"

# ── Warn early if upload is on but no account ID provided ─────────────────────
if [[ "${UPLOAD}" == "true" && -z "${ACCOUNT_ID}" ]]; then
echo "WARNING: ACCOUNT_ID is not set — dashboards will be saved locally only (upload skipped)."
UPLOAD="false"
fi

# ── Validate input ────────────────────────────────────────────────────────────
shopt -s nullglob
JSON_FILES=("${INPUT_DIR}"/*.json)
shopt -u nullglob

if [[ ${#JSON_FILES[@]} -eq 0 ]]; then
echo "No JSON files found in: ${INPUT_DIR}"
exit 1
fi

echo "Found ${#JSON_FILES[@]} dashboard(s) in: ${INPUT_DIR}"
echo "Output directory: ${OUTPUT_DIR}"
[[ "${UPLOAD}" == "true" ]] && echo "Upload: enabled (account: ${ACCOUNT_ID})" || echo "Upload: disabled (local files only)"
echo ""

# ── Translate each file ───────────────────────────────────────────────────────
SUCCESS=0
FAIL=0

for JSON_FILE in "${JSON_FILES[@]}"; do
BASENAME=$(basename "${JSON_FILE}" .json)
OUTPUT_FILE="${OUTPUT_DIR}/${BASENAME}_translated.json"

echo "Translating: ${BASENAME}.json ..."

if [[ -n "${ACCOUNT_ID}" ]]; then
PAYLOAD=$(jq -c -n \
--arg accountId "${ACCOUNT_ID}" \
--slurpfile dashboard "${JSON_FILE}" \
'{accountId: ($accountId | tonumber), dashboard: $dashboard[0]}')
else
PAYLOAD=$(jq -c -n \
--slurpfile dashboard "${JSON_FILE}" \
'{dashboard: $dashboard[0]}')
fi

if NR_DASHBOARD=$(curl -sf "${TRANSLATOR_ENDPOINT}" \
-H "Content-Type: application/json" \
-H "x-api-key: ${NR_USER_KEY}" \
-d "${PAYLOAD}" | jq '.dashboard'); then

echo "${NR_DASHBOARD}" > "${OUTPUT_FILE}"
echo " Saved → ${OUTPUT_FILE}"

# ── Upload (default: on) ────────────────────────────────────────────────────
if [[ "${UPLOAD}" == "true" ]]; then
RESULT=$(curl -sf -X POST "${NERDGRAPH_ENDPOINT}" \
-H "Content-Type: application/json" \
-H "API-Key: ${NR_USER_KEY}" \
-d "$(jq -cn \
--arg query 'mutation($accountId: Int!, $dashboard: DashboardInput!) { dashboardCreate(accountId: $accountId, dashboard: $dashboard) { entityResult { guid name } errors { description } } }' \
--argjson variables "{\"accountId\": ${ACCOUNT_ID}, \"dashboard\": ${NR_DASHBOARD}}" \
'{query: $query, variables: $variables}')" \
| jq -r '.data.dashboardCreate.entityResult.name // "upload failed"')

echo " Uploaded → ${RESULT}"
fi

((SUCCESS++))
else
echo " ERROR: Translation failed for ${BASENAME}.json"
((FAIL++))
fi
done

# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "────────────────────────────────────────"
echo "Done. Translated: ${SUCCESS} | Failed: ${FAIL}"
echo "Output directory: ${OUTPUT_DIR}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Providing a large script like this isn't really documentation.
I haven't seen other places in our docs where we provide 100+ lines of code as documentation.
This really does pigeonhole our customers into using this authoritative script.
Instead of having them have to dig though this massive block to figure out how to use the endpoint, the examples should be the shortest amount of info necessary to understand how to use the endpoint.

If we want to proved THE script that we want customers to use... It should be built into an endpoint on our end, or we should provide the script in the top languages we'd expect customers to use. Python/ Javascript/ bash.

It's also strange that the rest of the examples are in collapsable blocks but this one is not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As commented, I 'm going to simplify the script.
Did Cloudsoft provide a Python version of the script?

Regarding the format, you're right. Either all of them or none of them should be included inside collapsable blocks. I will review it today, consider this version as an early draft.

Comment on lines +48 to +139
Translates one Grafana dashboard JSON file and uploads it to New Relic by default.

**Usage:**

**Required:** `NR_USER_KEY` always. `ACCOUNT_ID` is required when `UPLOAD=true` (the default). All other options are documented in the script header.

```bash
# Translate and upload to New Relic (default, US region)
./translate_dashboard.sh path/to/dashboard.json
```

**Script (`translate_dashboard.sh`):**

```bash
#!/usr/bin/env bash
# translate_dashboard.sh
# Translates a single Grafana dashboard JSON to a New Relic dashboard
# and uploads it to New Relic by default.
#
# Required env vars:
# NR_USER_KEY — Your New Relic user API key
#
# Optional env vars:
# ACCOUNT_ID — New Relic account ID to override the default query target.
# Required when UPLOAD=true (the default).
# REGION — "US" (default) or "EU"
# UPLOAD — Set to "false" to skip uploading and only save the translated JSON locally.

set -euo pipefail

# ── Configuration ────────────────────────────────────────────────────────────
NR_USER_KEY="${NR_USER_KEY:?Please set NR_USER_KEY to your New Relic user API key}"
JSON_FILE="${1:?Usage: $0 <path/to/dashboard.json>}"
REGION="${REGION:-US}"
ACCOUNT_ID="${ACCOUNT_ID:-}"
UPLOAD="${UPLOAD:-true}"

if [[ "${REGION}" == "EU" ]]; then
TRANSLATOR_ENDPOINT="https://prometheus-api.eu.newrelic.com/api/v1/translate/dashboard/grafana"
NERDGRAPH_ENDPOINT="https://api.eu.newrelic.com/graphql"
else
TRANSLATOR_ENDPOINT="https://prometheus-api.newrelic.com/api/v1/translate/dashboard/grafana"
NERDGRAPH_ENDPOINT="https://api.newrelic.com/graphql"
fi

# ── Build request payload ─────────────────────────────────────────────────────
if [[ -n "${ACCOUNT_ID}" ]]; then
PAYLOAD=$(jq -c -n \
--arg accountId "${ACCOUNT_ID}" \
--slurpfile dashboard "${JSON_FILE}" \
'{accountId: ($accountId | tonumber), dashboard: $dashboard[0]}')
else
PAYLOAD=$(jq -c -n \
--slurpfile dashboard "${JSON_FILE}" \
'{dashboard: $dashboard[0]}')
fi

# ── Translate ─────────────────────────────────────────────────────────────────
echo "Translating: ${JSON_FILE} ..."

NR_DASHBOARD=$(curl -sf "${TRANSLATOR_ENDPOINT}" \
-H "Content-Type: application/json" \
-H "x-api-key: ${NR_USER_KEY}" \
-d "${PAYLOAD}" | jq '.dashboard')

OUTPUT_FILE="${JSON_FILE%.json}_translated.json"
echo "${NR_DASHBOARD}" > "${OUTPUT_FILE}"
echo "Translated dashboard saved to: ${OUTPUT_FILE}"

# ── Upload (default: on) ──────────────────────────────────────────────────────
if [[ "${UPLOAD}" == "true" ]]; then
if [[ -z "${ACCOUNT_ID}" ]]; then
echo "WARNING: ACCOUNT_ID is not set — skipping upload. Set ACCOUNT_ID to enable automatic upload."
else
echo "Uploading dashboard to New Relic (account: ${ACCOUNT_ID}) ..."

RESULT=$(curl -sf -X POST "${NERDGRAPH_ENDPOINT}" \
-H "Content-Type: application/json" \
-H "API-Key: ${NR_USER_KEY}" \
-d "$(jq -cn \
--arg query 'mutation($accountId: Int!, $dashboard: DashboardInput!) { dashboardCreate(accountId: $accountId, dashboard: $dashboard) { entityResult { guid name } errors { description } } }' \
--argjson variables "{\"accountId\": ${ACCOUNT_ID}, \"dashboard\": ${NR_DASHBOARD}}" \
'{query: $query, variables: $variables}')" \
| jq -r '.data.dashboardCreate')

echo "Upload result:"
echo "${RESULT}" | jq '.'
fi
else
echo "UPLOAD=false — dashboard saved locally only, not created in New Relic."
fi
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments here, it's activly working against the customer to provide such large examples.
It makes them have to dig though this code to pull out the pieces they want to use to get their workflow.
They may not be using bash, they may want to use python, javascript, or any other number of scripting languages.
So providing them a massive script, forces them to either, dig though this to figure out what's the minimal information they need which may be hard if they aren't well versed in bash, or may force them to use bash so they don't have to edit this script.

Neither of these situations are ideal, especially considering that this script isn't the only or the best use-case for our endpoint. Customers may want to set up script that pulls their grafana dashboards straight from the grafana API. It's easier to plug in small examples into the script that they want to write, rather than wrangle this large block. Especially if they'd rather use any other language than a bash script.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of agree, this doc is just the 1st version I created based on your original.
Scripts needs to be shorter and more simple

Comment on lines +28 to +36

There are two ways to translate your Grafana dashboards:

| | [Option 1: Scripts](#scripts) | [Option 2: Manual API request](#manual-api) |
|---|---|---|
| **Best for** | Translating one or many dashboards quickly | One-off translations, custom integrations |
| **Requirements** | `curl`, `jq`, bash | `curl`, `jq` |
| **Control** | Automated looping, uploads to NR by default | Full control over each request |
| **Output** | Uploads to NR by default; set `UPLOAD=false` to save locally only | Requires a second `curl` call to NerdGraph to upload to NR |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make sense. Customer can translate their dashboards using python, javascript, groovy, java, rust, go, anything. We're telling them that the only option they have is to use our curated, AI script, and it's simply not true.
We should not tell customers that they have to use these scripts.

  1. They can use any language that can submit a post to our endpoint
  2. The scripts on this page are very pigeonholed into one use case, even though our customers may have tons of different needs.

We're also calling the smaller examples "manual" which I don't understand.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the table to simplify the doc as it is repeating info already provided, I will look for alternatives instead of manual

Before using this API, you'll need:

* A [New Relic user API key](/docs/apis/get-started/intro-apis/types-new-relic-api-keys#user-api-key)
* Your Grafana dashboard exported as JSON (in Grafana: Settings → JSON Model → Copy to Clipboard)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessarily true if the customer writes a script to fetch their dashboard from Grafana's API

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're correct. Item removed

* A [New Relic user API key](/docs/apis/get-started/intro-apis/types-new-relic-api-keys#user-api-key)
* Your Grafana dashboard exported as JSON (in Grafana: Settings → JSON Model → Copy to Clipboard)
* Your New Relic account ID (if you want to override the default account)
* [`curl`](https://curl.se/) and [`jq`](https://stedolan.github.io/jq/) installed (required for the scripts and curl examples)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is is untrue if the customer wishes to use anything other than these scripts.
If they use a different language, this isn't true
And if they wish to use bash but not curl, and JQ there's other libraries that they could use instead.
These are only required if they use our examples exactly as they are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these requirements should only be commented in the scripts section.


## Option 1: Scripts [#scripts]

Copy and paste these scripts directly onto your laptop. Both scripts require `curl` and `jq` to be installed, and accept configuration via environment variables.
Copy link
Copy Markdown
Contributor

@Philip-R-Beckwith Philip-R-Beckwith May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy and paste these scripts directly onto your laptop.

It feels a bit weird that we're being this verbose about how to use the script, but also not providing further steps on how to run bash.
I think we can omit this callout with the assumption that our customers are familiar with bash scripts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed, now we just mention the commands required and the config/env variables capability


Copy and paste these scripts directly onto your laptop. Both scripts require `curl` and `jq` to be installed, and accept configuration via environment variables.

Both scripts **upload translated dashboards to New Relic automatically by default** via the NerdGraph API, and also save the translated JSON to a local file. Set `UPLOAD=false` to skip the upload and only save the translated JSON locally without creating it in your New Relic account.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange that we are always writing files locally.
We have an option UPLOAD=false but not one to skip writing the files to the local drive?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume so.
Is this a problem?
Should we delete the file after being uploaded?


---

## Option 2: Manual API request [#manual-api]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we calling these examples of automation manual?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both can be considered as manual, correct, let me rephrase it

}
```

### Examples [#manual-examples]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#manual-examples
I wouldn't call this section manual.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

<img
title="Translator action item example"
alt="Action Item Example"
src="/images/grafana_dashboard_translator_action_items.webp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This image is listed, but I'm not seeing it as a part of the PR.
Is it already included?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is pending, the PR is just an early draft, I need to add the image and the PromQL improvements to it


---

## Grafana and New Relic widget types
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep the long scripts, I'd move this to the top of the doc so it's not quite so buried.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


---

## Troubleshoot translation errors [#troubleshooting]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep the long scripts, I'd move this to the top of the doc so it's not quite so buried.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc should be much shorter, so no need IMO to move this section

@mangulonr
Copy link
Copy Markdown
Contributor Author

Please, don't merge the PR until confirmed

@mangulonr mangulonr marked this pull request as ready for review May 5, 2026 15:07
@mangulonr
Copy link
Copy Markdown
Contributor Author

netlify build fork

1 similar comment
@WriteMayur
Copy link
Copy Markdown
Contributor

netlify build fork

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants