Skip to content

Latest commit

 

History

127 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nominal Datasource Grafana Plugin

This repository contains Nominal Grafana plugins for integrating with the Nominal API.

Plugin Architecture

This is a production-ready Grafana data source plugin with:

  • Go backend: Secure API key handling and server-side processing
  • TypeScript frontend: Modern React-based query editor and configuration UI
  • Full-stack integration: Backend handles API calls, frontend provides intuitive interface
  • Enterprise features: Authentication, caching, and optimized performance

Note: Alternative implementations (pure TypeScript, panel plugin) are available in the archive/ directory for reference and development purposes.

For SQL setup, examples and local testing, see SQL queries.

Quick Start

Development and E2E testing

(required for Playwright tests)

pnpm install

# Build and start development environment:
mage -v                    # Build Go backend
pnpm run build            # Build TypeScript frontend
pnpm run server           # Start Docker development environment (with pre-configured datasource)

# Run E2E tests (requires development environment):
pnpm run e2e              # Run Playwright tests

Note: Use pnpm run server (development Docker) for testing - it includes pre-configured datasources that Playwright tests expect. The production build starts with unconfigured datasources and will cause test failures.

Adding frontend resource routes

Use src/resourceRoutes.json for frontend resource paths and register their POST handlers in pkg/plugin/resource_handler.go. The Go tests check every manifest entry against the router without live credentials. Add handler tests for request and response behavior.

Backend integration tests

The Go backend tests run without live Nominal credentials by default:

go test ./pkg/...

Live Nominal API checks are opt-in so normal CI and local unit tests do not depend on external data or credentials.

Run the live health-check integration test with:

NOMINAL_LIVE_TESTS=1 \
NOMINAL_API_KEY=... \
go test -count=1 ./pkg/plugin -run TestLiveNominal

NOMINAL_BASE_URL is optional and defaults to https://api.gov.nominal.io/api. For the shared local plugin .env credentials, use the staging API URL:

set -a
. ./.env
set +a

NOMINAL_LIVE_TESTS=1 \
NOMINAL_BASE_URL=https://api-staging.gov.nominal.io/api \
go test -count=1 ./pkg/plugin -run TestLiveNominalCheckHealthIntegration -v

To also run the live QueryData and SQL integration paths, point the test at staging. The test creates a temporary asset, data scope, dataset, and numeric CSV channel, queries that channel through the plugin, and archives the temporary asset and dataset during cleanup.

set -a
. ./.env
set +a

NOMINAL_LIVE_TESTS=1 \
NOMINAL_BASE_URL=https://api-staging.gov.nominal.io/api \
go test -count=1 ./pkg/plugin -run 'TestLiveNominal(QueryData|SQLQuery)Integration' -v

Optional query controls:

  • NOMINAL_WORKSPACE_RID: workspace for queries and temporary test resources. Required when the key has no default workspace.

  • NOMINAL_QUERY_BUCKETS: bucket count, default 100.

  • NOMINAL_QUERY_ASSET_RID, NOMINAL_QUERY_DATA_SCOPE_NAME, and NOMINAL_QUERY_CHANNEL: use an existing query target instead of creating temporary test data. Set all three together.

  • NOMINAL_QUERY_FROM and NOMINAL_QUERY_TO: RFC3339 timestamps, default to the temporary CSV range for self-provisioned data or the last 15 minutes for an existing query target.

  • NOMINAL_ALLOW_DEFAULT_LIVE_WRITES=1: allows the self-provisioning query test to create temporary resources against the default production base URL. Without this, set NOMINAL_BASE_URL explicitly for writeful live tests.

Verify Plugin Installation

After starting the container, verify the plugin is installed:

# Check if plugin directory exists
docker exec <container-name> ls -la /var/lib/grafana/plugins/nominal-nominalds-datasource/

# Should show:
# - module.js (frontend)
# - gpx_nominal_ds_linux_amd64 (backend binary)
# - plugin.json (metadata)
  • Build Output: The build creates a production-ready Grafana image containing:

    • Built TypeScript frontend
    • Built Go backend
    • Nominal datasource plugin
  • Access deployed instance at http://localhost:3000 (credentials from your .env file):

    1. Login with your configured credentials
    2. Go to Configuration > Data sources
    3. Click Add data source
    4. Look for Nominal in the list

Reviewer bootstrap

For plugin catalog review setup, see:

Provisioned resources included in this repo:

  • Datasource provisioning: provisioning/datasources/datasources.yml
  • Dashboard provisioning provider: provisioning/dashboards/dashboards.yml
  • Reviewer dashboard JSON: provisioning/dashboards/json/nominal-review-dashboard.json

Note: Internal deployment tooling (Helm/ECR/production Docker build) is maintained in a private audit repository and intentionally omitted here.

Installing Plugin on Existing Grafana Instances

If you already have Grafana running (cloud, self-hosted, or on-prem) and just need to install the plugin, download the pre-built plugin ZIP from GitHub Releases. Releases are cut automatically via release-please when conventional commits land on main.

Quick Install

# Download the latest release (replace VERSION with the GitHub release version)
VERSION="0.11.0"
curl -L "https://github.com/nominal-io/grafana-plugin-public/releases/download/${VERSION}/nominal-nominalds-datasource-${VERSION}.zip" \
  -o plugin.zip

# Extract to Grafana plugins directory
unzip plugin.zip -d /var/lib/grafana/plugins/

# Restart Grafana to load the plugin
sudo systemctl restart grafana-server
# or for Docker: docker restart <grafana-container>

Configuration

Release artifacts created after Grafana grants public signing include a Grafana plugin signature. Install those ZIP files normally, without configuring Grafana to allow unsigned plugins.

Older unsigned ZIP files still require Grafana to explicitly allow the plugin:

# In grafana.ini or via environment variable
[plugins]
allow_loading_unsigned_plugins = nominal-nominalds-datasource

# Or as environment variable:
# GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=nominal-nominalds-datasource

Public release signing

Public signing follows Grafana's signing and review flow. Grafana must approve the plugin for public signing before a signed release can be cut successfully.

Configure this GitHub secret before cutting a release tag:

  • GRAFANA_PLUGIN_ACCESS_KEY: the Grafana access policy token with plugins:write scope.

For a local signing check, build the plugin first and then run:

GRAFANA_ACCESS_POLICY_TOKEN="$GRAFANA_PLUGIN_ACCESS_KEY" \
  pnpm run sign

The local command maps GRAFANA_PLUGIN_ACCESS_KEY to GRAFANA_ACCESS_POLICY_TOKEN because that is the environment variable name Grafana's signing tool reads.

Do not commit tokens or generated signing output from dist/.

Signing without rootUrls is the public plugin path. Grafana approval is tied to the plugin ID, so if the plugin ID changes, the signing command may fail until the review submission is updated and Grafana approves that ID.

The release workflow also creates a GitHub provenance attestation for the signed plugin ZIP before running Grafana's release validator.

Grafana review submission

Grafana does not require the first public review submission to be signed. For the initial review, package the plugin ZIP, run the validator, and submit the plugin through Grafana's plugin publishing flow with:

  • Plugin ZIP URL
  • ZIP SHA1 hash
  • Source code URL
  • Testing guidance
  • Provisioning details from REVIEW.md

After Grafana approves the plugin and grants its public signature level, cut a release tag so the release workflow can sign and publish the catalog-ready ZIP.

Verify Installation

# Check plugin files exist
ls -la /var/lib/grafana/plugins/nominal-nominalds-datasource/

# Expected files:
# - module.js (frontend)
# - gpx_nominal_ds_linux_amd64 (backend binary for Linux)
# - gpx_nominal_ds_darwin_amd64 (backend binary for macOS Intel)
# - gpx_nominal_ds_darwin_arm64 (backend binary for macOS Apple Silicon)
# - plugin.json (metadata)

Then in Grafana UI:

  1. Go to Configuration > Data sources
  2. Click Add data source
  3. Search for Nominal
  4. Configure with your Nominal API key, base URL, and (recommended) workspace RID

API Testing

With Backend Plugin (Go + TypeScript): The backend plugin uses /resources/ endpoints that route through the Go backend.

  • Health Check

    curl "http://localhost:3000/api/datasources/uid/{UID}/health"
    # {"message":"Successfully connected to Nominal API","status":"OK"}
  • Asset Search

    The backend exposes a fixed set of resource endpoints: channels, assets, datascopes, channelvariables, search-assets, and assets-by-rid. Any other path returns 404. To call other Nominal APIs, use the API key directly against the Nominal base URL.

    curl -s -X POST "http://localhost:3000/api/datasources/uid/{UID}/resources/search-assets" \
      -H 'Content-Type: application/json' \
      -d '{"query":{"type":"searchText","searchText":""},"sort":{"field":"CREATED_AT","isDescending":true},"pageSize":10}' | jq
  • Find Your Datasource

    # List all datasources
    curl -s "http://localhost:3000/api/datasources" | jq '.[] | {id, uid, name, type, url}'
    
    # Get specific datasource by ID
    curl -s "http://localhost:3000/api/datasources/{ID}" | jq '{id, uid, name, url, jsonData, secureJsonFields}'
    {
      "id": 1,
      "uid": "P1E5984762EB73E39",
      "name": "nominal",
      "url": "",
      "jsonData": {
        "baseUrl": "https://api.gov.nominal.io/api"
      },
      "secureJsonFields": {
        "apiKey": true
      }
    }

Docs and other references

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages