├── examples ├── php │ ├── .gitignore │ ├── linux-node-overview │ │ ├── index.php │ │ └── composer.json │ ├── grafana-agent-overview │ │ ├── index.php │ │ └── composer.json │ ├── custom-panel │ │ ├── composer.json │ │ ├── src │ │ │ └── Custom │ │ │ │ ├── Options.php │ │ │ │ └── PanelBuilder.php │ │ └── index.php │ ├── custom-query │ │ ├── composer.json │ │ ├── index.php │ │ └── src │ │ │ └── Custom │ │ │ ├── Query.php │ │ │ └── QueryBuilder.php │ ├── red-method │ │ ├── composer.json │ │ └── index.php │ └── README.md ├── go │ ├── .gitignore │ ├── alert-rule │ │ ├── go.mod │ │ └── go.sum │ ├── red-method │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── common.go │ ├── custom-panel │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── custom-query │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── linux-node-overview │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── builder │ │ │ └── host.go │ ├── grafana-agent-overview │ │ ├── go.mod │ │ ├── go.sum │ │ ├── discovery.go │ │ ├── overview.go │ │ └── common.go │ └── grafana-openapi-client-go │ │ └── go.mod ├── pulumi │ ├── python │ │ ├── .gitignore │ │ ├── Pulumi.dev.yaml │ │ ├── requirements.txt │ │ ├── Pulumi.yaml │ │ └── __main__.py │ ├── typescript │ │ ├── .gitignore │ │ ├── Pulumi.dev.yaml │ │ ├── Pulumi.yaml │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── index.ts │ ├── go │ │ ├── Pulumi.dev.yaml │ │ ├── Pulumi.yaml │ │ └── main.go │ └── README.md ├── python │ ├── red-method │ │ ├── src │ │ │ ├── __init__.py │ │ │ └── common.py │ │ ├── requirements.txt │ │ └── main.py │ ├── custom-panel │ │ ├── src │ │ │ ├── __init__.py │ │ │ └── custompanel.py │ │ ├── requirements.txt │ │ └── main.py │ ├── custom-query │ │ ├── src │ │ │ └── __init__.py │ │ ├── requirements.txt │ │ └── main.py │ ├── linux-node-overview │ │ ├── src │ │ │ └── __init__.py │ │ └── requirements.txt │ ├── .gitignore │ ├── grafana-agent-overview │ │ ├── src │ │ │ ├── __init__.py │ │ │ ├── discovery.py │ │ │ └── common.py │ │ └── requirements.txt │ └── alert-rule │ │ └── requirements.txt ├── typescript │ ├── .gitignore │ ├── red-method │ │ ├── src │ │ │ ├── index.ts │ │ │ └── common.ts │ │ ├── package.json │ │ └── tsconfig.json │ ├── custom-panel │ │ ├── src │ │ │ ├── index.ts │ │ │ └── customPanel.ts │ │ ├── package.json │ │ └── tsconfig.json │ ├── custom-query │ │ ├── src │ │ │ ├── index.ts │ │ │ └── customQuery.ts │ │ ├── package.json │ │ └── tsconfig.json │ ├── alert-rule │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── src │ │ │ └── index.ts │ ├── linux-node-overview │ │ ├── package.json │ │ └── tsconfig.json │ └── grafana-agent-overview │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── src │ │ ├── discovery.ts │ │ ├── overview.ts │ │ └── common.ts ├── terraform │ ├── .gitignore │ ├── main.tf │ ├── README.md │ └── dashboards.tf └── java │ ├── alert-rule │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ └── build.gradle │ ├── red-method │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── red │ │ │ ├── Main.java │ │ │ └── Common.java │ └── build.gradle │ ├── custom-panel │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── custompanel │ │ │ ├── CustomPanelOptions.java │ │ │ ├── Main.java │ │ │ └── CustomPanelBuilder.java │ └── build.gradle │ ├── custom-query │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── customquery │ │ │ ├── CustomQuery.java │ │ │ ├── CustomQueryBuilder.java │ │ │ └── Main.java │ └── build.gradle │ ├── linux-node-overview │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── linuxnode │ │ │ ├── Main.java │ │ │ └── linux │ │ │ └── Network.java │ └── build.gradle │ ├── grafana-agent-overview │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── agent │ │ └── Discovery.java │ └── README.md ├── gradle.properties ├── .github ├── CODEOWNERS ├── zizmor.yaml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 3-general-issue.yaml │ ├── 2-feature-request.yaml │ ├── 0-bug-report.yaml │ └── 1-missing-fields.yaml └── actions │ └── setup-cog │ └── action.yaml ├── .gitignore ├── .cog ├── templates │ ├── java │ │ ├── extra │ │ │ ├── settings.gradle │ │ │ ├── gradle.properties │ │ │ ├── src │ │ │ │ └── main │ │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── grafana │ │ │ │ │ └── foundation │ │ │ │ │ └── cog │ │ │ │ │ └── variants │ │ │ │ │ ├── Dataquery.java │ │ │ │ │ ├── UnknownDataquery.java │ │ │ │ │ ├── UnknownDataquerySerializer.java │ │ │ │ │ ├── DataqueryConfig.java │ │ │ │ │ └── PanelConfig.java │ │ │ └── docs │ │ │ │ └── Installing.md │ │ └── overrides │ │ │ ├── api_reference_object_dashboard_Dashboard_extra.tmpl │ │ │ ├── assigment_Dashboard_WithRow.tmpl │ │ │ └── assigment_Dashboard_WithPanel.tmpl │ ├── go │ │ ├── extra │ │ │ ├── go.mod │ │ │ ├── docs │ │ │ │ ├── Installing.md │ │ │ │ └── How-To │ │ │ │ │ ├── converting-a-dashboard.md │ │ │ │ │ └── building-a-dashboard.md │ │ │ └── cog │ │ │ │ └── plugins │ │ │ │ └── variants.go │ │ └── overrides │ │ │ ├── object_variant_dataquery.tmpl │ │ │ ├── object_dashboardv2beta1_DataQueryKind_field_spec_custom_strict_unmarshal.tmpl │ │ │ ├── object_dashboard_Panel_field_options_custom_strict_unmarshal.tmpl │ │ │ ├── object_common_TableFooterOptions_field_fields_custom_strict_unmarshal.tmpl │ │ │ ├── object_dashboard_DataSourceRef_custom_unmarshal.tmpl │ │ │ ├── object_dashboard_Panel_field_targets_custom_strict_unmarshal.tmpl │ │ │ ├── assignment_Dashboard_withPanel.tmpl │ │ │ ├── variant_dataquery_field_unmarshal.tmpl │ │ │ ├── object_dashboard_Panel_field_fieldConfig_custom_strict_unmarshal.tmpl │ │ │ ├── object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl │ │ │ ├── api_reference_object_dashboard_Dashboard_extra.tmpl │ │ │ ├── assignment_Dashboard_withRow.tmpl │ │ │ ├── api_reference_builder_dashboard_Dashboard_extra.tmpl │ │ │ └── object_common_TableFooterOptions_custom_unmarshal.tmpl │ ├── python │ │ ├── extra │ │ │ ├── grafana_foundation_sdk │ │ │ │ └── cog │ │ │ │ │ ├── variants.py │ │ │ │ │ └── plugins.py │ │ │ ├── docs │ │ │ │ ├── Installing.md │ │ │ │ └── How-To │ │ │ │ │ └── building-a-dashboard.md │ │ │ └── pyproject.toml │ │ └── overrides │ │ │ ├── assignment_Dashboard_withPanel.tmpl │ │ │ ├── variant_dataquery_field_unmarshal.tmpl │ │ │ ├── object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl.tmpl │ │ │ ├── schema_variant_panelcfg.tmpl │ │ │ ├── api_reference_object_dashboard_Dashboard_extra.tmpl │ │ │ ├── schema_variant_dataquery.tmpl │ │ │ ├── api_reference_builder_dashboard_Dashboard_extra.tmpl │ │ │ └── assignment_Dashboard_withRow.tmpl │ ├── php │ │ ├── extra │ │ │ ├── docs │ │ │ │ ├── Installing.md │ │ │ │ └── How-To │ │ │ │ │ ├── converting-a-dashboard.md │ │ │ │ │ └── building-a-dashboard.md │ │ │ └── src │ │ │ │ └── Cog │ │ │ │ ├── Dataquery.php │ │ │ │ ├── UnknownDataqueryBuilder.php │ │ │ │ ├── PanelcfgConfig.php │ │ │ │ ├── DataqueryConfig.php │ │ │ │ └── UnknownDataquery.php │ │ └── overrides │ │ │ ├── object_variant_dataquery.tmpl │ │ │ ├── api_reference_object_dashboard_Dashboard_extra.tmpl │ │ │ ├── assignment_Dashboard_withPanel.tmpl │ │ │ ├── variant_dataquery_field_unmarshal.tmpl │ │ │ ├── object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl.tmpl │ │ │ ├── api_reference_builder_dashboard_Dashboard_extra.tmpl │ │ │ └── api_reference_package_dashboard_extra.tmpl │ ├── typescript │ │ ├── extra │ │ │ ├── docs │ │ │ │ ├── Installing.md │ │ │ │ └── How-To │ │ │ │ │ ├── building-a-dashboard.md │ │ │ │ │ └── custom-panel-type.md │ │ │ ├── babel.config.json │ │ │ └── tsconfig.json │ │ └── overrides │ │ │ ├── assignment_Dashboard_withPanel.tmpl │ │ │ ├── api_reference_builder_dashboard_Dashboard_extra.tmpl │ │ │ └── assignment_Dashboard_withRow.tmpl │ └── README.md ├── veneers │ ├── expr.common.yaml │ ├── datasource.common.yaml │ ├── elasticsearch.common.yaml │ ├── alerting.go.yaml │ ├── alerting.typescript.yaml │ ├── accesspolicy.common.yaml │ ├── team.common.yaml │ ├── folder.common.yaml │ ├── dashboardv2 │ │ ├── datasource.python.yaml │ │ ├── datasource.ts.yaml │ │ ├── dashboardv2beta1.go.yaml │ │ └── dashboardv2beta1.java.yaml │ ├── tempo.go.yaml │ ├── cloudwatch.go.yaml │ ├── dashboard.go.yaml │ ├── dashboard.php.yaml │ ├── common.common.yaml │ ├── alerting.common.yaml │ ├── dashboard.python.yaml │ └── heatmap.common.yaml ├── schemas │ ├── composable │ │ ├── datasource │ │ │ └── dataquery.cue │ │ └── athena │ │ │ └── dataquery.cue │ └── resource │ │ └── manifest.cue ├── compiler │ ├── team.yaml │ ├── common_passes.yaml │ ├── athena.yaml │ ├── prometheus.yaml │ ├── dataqueries_refid.yaml │ ├── dashboardv2beta1.yaml │ └── testdata_passes.yaml ├── converters │ └── config.yaml └── repository_templates │ ├── php │ ├── composer.json │ └── .config │ │ └── ci │ │ └── php │ │ └── phpstan.neon │ ├── python │ └── .github │ │ └── workflows │ │ └── python-release.tmpl │ ├── typescript │ └── .github │ │ └── workflows │ │ └── typescript-release.tmpl │ └── common │ └── README.md ├── scripts ├── versions.sh ├── validate │ ├── java.sh │ ├── python.sh │ ├── typescript.sh │ ├── go.sh │ └── php.sh ├── fetch-kind-registry.sh ├── release-validate.sh ├── release-all.sh ├── libs │ └── git.sh └── docs │ └── _pull_versions.sh ├── .mkdocs ├── mkdocs-version.yml └── overrides │ └── main.html ├── .editorconfig ├── composer.json ├── devbox.json ├── catalog-info.yaml ├── requirements.txt └── Makefile /examples/php/.gitignore: -------------------------------------------------------------------------------- 1 | */vendor -------------------------------------------------------------------------------- /examples/go/.gitignore: -------------------------------------------------------------------------------- 1 | */vendor 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.java.home= 2 | -------------------------------------------------------------------------------- /examples/pulumi/python/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | venv/ 3 | -------------------------------------------------------------------------------- /examples/python/red-method/src/__init__.py: -------------------------------------------------------------------------------- 1 | # src 2 | -------------------------------------------------------------------------------- /examples/python/custom-panel/src/__init__.py: -------------------------------------------------------------------------------- 1 | # src 2 | -------------------------------------------------------------------------------- /examples/python/custom-query/src/__init__.py: -------------------------------------------------------------------------------- 1 | # src 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @grafana/grafana-app-platform-squad 2 | -------------------------------------------------------------------------------- /examples/python/linux-node-overview/src/__init__.py: -------------------------------------------------------------------------------- 1 | # src 2 | -------------------------------------------------------------------------------- /examples/typescript/.gitignore: -------------------------------------------------------------------------------- 1 | */build 2 | */node_modules 3 | -------------------------------------------------------------------------------- /examples/pulumi/typescript/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /examples/python/.gitignore: -------------------------------------------------------------------------------- 1 | */.venv 2 | __pycache__ 3 | *.pyc 4 | -------------------------------------------------------------------------------- /examples/python/grafana-agent-overview/src/__init__.py: -------------------------------------------------------------------------------- 1 | # src 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .gradle 3 | /examples/java/*/build/ 4 | 5 | /kind-registry 6 | -------------------------------------------------------------------------------- /.cog/templates/java/extra/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'grafana-foundation-sdk' 2 | -------------------------------------------------------------------------------- /examples/python/alert-rule/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /examples/python/custom-panel/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /examples/python/custom-query/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /examples/python/red-method/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /examples/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | .terraform.lock.hcl 3 | terraform.tfstate* 4 | -------------------------------------------------------------------------------- /examples/python/linux-node-overview/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /examples/python/grafana-agent-overview/requirements.txt: -------------------------------------------------------------------------------- 1 | grafana-foundation-sdk==1746136285!11.6.0 2 | -------------------------------------------------------------------------------- /.github/zizmor.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | template-injection: 3 | ignore: 4 | - ".cog/repository_templates/**/*.yaml" 5 | -------------------------------------------------------------------------------- /examples/pulumi/go/Pulumi.dev.yaml: -------------------------------------------------------------------------------- 1 | encryptionsalt: v1:BEftHDn41bo=:v1:/KUE9m/zZaCucHYs:hUJgPPzk6cspkZqzbSeMxjSLc4PolA== 2 | -------------------------------------------------------------------------------- /examples/pulumi/python/Pulumi.dev.yaml: -------------------------------------------------------------------------------- 1 | encryptionsalt: v1:JUSLvitr2WM=:v1:/JLqOYWEeQHx7gqH:nM9RkBYiHXQKdMP+62QkLZycWyD9pg== 2 | -------------------------------------------------------------------------------- /.cog/templates/go/extra/go.mod: -------------------------------------------------------------------------------- 1 | {{- if not .Data.Debug -}} 2 | module {{ .Data.PackageRoot }} 3 | 4 | go 1.21 5 | 6 | {{- end -}} -------------------------------------------------------------------------------- /examples/pulumi/typescript/Pulumi.dev.yaml: -------------------------------------------------------------------------------- 1 | encryptionsalt: v1:ndbNyyxE2kA=:v1:R0etjMYFwiaC7/me:ouuaAW0UsZ6GpU68p6NvHWdBsCQOzQ== 2 | -------------------------------------------------------------------------------- /.cog/templates/python/extra/grafana_foundation_sdk/cog/variants.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | 3 | 4 | class Dataquery(ABC): 5 | ... 6 | -------------------------------------------------------------------------------- /examples/pulumi/python/requirements.txt: -------------------------------------------------------------------------------- 1 | pulumi>=3.0.0,<4.0.0 2 | pulumiverse_grafana==0.5.0 3 | grafana_foundation_sdk==1713437340!10.4.0 4 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/docs/Installing.md: -------------------------------------------------------------------------------- 1 | # Installing 2 | 3 | ```shell 4 | composer require "grafana/foundation-sdk:dev-{{ .Extra.ReleaseBranch }}" 5 | ``` 6 | -------------------------------------------------------------------------------- /.cog/templates/go/extra/docs/Installing.md: -------------------------------------------------------------------------------- 1 | # Installing 2 | 3 | ```shell 4 | go get github.com/grafana/grafana-foundation-sdk/go@{{ .Extra.ReleaseBranch }} 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/java/alert-rule/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/HEAD/examples/java/alert-rule/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/java/red-method/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/HEAD/examples/java/red-method/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/java/custom-panel/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/HEAD/examples/java/custom-panel/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/java/custom-query/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/HEAD/examples/java/custom-query/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.cog/templates/java/extra/gradle.properties: -------------------------------------------------------------------------------- 1 | grafanaFoundationSDKVersion={{ if .Data.Debug }}0.0{{ else }}{{ .Extra.GrafanaVersion|registryToSemver }}-{{ .Extra.BuildTimestamp }}{{ end }} 2 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/src/Cog/Dataquery.php: -------------------------------------------------------------------------------- 1 | {{ page.meta.title | striptags }} - {{ config.site_name }} 6 | {% elif page.title and not page.is_homepage %} 7 | {{ page.title | striptags }} - {{ config.site_name }} 8 | {% else %} 9 | {{ config.site_name }} 10 | {% endif %} 11 | {% endblock %} -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboardv2beta1_DataQueryKind_field_spec_custom_strict_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboardv2beta1_DataQueryKind_field_spec_custom_strict_unmarshal" -}} 2 | {{- $cog := importPkg "cog" -}} 3 | dataquery, err := cog.UnmarshalDataquery(fields["spec"], resource.Group) 4 | if err != nil { 5 | errs = append(errs, cog.MakeBuildErrors("spec", err)...) 6 | } else { 7 | resource.Spec = dataquery 8 | } 9 | {{- end }} 10 | -------------------------------------------------------------------------------- /.cog/veneers/cloudwatch.go.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: go 4 | 5 | package: cloudwatch 6 | 7 | builders: ~ 8 | 9 | options: 10 | - struct_fields_as_arguments: 11 | by_name: QueryEditorOperator.value 12 | fields: [ ArrayOfQueryEditorOperatorType ] 13 | - rename_arguments: 14 | by_name: QueryEditorOperator.value 15 | as: [operatorTypes] 16 | -------------------------------------------------------------------------------- /.cog/converters/config.yaml: -------------------------------------------------------------------------------- 1 | runtime: 2 | - package: 'dashboard' 3 | name: 'panel' 4 | name_func: 'ConvertPanelToCode' 5 | discriminator_field: 'type' 6 | 7 | - package: 'dashboardv2beta1' 8 | name: 'vizconfigkind' 9 | name_func: 'ConvertPanelToCode' 10 | discriminator_field: 'group' 11 | 12 | - package: 'dashboardv2beta1' 13 | name: 'dataquerykind' 14 | name_func: 'ConvertDataQueryKindToCode' 15 | discriminator_field: 'group' 16 | -------------------------------------------------------------------------------- /examples/typescript/custom-panel/src/index.ts: -------------------------------------------------------------------------------- 1 | import { DashboardBuilder } from '@grafana/grafana-foundation-sdk/dashboard'; 2 | import { CustomPanelBuilder } from "./customPanel"; 3 | 4 | const builder = new DashboardBuilder('[Example] Custom panel') 5 | .uid('example-custom-panel') 6 | .withPanel( 7 | new CustomPanelBuilder() 8 | .title('Sample custom panel') 9 | .makeBeautiful() 10 | ); 11 | 12 | console.log(JSON.stringify(builder.build(), null, 2)); 13 | -------------------------------------------------------------------------------- /.cog/compiler/athena.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/compiler_passes.json 2 | 3 | passes: 4 | ########## 5 | # Athena # 6 | ########## 7 | 8 | - schema_set_entry_point: 9 | package: athena 10 | entry_point: Dataquery 11 | 12 | - retype_field: 13 | field: athena.Dataquery.datasource 14 | as: 15 | kind: ref 16 | ref: { referred_pkg: common, referred_type: DataSourceRef } 17 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/docs/How-To/converting-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Converting a dashboard 2 | 3 | ```php 4 | 10 | com.grafana 11 | grafana-foundation-sdk 12 | {{ .Extra.GrafanaVersion|registryToSemver }}-{{ .Extra.BuildTimestamp }} 13 | 14 | ``` 15 | -------------------------------------------------------------------------------- /devbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/main/.schema/devbox.schema.json", 3 | "packages": [ 4 | "python312@3.12", 5 | "go@1.23", 6 | "php83Packages.composer@2.7", 7 | "php83Packages.phpstan@2.1.1", 8 | "yarn@1.22", 9 | "gradle@8.10" 10 | ], 11 | "shell": { 12 | "init_hook": [ 13 | "echo 'Entering Python venv' && . $VENV_DIR/bin/activate", 14 | "echo 'Installing Python dependencies' && pip install -qq -r requirements.txt" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/pulumi/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains examples of the Foundation SDK being used in combination with [Pulumi](https://www.pulumi.com/). These tools work well together because they both allow users to work in the programming language of their choice. The Foundation SDK allows users to build Grafana objects in various languages, and Pulumi allows users to invoke Terraform in various languages. 4 | 5 | ## Running the examples 6 | 7 | From an example's folder: 8 | 9 | ```console 10 | $ pulumi install 11 | $ pulumi up 12 | ``` 13 | -------------------------------------------------------------------------------- /examples/pulumi/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "outDir": "bin", 5 | "target": "es2020", 6 | "module": "NodeNext", 7 | "moduleResolution": "NodeNext", 8 | "sourceMap": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "forceConsistentCasingInFileNames": true 14 | }, 15 | "files": [ 16 | "index.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.cog/templates/java/extra/src/main/java/com/grafana/foundation/cog/variants/UnknownDataquery.java: -------------------------------------------------------------------------------- 1 | package com.grafana.foundation.cog.variants; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | @JsonSerialize(using = UnknownDataquerySerializer.class) 9 | public class UnknownDataquery implements Dataquery { 10 | public final Map genericFields = new HashMap<>(); 11 | 12 | public String dataqueryName() { 13 | return "unknown"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/java/custom-query/src/main/java/customquery/CustomQuery.java: -------------------------------------------------------------------------------- 1 | package customquery; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.grafana.foundation.cog.variants.Dataquery; 5 | 6 | public class CustomQuery implements Dataquery { 7 | @JsonProperty("refId") 8 | public String refId; 9 | @JsonProperty("hide") 10 | public Boolean hide; 11 | @JsonProperty("query") 12 | public String query; 13 | 14 | @Override 15 | public String dataqueryName() { 16 | return "custom-query"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/pulumi/typescript/index.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as grafana from "@pulumiverse/grafana"; 3 | import { builder } from '../../typescript/linux-node-overview/src/index'; 4 | 5 | const provider = new grafana.Provider("grafana", { url: "http://localhost:3000", auth: "admin:admin" }); 6 | 7 | const dashboard = builder.build(); 8 | 9 | const pulumiDashboard = new grafana.Dashboard(dashboard.uid || 'uid', { configJson: JSON.stringify(dashboard, null, 2) }, { provider: provider }); 10 | 11 | export const dashboardUid = pulumiDashboard.uid; 12 | -------------------------------------------------------------------------------- /scripts/validate/go.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error. Append "|| true" if you expect an error. 4 | set -o errexit 5 | # Exit on error inside any functions or subshells. 6 | set -o errtrace 7 | # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR 8 | set -o nounset 9 | # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump | gzip` 10 | set -o pipefail 11 | 12 | release_path=${1:-"./"} 13 | 14 | cd "${release_path}/go" 15 | 16 | for d in */ ; do 17 | [[ "$d" != "docs/" ]] && (echo "Building $d"; go build "./$d") 18 | done -------------------------------------------------------------------------------- /.cog/templates/README.md: -------------------------------------------------------------------------------- 1 | # Template blocks 2 | 3 | The templates contained in this folder are used to customize the types, builders and docs generated by cog. 4 | For example, to automatically position panels on the grid whenever they're added to a dashboard. 5 | 6 | To achieve that, cog looks for [template blocks](https://pkg.go.dev/text/template#example-Template-Block) following 7 | a particular naming convention, and renders them. 8 | 9 | A [list of every supported template block](https://grafana.github.io/cog/reference/template_blocks/) is available in cog's documentation. 10 | -------------------------------------------------------------------------------- /.cog/templates/typescript/extra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@grafana/tsconfig", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "emitDeclarationOnly": false, 6 | "lib": ["es2015", "es2016", "es2017", "es2018", "es2019", "es2020"], 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "target": "es2020", 10 | "declarationDir": "dist", 11 | "importHelpers": true, 12 | "outDir": "dist", 13 | "rootDirs": ["."], 14 | "useUnknownInCatchVariables": false 15 | }, 16 | "exclude": ["dist", "node_modules"], 17 | "include": ["**/*.ts"] 18 | } -------------------------------------------------------------------------------- /examples/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains examples of the Foundation SDK being used in combination with [Terraform](https://www.terraform.io/) and the [Grafana Provider](https://registry.terraform.io/providers/grafana/grafana/latest/docs). 4 | 5 | While the Foundation SDK does not integrate directly with TF/the HCL language, it can be used in Terraform "external datasources". Building dashboards is a complex problem that is hard to model/reason with in the HCL language. 6 | 7 | ## Running this example 8 | 9 | ```console 10 | $ terraform init 11 | $ terraform apply 12 | ``` 13 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/src/Cog/UnknownDataqueryBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | final class UnknownDataqueryBuilder implements Builder 9 | { 10 | protected UnknownDataquery $internal; 11 | 12 | public function __construct(?UnknownDataquery $object = null) 13 | { 14 | $this->internal = $object ?: new UnknownDataquery([]); 15 | } 16 | 17 | /** 18 | * @return UnknownDataquery 19 | */ 20 | public function build() 21 | { 22 | return $this->internal; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/typescript/custom-query/src/index.ts: -------------------------------------------------------------------------------- 1 | import { DashboardBuilder } from '@grafana/grafana-foundation-sdk/dashboard'; 2 | import { PanelBuilder as TimeSeriesBuilder } from "@grafana/grafana-foundation-sdk/timeseries"; 3 | import { CustomQueryBuilder } from "./customQuery"; 4 | 5 | const builder = new DashboardBuilder('[Example] Custom query') 6 | .uid('example-custom-query') 7 | .withPanel( 8 | new TimeSeriesBuilder() 9 | .title('Sample panel') 10 | .withTarget( 11 | new CustomQueryBuilder("query here") 12 | ) 13 | ); 14 | 15 | console.log(JSON.stringify(builder.build(), null, 2)); 16 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: grafana-foundation-sdk 5 | title: Grafana Foundation SDK 6 | annotations: 7 | github.com/project-slug: grafana/grafana-foundation-sdk 8 | tags: 9 | - gitops 10 | links: 11 | - title: "Internal Slack Channel #app-platform" 12 | url: https://grafanalabs.enterprise.slack.com/archives/C04RVCAG9B5 13 | description: | 14 | Set of tools, types and libraries for building and manipulating Grafana objects. 15 | spec: 16 | type: library 17 | owner: group:default/grafana-app-platform-squad 18 | lifecycle: experimental 19 | -------------------------------------------------------------------------------- /.cog/repository_templates/php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grafana/foundation-sdk", 3 | "type": "library", 4 | "license": "Apache-2.0", 5 | "homepage": "https://github.com/grafana/grafana-foundation-sdk", 6 | "description": "A set of tools, types and libraries for building and manipulating Grafana objects.", 7 | "keywords": [ 8 | "observability", 9 | "sdk", 10 | "grafana", 11 | "logs", 12 | "traces", 13 | "metrics" 14 | ], 15 | "autoload": { 16 | "psr-4": { 17 | "Grafana\\Foundation\\": "php/src/" 18 | } 19 | }, 20 | "require": {} 21 | } 22 | -------------------------------------------------------------------------------- /examples/typescript/alert-rule/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alert-rule", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/typescript/red-method/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-query", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.cog/schemas/resource/manifest.cue: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | DashboardV2Beta1: "dashboard.grafana.app/v2beta1" 4 | DashboardV2Alpha1: "dashboard.grafana.app/v2alpha1" 5 | DashboardKind: "Dashboard" 6 | 7 | Manifest: { 8 | apiVersion: string 9 | kind: string 10 | metadata: #Metadata 11 | spec: _ 12 | } 13 | 14 | #Metadata: { 15 | name: string 16 | namespace?: string 17 | labels?: [string]: string 18 | annotations?: [string]: string 19 | uid?: string 20 | resourceVersion?: string 21 | generation?: int64 22 | creationTimestamp?: string 23 | updateTimestamp?: string 24 | deletionTimestamp?: string 25 | } 26 | -------------------------------------------------------------------------------- /examples/java/red-method/src/main/java/red/Main.java: -------------------------------------------------------------------------------- 1 | package red; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.grafana.foundation.dashboard.Dashboard; 5 | 6 | import java.util.List; 7 | 8 | import static red.Red.red; 9 | 10 | public class Main { 11 | public static void main(String[] args) { 12 | Dashboard dashboard = red("RED method", List.of("sample-service", "payments", "front-gateway")).build(); 13 | 14 | try { 15 | System.out.println(dashboard.toJSON()); 16 | } catch (JsonProcessingException e) { 17 | throw new RuntimeException(e); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/typescript/custom-panel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-panel", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/typescript/custom-query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-query", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/typescript/linux-node-overview/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-query", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/typescript/grafana-agent-overview/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-query", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "author": "Grafana Labs", 6 | "private": true, 7 | "scripts": { 8 | "start": "node build/src/index.js", 9 | "dev": "ts-node src/index.ts", 10 | "build": "tsc -p tsconfig.json", 11 | "build:watch": "tsc -w -p tsconfig.json" 12 | }, 13 | "dependencies": { 14 | "@grafana/grafana-foundation-sdk": "~11.6.0-cogv0.0.x.1746136285", 15 | "tslib": "~2.6" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "~20", 19 | "ts-node": "^10.9.2", 20 | "typescript": "~5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.cog/templates/java/overrides/api_reference_object_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_object_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Unmarshalling from JSON 5 | 6 | ```java 7 | public class Main { 8 | public static void main(String[] args) { 9 | ObjectMapper mapper = new ObjectMapper(); 10 | try { 11 | InputStream json = Main.class.getResourceAsStream("/dashboard.json"); 12 | Dashboard dashboard = mapper.readValue(json, Dashboard.class); 13 | System.out.println(dashboard.toJSON()); 14 | } catch (IOException e) { 15 | e.printStackTrace(); 16 | } 17 | } 18 | } 19 | ``` 20 | {{ end }} 21 | -------------------------------------------------------------------------------- /.cog/templates/java/extra/src/main/java/com/grafana/foundation/cog/variants/UnknownDataquerySerializer.java: -------------------------------------------------------------------------------- 1 | package com.grafana.foundation.cog.variants; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | 7 | import java.io.IOException; 8 | 9 | public class UnknownDataquerySerializer extends JsonSerializer { 10 | @Override 11 | public void serialize(UnknownDataquery unknownDataquery, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 12 | jsonGenerator.writeObject(unknownDataquery.genericFields); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /scripts/validate/php.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error. Append "|| true" if you expect an error. 4 | set -o errexit 5 | # Exit on error inside any functions or subshells. 6 | set -o errtrace 7 | # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR 8 | set -o nounset 9 | # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump | gzip` 10 | set -o pipefail 11 | 12 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | source "${__dir}/../libs/logs.sh" 14 | 15 | release_path=${1:-"./"} 16 | 17 | cd "${release_path}" 18 | 19 | composer install 20 | 21 | debug " → running phpstan" 22 | phpstan analyze --memory-limit 512M -c .config/ci/php/phpstan.neon 23 | -------------------------------------------------------------------------------- /.cog/veneers/dashboard.go.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: go 4 | 5 | package: dashboard 6 | 7 | builders: ~ 8 | 9 | options: 10 | ############## 11 | # Dashboards # 12 | ############## 13 | 14 | # Time(from, to) instead of time(struct {From string `json:"from"`, To string `json:"to"`}{From: "lala", To: "lala}) 15 | - struct_fields_as_arguments: 16 | by_name: Dashboard.time 17 | 18 | ############## 19 | # Panels # 20 | ############## 21 | 22 | # WithOverride(matcher, properties) instead of WithOverride(struct{...}) 23 | - struct_fields_as_arguments: 24 | by_name: Panel.withOverride 25 | -------------------------------------------------------------------------------- /.cog/veneers/dashboard.php.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: php 4 | 5 | package: dashboard 6 | 7 | builders: ~ 8 | 9 | options: 10 | ############## 11 | # Dashboards # 12 | ############## 13 | 14 | # Time(from, to) instead of time(struct {From string `json:"from"`, To string `json:"to"`}{From: "lala", To: "lala}) 15 | - struct_fields_as_arguments: 16 | by_name: Dashboard.time 17 | 18 | ############## 19 | # Panels # 20 | ############## 21 | 22 | # WithOverride(matcher, properties) instead of WithOverride(struct{...}) 23 | - struct_fields_as_arguments: 24 | by_name: Panel.withOverride 25 | -------------------------------------------------------------------------------- /examples/typescript/custom-panel/src/customPanel.ts: -------------------------------------------------------------------------------- 1 | import * as dashboard from '@grafana/grafana-foundation-sdk/dashboard'; 2 | 3 | export interface CustomPanelOptions { 4 | makeBeautiful?: boolean; 5 | } 6 | 7 | export const defaultCustomPanelOptions = (): CustomPanelOptions => ({}); 8 | 9 | export class CustomPanelBuilder extends dashboard.PanelBuilder { 10 | constructor() { 11 | super(); 12 | 13 | this.internal.type = "custom-panel"; // panel plugin ID 14 | } 15 | 16 | makeBeautiful(): this { 17 | if (!this.internal.options) { 18 | this.internal.options = defaultCustomPanelOptions(); 19 | } 20 | this.internal.options.makeBeautiful = true; 21 | return this; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.cog/repository_templates/php/.config/ci/php/phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 9 # https://phpstan.org/user-guide/rule-levels 3 | paths: 4 | - ../../../php/src 5 | 6 | ignoreErrors: 7 | - identifier: identical.alwaysFalse # not a big deal, duplicated enum values come from the schema 8 | paths: 9 | - ../../../php/src/Common/ScaleDirection.php 10 | - identifier: offsetAccess.nonOffsetAccessible 11 | paths: 12 | - ../../../php/src/Cog/Runtime.php 13 | 14 | # cog being very defensive isn't a bad thing 15 | - identifier: isset.variable 16 | - identifier: instanceof.alwaysTrue 17 | - identifier: function.alreadyNarrowedType 18 | - identifier: arrayFilter.same 19 | -------------------------------------------------------------------------------- /examples/typescript/alert-rule/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/typescript/custom-panel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/typescript/custom-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/typescript/red-method/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/typescript/grafana-agent-overview/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/typescript/linux-node-overview/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "node16", 5 | "lib": ["ES2022"], 6 | "moduleResolution": "node16", 7 | "rootDir": ".", 8 | "outDir": "build", 9 | "allowSyntheticDefaultImports": true, 10 | "importHelpers": true, 11 | "alwaysStrict": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitAny": false, 19 | "noImplicitThis": false, 20 | "strictNullChecks": false 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/java/red-method/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "red.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /examples/java/alert-rule/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "alertrule.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /examples/java/custom-panel/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "custompanel.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /examples/java/custom-query/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "customquery.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-general-issue.yaml: -------------------------------------------------------------------------------- 1 | name: General issue 2 | description: Ask for information, doubts, etc... 3 | title: "[Question]: " 4 | labels: [question, help wanted] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | If you are looking for examples, you should take a look to [this link](https://github.com/grafana/grafana-foundation-sdk/tree/main/examples). 11 | Also each language in each branch has extra information about their setup. 12 | - type: markdown 13 | attributes: 14 | value: | 15 | # 16 | 17 | - type: textarea 18 | attributes: 19 | label: What do you need? 20 | description: Describe your issue. 21 | validations: 22 | required: true 23 | -------------------------------------------------------------------------------- /examples/java/grafana-agent-overview/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "agent.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /examples/java/linux-node-overview/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'application' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | 10 | withJavadocJar() 11 | withSourcesJar() 12 | } 13 | 14 | application { 15 | mainClass = "linuxnode.Main" 16 | } 17 | 18 | allprojects { 19 | version = rootProject.version 20 | apply plugin: "java" 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation("com.grafana:grafana-foundation-sdk:11.6.0-1746136285") 29 | } 30 | 31 | tasks.withType(Javadoc).configureEach { 32 | options.addStringOption('Xdoclint:-missing', '-quiet') 33 | } 34 | -------------------------------------------------------------------------------- /examples/php/custom-panel/src/Custom/Options.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace App\Custom; 4 | 5 | class Options implements \JsonSerializable 6 | { 7 | public bool $makeBeautiful; 8 | 9 | public function __construct(?bool $makeBeautiful = null) 10 | { 11 | $this->makeBeautiful = $makeBeautiful ?: false; 12 | } 13 | 14 | public function jsonSerialize(): array 15 | { 16 | return [ 17 | "makeBeautiful" => $this->makeBeautiful, 18 | ]; 19 | } 20 | 21 | /** 22 | * @param array{makeBeautiful?: bool} $data 23 | */ 24 | public static function fromArray(array $data): self 25 | { 26 | return new self( 27 | makeBeautiful: $data["makeBeautiful"] ?? null, 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /.cog/templates/go/extra/docs/How-To/converting-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Converting a dashboard 2 | 3 | ```go 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "os" 10 | 11 | "github.com/grafana/grafana-foundation-sdk/go/cog/plugins" 12 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 13 | ) 14 | 15 | func main() { 16 | // Required to correctly unmarshal panels and dataqueries 17 | plugins.RegisterDefaultPlugins() 18 | 19 | dashboardJSON, err := os.ReadFile("dashboard.json") 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | dash := dashboard.Dashboard{} 25 | if err = json.Unmarshal(dashboardJSON, &dash); err != nil { 26 | panic(err) 27 | } 28 | 29 | converted := dashboard.DashboardConverter(dash) 30 | fmt.Println(converted) 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /.cog/veneers/common.common.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: all 4 | 5 | package: common 6 | 7 | builders: 8 | ############### 9 | # Table panel # 10 | ############### 11 | 12 | - omit: { by_name: TableAutoCellOptions } 13 | - omit: { by_name: TableSparklineCellOptions } 14 | - omit: { by_name: TableBarGaugeCellOptions } 15 | - omit: { by_name: TableColoredBackgroundCellOptions } 16 | - omit: { by_name: TableColorTextCellOptions } 17 | - omit: { by_name: TableImageCellOptions } 18 | - omit: { by_name: TableDataLinksCellOptions } 19 | - omit: { by_name: TableActionsCellOptions } 20 | - omit: { by_name: TableJsonViewCellOptions } 21 | 22 | - omit: { by_name: DataSourceRef } 23 | -------------------------------------------------------------------------------- /.cog/templates/python/extra/grafana_foundation_sdk/cog/plugins.py: -------------------------------------------------------------------------------- 1 | {{- $panelPackages := .Context.PackagesForVariant "panelcfg" -}} 2 | {{- $dataqueryPackages := .Context.PackagesForVariant "dataquery" -}} 3 | {{- range $pkg := $panelPackages }} 4 | from ..models import {{ $pkg }} 5 | {{- end }} 6 | {{- range $pkg := $dataqueryPackages }} 7 | from ..models import {{ $pkg }} 8 | {{- end }} 9 | from . import runtime as cogruntime 10 | 11 | 12 | def register_default_plugins(): 13 | # Panelcfg variants 14 | {{- range $pkg := $panelPackages }} 15 | cogruntime.register_panelcfg_variant({{ $pkg }}.variant_config()) 16 | {{- end }} 17 | 18 | # Dataquery variants 19 | {{- range $pkg := $dataqueryPackages }} 20 | cogruntime.register_dataquery_variant({{ $pkg }}.variant_config()) 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /.cog/templates/java/overrides/assigment_Dashboard_WithRow.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withRow" }} 2 | 3 | // Position the row on the grid 4 | if (panelOrRowPanel.rowPanel.gridPos == null || (panelOrRowPanel.rowPanel.gridPos.x == 0 && panelOrRowPanel.rowPanel.gridPos.y == 0)) { 5 | GridPos gridPos = new GridPos(); 6 | gridPos.x = 0; // beginning of the line 7 | gridPos.y = this.currentY; 8 | gridPos.h = 1; 9 | gridPos.w = 24; // full width 10 | panelOrRowPanel.rowPanel.gridPos = gridPos; 11 | } 12 | {{- end }} 13 | 14 | {{- define "post_assignment_Dashboard_withRow" }} 15 | 16 | // Reset the state for the next row 17 | this.currentX = 0; 18 | this.currentY += panelOrRowPanel.rowPanel.gridPos.h; 19 | this.lastPanelHeight = 0; 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /.cog/compiler/prometheus.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/compiler_passes.json 2 | 3 | passes: 4 | - unspec: {} 5 | 6 | - retype_field: 7 | field: prometheus.Dataquery.datasource 8 | as: 9 | kind: ref 10 | ref: { referred_pkg: common, referred_type: DataSourceRef } 11 | 12 | # Add a few missing fields. 13 | # This is meant to be removed once the schema is updated. 14 | - add_fields: 15 | to: prometheus.dataquery 16 | fields: 17 | - name: interval 18 | comments: ['An additional lower limit for the step parameter of the Prometheus query and for the', '`$__interval` and `$__rate_interval` variables.'] 19 | type: 20 | kind: scalar 21 | scalar: { scalar_kind: string } 22 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboard_Panel_field_options_custom_strict_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboard_Panel_field_options_custom_strict_unmarshal" -}} 2 | {{- /* Non-standard unmarshalling to parse the correct Options struct based on the panel type */}} 3 | variantCfg, found := cog.ConfigForPanelcfgVariant(resource.Type) 4 | if found && variantCfg.StrictOptionsUnmarshaler != nil { 5 | options, err := variantCfg.StrictOptionsUnmarshaler(fields["options"]) 6 | if err != nil { 7 | errs = append(errs, cog.MakeBuildErrors("options", err)...) 8 | } else { 9 | resource.Options = options 10 | } 11 | } else { 12 | if err := json.Unmarshal(fields["options"], &resource.Options); err != nil { 13 | errs = append(errs, cog.MakeBuildErrors("options", err)...) 14 | } 15 | } 16 | 17 | {{ end }} 18 | -------------------------------------------------------------------------------- /examples/php/custom-panel/src/Custom/PanelBuilder.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace App\Custom; 4 | 5 | use Grafana\Foundation\Cog; 6 | use Grafana\Foundation\Dashboard; 7 | 8 | /** 9 | * @implements Cog\Builder<Dashboard\Panel> 10 | */ 11 | class PanelBuilder extends Dashboard\PanelBuilder implements Cog\Builder 12 | { 13 | public function __construct() 14 | { 15 | parent::__construct(); 16 | $this->internal->type = 'custom-panel'; 17 | } 18 | 19 | public function makeBeautiful(): static 20 | { 21 | if ($this->internal->options === null) { 22 | $this->internal->options = new Options(); 23 | } 24 | assert($this->internal->options instanceof Options); 25 | $this->internal->options->makeBeautiful = true; 26 | 27 | return $this; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_common_TableFooterOptions_field_fields_custom_strict_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_common_TableFooterOptions_field_fields_custom_strict_unmarshal" -}} 2 | {{- /* Non-standard unmarshalling needed because even though the "fields" field 3 | is typed as []string in the schema, Grafana uses `""` as an empty value 4 | */ -}} 5 | if len(fields["fields"]) != 0 && fields["fields"][0] == '"' { 6 | var field string 7 | if err := json.Unmarshal(fields["fields"], &field); err != nil { 8 | errs = append(errs, cog.MakeBuildErrors("fields", err)...) 9 | } else { 10 | resource.Fields = []string{field} 11 | } 12 | } else { 13 | if err := json.Unmarshal(fields["fields"], &resource.Fields); err != nil { 14 | errs = append(errs, cog.MakeBuildErrors("fields", err)...) 15 | } 16 | } 17 | 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/api_reference_object_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_object_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Encoding to JSON 5 | 6 | ```php 7 | use Grafana\Foundation\Dashboard\Dashboard; 8 | 9 | require_once __DIR__.'/vendor/autoload.php'; 10 | 11 | $dashboard = new Dashboard( 12 | uid: 'sample-dashboard-uid', 13 | title: 'Sample dashboard', 14 | ); 15 | 16 | echo(json_encode($dashboard, JSON_PRETTY_PRINT).PHP_EOL); 17 | ``` 18 | 19 | ### Decoding from JSON 20 | 21 | ```php 22 | use Grafana\Foundation\Dashboard\Dashboard; 23 | 24 | require_once __DIR__.'/vendor/autoload.php'; 25 | 26 | $dashboardJSON = file_get_contents(__DIR__.'/dashboard.json'); 27 | 28 | $dashboard = Dashboard::fromArray(json_decode($dashboardJSON, true)); 29 | 30 | var_dump($dashboard); 31 | ``` 32 | {{ end }} 33 | -------------------------------------------------------------------------------- /examples/java/custom-query/src/main/java/customquery/CustomQueryBuilder.java: -------------------------------------------------------------------------------- 1 | package customquery; 2 | 3 | import com.grafana.foundation.cog.Builder; 4 | import com.grafana.foundation.cog.variants.Dataquery; 5 | 6 | public class CustomQueryBuilder implements Builder<Dataquery> { 7 | private final CustomQuery internal; 8 | 9 | public CustomQueryBuilder(String query) { 10 | this.internal = new CustomQuery(); 11 | this.internal.query = query; 12 | } 13 | 14 | public CustomQueryBuilder refId(String refId) { 15 | this.internal.refId = refId; 16 | return this; 17 | } 18 | 19 | public CustomQueryBuilder hide(Boolean hide) { 20 | this.internal.hide = hide; 21 | return this; 22 | } 23 | 24 | @Override 25 | public CustomQuery build() { 26 | return this.internal; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/php/custom-query/index.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | use App\Custom; 4 | use Grafana\Foundation\Cog; 5 | use Grafana\Foundation\Dashboard\DashboardBuilder; 6 | use Grafana\Foundation\Timeseries; 7 | 8 | require_once __DIR__.'/vendor/autoload.php'; 9 | 10 | // This lets cog know about the newly created query type and how to unmarshal it. 11 | Cog\Runtime::get()->registerDataqueryVariant(new Cog\DataqueryConfig( 12 | identifier: 'custom', // datasource plugin ID 13 | fromArray: [Custom\Query::class, 'fromArray'], 14 | )); 15 | 16 | $builder = (new DashboardBuilder(title: '[Example] Custom query')) 17 | ->uid('example-custom-query') 18 | ->withPanel( 19 | (new Timeseries\PanelBuilder()) 20 | ->title('Sample panel') 21 | ->withTarget(new Custom\QueryBuilder('query here')) 22 | ) 23 | ; 24 | 25 | var_dump($builder->build()); -------------------------------------------------------------------------------- /.cog/schemas/composable/athena/dataquery.cue: -------------------------------------------------------------------------------- 1 | package athena 2 | 3 | import ( 4 | "github.com/grafana/grafana/packages/grafana-schema/src/common" 5 | ) 6 | 7 | // Manually converted from https://github.com/grafana/athena-datasource/blob/57ad707147b7a11e9a521a836d6bf9799877e0e3/src/types.ts 8 | Dataquery: { 9 | common.DataQuery 10 | 11 | format: #FormatOptions 12 | connectionArgs: #ConnectionArgs 13 | table?: string 14 | column?: string 15 | 16 | queryID?: string 17 | 18 | rawSQL: string | *"" 19 | } 20 | 21 | defaultKey: "__default" 22 | 23 | #ConnectionArgs: { 24 | region?: string | *defaultKey 25 | catalog?: string | *defaultKey 26 | database?: string | *defaultKey 27 | resultReuseEnabled?: bool | *false 28 | resultReuseMaxAgeInMinutes?: number | *60 29 | } 30 | 31 | #FormatOptions: 0 | 1 | 2 @cog(kind="enum", memberNames="TimeSeries|Table|Logs") 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.15.0 2 | brotlicffi==1.1.0.0 3 | certifi==2024.7.4 4 | cffi==1.17.1 5 | charset-normalizer==3.3.2 6 | click==8.1.7 7 | colorama==0.4.6 8 | ghp-import==2.1.0 9 | idna==3.7 10 | importlib_metadata==7.1.0 11 | Jinja2==3.1.4 12 | Markdown==3.7 13 | MarkupSafe==2.1.5 14 | mergedeep==1.3.4 15 | mkdocs==1.6.1 16 | mkdocs-get-deps==0.2.0 17 | mkdocs-material==9.5.39 18 | mkdocs-material-extensions==1.3.1 19 | mkdocs-nav-weight==0.2.0 20 | mypy==1.10.0 21 | mypy-extensions==1.0.0 22 | packaging==24.1 23 | paginate==0.5.7 24 | pathspec==0.12.1 25 | platformdirs==4.2.2 26 | pycparser==2.22 27 | Pygments==2.18.0 28 | pymdown-extensions==10.11.2 29 | python-dateutil==2.9.0.post0 30 | PyYAML==6.0.2 31 | pyyaml_env_tag==0.1 32 | regex==2024.5.15 33 | requests==2.32.3 34 | six==1.16.0 35 | toml==0.10.2 36 | typing_extensions==4.12.2 37 | urllib3==2.2.2 38 | watchdog==4.0.1 39 | zipp==3.19.2 40 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboard_DataSourceRef_custom_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboard_DataSourceRef_custom_unmarshal" }} 2 | {{- /* Non-standard unmarshalling, to support "legacy" datasource references. ie: older versions of Grafana reference datasources directly by their name */ -}} 3 | func (resource *DataSourceRef) UnmarshalJSON(raw []byte) error { 4 | if raw == nil { 5 | return nil 6 | } 7 | 8 | if raw[0] == '"' { 9 | var datasourceUid string 10 | if err := json.Unmarshal(raw, &datasourceUid); err != nil { 11 | return err 12 | } 13 | resource.Uid = &datasourceUid 14 | } else { 15 | type original DataSourceRef 16 | 17 | if err := json.Unmarshal(raw, (*original)(resource)); err != nil { 18 | return err 19 | } 20 | } 21 | 22 | return nil 23 | } 24 | 25 | {{ end }} 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature/enhancement request 2 | description: Request for feature or enhancement that are you would like to see. 3 | title: "[Feature]: <title>" 4 | labels: [enhancement] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Grafana-foundation-SDK is open to improvements. We will evaluate your request. 11 | - type: markdown 12 | attributes: 13 | value: | 14 | # 15 | 16 | - type: textarea 17 | attributes: 18 | label: Why is this needed? 19 | description: Describe the problem that you want to solve. 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | attributes: 25 | label: What would you like to be added? 26 | description: Add a brief description of the feature o enhancement should do. 27 | validations: 28 | required: true 29 | -------------------------------------------------------------------------------- /scripts/release-validate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error. Append "|| true" if you expect an error. 4 | set -o errexit 5 | # Exit on error inside any functions or subshells. 6 | set -o errtrace 7 | # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR 8 | set -o nounset 9 | # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump | gzip` 10 | set -o pipefail 11 | 12 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | source "${__dir}/libs/logs.sh" 14 | 15 | release_path=${1:-"./"} 16 | 17 | find "${release_path}" -maxdepth 1 -mindepth 1 -type d -not -path '*/.*' -print | while read -r target; do 18 | target=${target#"$release_path/"} 19 | 20 | if [ -e "${__dir}/validate/${target}.sh" ]; then 21 | info " → checking ${target}" 22 | ${__dir}/validate/${target}.sh "${release_path}" 23 | else 24 | warning " → skipping ${target}: no validator file found" 25 | fi 26 | done -------------------------------------------------------------------------------- /.cog/veneers/alerting.common.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: all 4 | 5 | package: alerting 6 | 7 | builders: 8 | - omit: { by_object: RelativeTimeRange } 9 | 10 | # Make some constructors more friendly 11 | - promote_options_to_constructor: 12 | by_object: RuleGroup 13 | options: [title] 14 | - promote_options_to_constructor: 15 | by_object: Rule 16 | options: [title] 17 | - promote_options_to_constructor: 18 | by_object: Query 19 | options: [refId] 20 | 21 | options: 22 | - duplicate: 23 | by_name: RuleGroup.rules 24 | as: withRule 25 | - array_to_append: 26 | by_name: RuleGroup.withRule 27 | 28 | - rename: 29 | by_name: Rule.data 30 | as: queries 31 | - duplicate: 32 | by_name: Rule.queries 33 | as: withQuery 34 | - array_to_append: 35 | by_name: Rule.withQuery 36 | -------------------------------------------------------------------------------- /examples/php/custom-panel/index.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | use App\Custom; 4 | use Grafana\Foundation\Cog; 5 | use Grafana\Foundation\Dashboard\DashboardBuilder; 6 | use Grafana\Foundation\Dashboard\RowBuilder; 7 | 8 | require_once __DIR__.'/vendor/autoload.php'; 9 | 10 | // This lets cog know about the newly created panel type and how to unmarshal it. 11 | Cog\Runtime::get()->registerPanelcfgVariant(new Cog\PanelcfgConfig( 12 | identifier: 'custom-panel', // panel plugin ID 13 | optionsFromArray: [Custom\Options::class, 'fromArray'], 14 | )); 15 | 16 | $builder = (new DashboardBuilder(title: '[Example] Custom panel type')) 17 | ->uid('example-custom-panel') 18 | ->refresh('1m') 19 | ->time('now-30m', 'now') 20 | ->withRow(new RowBuilder('Overview')) 21 | ->withPanel( 22 | (new Custom\PanelBuilder()) 23 | ->title('Sample panel') 24 | ->makeBeautiful() 25 | ) 26 | ; 27 | 28 | var_dump($builder->build()); 29 | -------------------------------------------------------------------------------- /.github/actions/setup-cog/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Setup cog" 2 | description: "Downloads cog and adds it to the $PATH" 3 | inputs: 4 | version: 5 | description: "Cog version to install" 6 | required: true 7 | default: "0.0.46" 8 | outputs: 9 | bin-path: 10 | description: "Path to the cog binary" 11 | value: ${{ steps.setup.outputs.bin-path }} 12 | runs: 13 | using: "composite" 14 | steps: 15 | - name: Download cog 16 | shell: bash 17 | env: 18 | COG_VERSION: ${{ inputs.version }} 19 | run: | 20 | version="$(echo "${COG_VERSION}" | sed 's/[^a-zA-Z0-9._\/-]//g')" 21 | mkdir -p "${HOME}/.local/bin/" 22 | wget https://github.com/grafana/cog/releases/download/v${version}/cog_Linux_x86_64.tar.gz 23 | tar -xzf cog_Linux_x86_64.tar.gz 24 | mv cog "${HOME}/.local/bin/cog" 25 | chmod +x "${HOME}/.local/bin/cog" 26 | echo "bin-path=${HOME}/.local/bin" >> "${GITHUB_OUTPUT}" 27 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboard_Panel_field_targets_custom_strict_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboard_Panel_field_targets_custom_strict_unmarshal" -}} 2 | {{- /* Non-standard unmarshalling to use the possible datasourceRef defined on the panel when unmarshalling dataqueries */}} 3 | rawDataqueries := []json.RawMessage{} 4 | if err := json.Unmarshal(fields["targets"], &rawDataqueries); err != nil { 5 | return err 6 | } 7 | 8 | dataqueryTypeHint := "" 9 | if resource.Datasource != nil && resource.Datasource.Type != nil { 10 | dataqueryTypeHint = *resource.Datasource.Type 11 | } 12 | 13 | for i1 := range rawDataqueries { 14 | dataquery, err := cog.StrictUnmarshalDataquery(rawDataqueries[i1], dataqueryTypeHint) 15 | if err != nil { 16 | errs = append(errs, cog.MakeBuildErrors("targets["+strconv.Itoa(i1)+"]", err)...) 17 | continue 18 | } 19 | 20 | resource.Targets = append(resource.Targets, dataquery) 21 | } 22 | 23 | {{ end }} 24 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/assignment_Dashboard_withPanel.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withPanel" }} 2 | 3 | if panelResource.GridPos == nil { 4 | panelResource.GridPos = NewGridPos() 5 | } 6 | // The panel either has no position set, or it is the first panel of the dashboard. 7 | // In that case, we position it on the grid 8 | if panelResource.GridPos.X == 0 && panelResource.GridPos.Y == 0 { 9 | panelResource.GridPos.X = builder.currentX 10 | panelResource.GridPos.Y = builder.currentY 11 | } 12 | {{- end }} 13 | 14 | {{- define "post_assignment_Dashboard_withPanel" }} 15 | 16 | // Prepare the coordinates for the next panel 17 | builder.currentX += panelResource.GridPos.W 18 | builder.lastPanelHeight = max(builder.lastPanelHeight, panelResource.GridPos.H) 19 | 20 | // Check for grid width overflow? 21 | if builder.currentX >= 24 { 22 | builder.currentX = 0 23 | builder.currentY += builder.lastPanelHeight 24 | builder.lastPanelHeight = 0 25 | } 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /examples/python/custom-panel/main.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.builders.dashboard import Dashboard 2 | from grafana_foundation_sdk.cog.encoder import JSONEncoder 3 | from grafana_foundation_sdk.cog.plugins import register_default_plugins 4 | from grafana_foundation_sdk.cog.runtime import register_panelcfg_variant 5 | 6 | from src.custompanel import custom_panel_variant_config, CustomPanelBuilder 7 | 8 | 9 | if __name__ == "__main__": 10 | # Required to correctly unmarshal panels and dataqueries 11 | register_default_plugins() 12 | 13 | # This lets cog know about the newly created panel type and how to unmarshal it. 14 | register_panelcfg_variant(custom_panel_variant_config()) 15 | 16 | dashboard = ( 17 | Dashboard("[Example] Custom panel") 18 | .uid("example-custom-panel") 19 | .with_panel(CustomPanelBuilder().title("Sample custom panel").make_beautiful()) 20 | ).build() 21 | 22 | print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard)) 23 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/assignment_Dashboard_withPanel.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withPanel" }} 2 | 3 | if panel_resource.grid_pos is None: 4 | panel_resource.grid_pos = dashboard.GridPos() 5 | 6 | # The panel either has no position set, or it is the first panel of the dashboard. 7 | # In that case, we position it on the grid 8 | if panel_resource.grid_pos.x == 0 and panel_resource.grid_pos.y == 0: 9 | panel_resource.grid_pos.x = self.__current_x 10 | panel_resource.grid_pos.y = self.__current_y 11 | {{- end }} 12 | 13 | {{- define "post_assignment_Dashboard_withPanel" }} 14 | 15 | # Prepare the coordinates for the next panel 16 | self.__current_x += panel_resource.grid_pos.w 17 | self.__last_panel_height = max(self.__last_panel_height, panel_resource.grid_pos.h) 18 | 19 | # Check for grid width overflow? 20 | if self.__current_x >= 24: 21 | self.__current_x = 0 22 | self.__current_y += self.__last_panel_height 23 | self.__last_panel_height = 0 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /scripts/release-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error. Append "|| true" if you expect an error. 4 | set -o errexit 5 | # Exit on error inside any functions or subshells. 6 | set -o errtrace 7 | # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR 8 | set -o nounset 9 | # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` 10 | set -o pipefail 11 | 12 | bold=$(tput bold) 13 | normal=$(tput sgr0) 14 | 15 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 16 | source "${__dir}/libs/logs.sh" 17 | source "${__dir}/versions.sh" 18 | 19 | ################# 20 | ### Usage ### 21 | ################# 22 | 23 | # LOG_LEVEL=7 ./scripts/release-all.sh 24 | 25 | ############ 26 | ### Main ### 27 | ############ 28 | 29 | for version in ${ALL_GRAFANA_VERSIONS//;/ } ; do 30 | log_group_start "Releasing ${version}" 31 | 32 | info "🪧 Releasing ${bold}${version}${normal}" 33 | $__dir/release-version.sh "${version}" 34 | 35 | log_group_end 36 | done 37 | -------------------------------------------------------------------------------- /.cog/templates/typescript/overrides/assignment_Dashboard_withPanel.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withPanel" }} 2 | 3 | if (!panelResource.gridPos) { 4 | panelResource.gridPos = dashboard.defaultGridPos(); 5 | } 6 | 7 | // The panel either has no position set, or it is the first panel of the dashboard. 8 | // In that case, we position it on the grid 9 | if (panelResource.gridPos.x == 0 && panelResource.gridPos.y == 0) { 10 | panelResource.gridPos.x = this.currentX; 11 | panelResource.gridPos.y = this.currentY; 12 | } 13 | {{- end }} 14 | 15 | {{- define "post_assignment_Dashboard_withPanel" }} 16 | 17 | // Prepare the coordinates for the next panel 18 | this.currentX += panelResource.gridPos.w; 19 | this.lastPanelHeight = Math.max(this.lastPanelHeight, panelResource.gridPos.h); 20 | 21 | // Check for grid width overflow? 22 | if (this.currentX >= 24) { 23 | this.currentX = 0; 24 | this.currentY += this.lastPanelHeight; 25 | this.lastPanelHeight = 0; 26 | } 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /.cog/templates/java/extra/src/main/java/com/grafana/foundation/cog/variants/DataqueryConfig.java: -------------------------------------------------------------------------------- 1 | package com.grafana.foundation.cog.variants; 2 | {{ if .Data.Config.GenerateConverters }} 3 | import com.grafana.foundation.cog.Converter; 4 | {{- end }} 5 | 6 | public class DataqueryConfig { 7 | private final Class<? extends Dataquery> dataquery; 8 | {{ if .Data.Config.GenerateConverters }}private final Converter<Dataquery> converter;{{ end }} 9 | 10 | public DataqueryConfig(Class<? extends Dataquery> dataquery{{ if .Data.Config.GenerateConverters }}, Converter<Dataquery> converter{{ end }}) { 11 | this.dataquery = dataquery; 12 | {{ if .Data.Config.GenerateConverters }}this.converter = converter;{{ end }} 13 | } 14 | 15 | public Class<? extends Dataquery> getDataquery() { 16 | return dataquery; 17 | } 18 | 19 | {{- if .Data.Config.GenerateConverters }} 20 | public Converter<Dataquery> getConverter() { 21 | return converter; 22 | } 23 | {{- end }} 24 | } 25 | -------------------------------------------------------------------------------- /examples/java/custom-panel/src/main/java/custompanel/Main.java: -------------------------------------------------------------------------------- 1 | package custompanel; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.grafana.foundation.cog.variants.Registry; 5 | import com.grafana.foundation.dashboard.Dashboard; 6 | import com.grafana.foundation.dashboard.DashboardBuilder; 7 | 8 | public class Main { 9 | public static void main(String[] args) { 10 | Registry.registerPanel("custom-panel", CustomPanelOptions.class, null); 11 | 12 | Dashboard dashboard = new DashboardBuilder("[Example] Custom Panel"). 13 | uid("example-custom-panel"). 14 | withPanel(new CustomPanelBuilder(). 15 | title("Sample custom panel"). 16 | makeItBeautiful() 17 | ). 18 | build(); 19 | 20 | try { 21 | System.out.println(dashboard.toJSON()); 22 | } catch (JsonProcessingException e) { 23 | throw new RuntimeException(e); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/variant_dataquery_field_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "variant_dataquery_field_unmarshal" -}} 2 | {{- $dataqueryHint := "\"\"" -}} 3 | {{- range $candidate := .Object.Type.Struct.Fields -}} 4 | {{- if and ($candidate.Type.IsRef) (eq $candidate.Type.Ref.ReferredType "DataSourceRef") -}} 5 | {{- $dataqueryHint = include "dataquery_field_unmarshal_hint" (dict "Candidate" $candidate) -}} 6 | {{- end -}} 7 | {{- end -}} 8 | {{- $cogruntime := importModule "cogruntime" "..cog" "runtime" -}} 9 | 10 | {{- if .Field.Type.IsArray -}} 11 | [{{ $cogruntime }}.dataquery_from_json(dataquery_json, {{ $dataqueryHint }}) for dataquery_json in data["{{ .Field.Name }}"]] 12 | {{- else -}} 13 | {{ $cogruntime }}.dataquery_from_json(data["{{ .Field.Name }}"], {{ $dataqueryHint }}) 14 | {{- end -}} 15 | {{- end }} 16 | 17 | {{- define "dataquery_field_unmarshal_hint" -}} 18 | data["{{ .Candidate.Name }}"]["type"] if data.get("{{ .Candidate.Name }}") is not None and data["{{ .Candidate.Name }}"].get("type", "") != "" else "" 19 | {{- end }} -------------------------------------------------------------------------------- /examples/pulumi/python/__main__.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.cog.encoder import JSONEncoder 2 | import pulumiverse_grafana as grafana 3 | import pulumi 4 | import os 5 | import sys 6 | 7 | 8 | if __name__ == '__main__': 9 | # Hack to re-use other examples 10 | sys.path.append(os.path.join(os.path.dirname(__file__), 11 | "..", '..', "python", "linux-node-overview")) 12 | from main import builder 13 | 14 | provider = grafana.Provider( 15 | 'grafana', url='http://localhost:3000', auth='admin:admin') 16 | 17 | dashboard = builder.build() 18 | dashboard_json = JSONEncoder( 19 | sort_keys=True, indent=2).encode(dashboard) 20 | 21 | pulumi_dashboard = grafana.dashboard.Dashboard(dashboard.uid, 22 | config_json=dashboard_json, 23 | opts=pulumi.ResourceOptions(providers=[provider])) 24 | 25 | # Export the Name of the repository 26 | pulumi.export('dashboardUID', pulumi_dashboard.uid) 27 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboardv2beta1_DataQueryKind_custom_unmarshal" }} 2 | @classmethod 3 | def from_json(cls, data: dict[str, typing.Any]) -> typing.Self: 4 | args: dict[str, typing.Any] = {} 5 | {{ range $field := .Object.Type.Struct.Fields }} 6 | {{- if eq $field.Name "spec" }} 7 | if "{{ $field.Name }}" in data: 8 | args["{{ $field.Name }}"] = {{ template "dashboardv2beta1_DataQueryKind_spec_unmarshal" (dict "Field" $field) }} 9 | {{- else if $field.Type.IsConcreteScalar }} 10 | {{ continue}} 11 | {{- else }} 12 | if "{{ $field.Name }}" in data: 13 | args["{{ $field.Name }}"] = data["{{ $field.Name }}"] 14 | {{ end -}} 15 | {{ end }} 16 | return cls(**args) 17 | {{- end }} 18 | 19 | {{- define "dashboardv2beta1_DataQueryKind_spec_unmarshal" -}} 20 | {{- $cogruntime := importModule "cogruntime" "..cog" "runtime" -}} 21 | {{ $cogruntime }}.dataquery_from_json(data["{{ .Field.Name }}"], data["group"]) 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/schema_variant_panelcfg.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "schema_variant_panelcfg" -}} 2 | {{- $_ := apiDeclareFunction (dict 3 | "pkg" .Schema.Package 4 | "name" "variant_config" 5 | "comments" (listStr 6 | (print "variant_config returns the configuration related to " (.Schema.Metadata.Identifier|lower) " panels.") 7 | "This configuration describes how to unmarshal it, convert it to code, …" 8 | ) 9 | "return" "variants.PanelcfgConfig" 10 | ) -}} 11 | {{- $cogruntime := importModule "cogruntime" "..cog" "runtime" -}} 12 | {{- $options := ternary "Options.from_json" "None" (schemaHasObject .Schema "Options") -}} 13 | {{- $fieldConfig := ternary "FieldConfig.from_json" "None" (schemaHasObject .Schema "FieldConfig") -}} 14 | 15 | def variant_config() -> {{ $cogruntime }}.PanelCfgConfig: 16 | return {{ $cogruntime }}.PanelCfgConfig( 17 | identifier="{{ .Schema.Metadata.Identifier }}", 18 | options_from_json_hook={{ $options }}, 19 | field_config_from_json_hook={{ $fieldConfig }}, 20 | ) 21 | {{ end }} -------------------------------------------------------------------------------- /examples/go/custom-panel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/grafana/grafana-foundation-sdk/go/cog" 8 | "github.com/grafana/grafana-foundation-sdk/go/cog/plugins" 9 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 10 | ) 11 | 12 | func main() { 13 | // Required to correctly unmarshal panels and dataqueries 14 | plugins.RegisterDefaultPlugins() 15 | 16 | // This lets cog know about the newly created query type and how to unmarshal it. 17 | cog.NewRuntime().RegisterPanelcfgVariant(CustomPanelVariantConfig()) 18 | 19 | builder := dashboard.NewDashboardBuilder("[Example] Custom panel"). 20 | Uid("example-custom-panel"). 21 | WithPanel( 22 | NewCustomPanelBuilder(). 23 | Title("Sample custom panel"). 24 | MakeBeautiful(), 25 | ) 26 | 27 | sampleDashboard, err := builder.Build() 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | fmt.Println(string(dashboardJson)) 38 | } 39 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/api_reference_object_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_object_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Marshalling to JSON 5 | 6 | ```python 7 | from grafana_foundation_sdk.cog.encoder import JSONEncoder 8 | from grafana_foundation_sdk.models.dashboard import Dashboard 9 | 10 | 11 | if __name__ == '__main__': 12 | dashboard = Dashboard() 13 | 14 | encoder = JSONEncoder(sort_keys=True, indent=2) 15 | print(encoder.encode(dashboard)) 16 | ``` 17 | 18 | ### Unmarshalling from JSON 19 | 20 | ```python 21 | import json 22 | 23 | from grafana_foundation_sdk.cog.plugins import register_default_plugins 24 | from grafana_foundation_sdk.models.dashboard import Dashboard as DashboardModel 25 | 26 | 27 | if __name__ == '__main__': 28 | # Required to correctly unmarshal panels and dataqueries 29 | register_default_plugins() 30 | 31 | with open("dashboard.json", "r") as f: 32 | decoded_dashboard = DashboardModel.from_json(json.load(f)) 33 | print(decoded_dashboard) 34 | ``` 35 | {{ end }} 36 | -------------------------------------------------------------------------------- /examples/java/custom-query/src/main/java/customquery/Main.java: -------------------------------------------------------------------------------- 1 | package customquery; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.grafana.foundation.cog.variants.Registry; 5 | import com.grafana.foundation.dashboard.Dashboard; 6 | import com.grafana.foundation.dashboard.DashboardBuilder; 7 | import com.grafana.foundation.timeseries.PanelBuilder; 8 | 9 | public class Main { 10 | public static void main(String[] args) { 11 | Registry.registerDataquery("custom-query", CustomQuery.class); 12 | 13 | Dashboard dashboard = new DashboardBuilder("[Example] Custom query"). 14 | uid("example-custom-query"). 15 | withPanel(new PanelBuilder(). 16 | title("Sample panel"). 17 | withTarget(new customquery.CustomQueryBuilder("query-here")) 18 | ). 19 | build(); 20 | 21 | try { 22 | System.out.println(dashboard.toJSON()); 23 | } catch (JsonProcessingException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/terraform/dashboards.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | go_builders = toset([ 3 | "${path.module}/../go/custom-panel", 4 | "${path.module}/../go/custom-query", 5 | "${path.module}/../go/grafana-agent-overview", 6 | "${path.module}/../go/linux-node-overview", 7 | "${path.module}/../go/red-method", 8 | ]) 9 | } 10 | 11 | // List and run the go dashboard builders. Any of the languages would also work. 12 | // We are using jq to convert the output to a map of strings, as expected by the external data source. 13 | // The dashboard building could be done with a single Go program that outputs the expected TF format, 14 | // but this example does it in multiple Go calls in order to reuse the existing examples. 15 | data "external" "dashboards" { 16 | for_each = local.go_builders 17 | working_dir = each.value 18 | program = ["bash", "-c", <<EOF 19 | go run *.go | jq '{"dashboard": . | tostring}' 20 | EOF 21 | ] 22 | } 23 | 24 | resource "grafana_dashboard" "examples" { 25 | for_each = local.go_builders 26 | config_json = data.external.dashboards[each.key].result.dashboard 27 | } 28 | -------------------------------------------------------------------------------- /.cog/templates/go/extra/cog/plugins/variants.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | {{- $panelPackages := .Context.PackagesForVariant "panelcfg" -}} 4 | {{- $dataqueryPackages := .Context.PackagesForVariant "dataquery" -}} 5 | 6 | {{- if or (ne (len $panelPackages) 0) (ne (len $dataqueryPackages) 0) }} 7 | import ( 8 | cog "{{ .Data.PackageRoot }}/cog" 9 | {{- range $pkg := $panelPackages }} 10 | {{ $pkg }} "{{ $.Data.PackageRoot }}/{{ $pkg }}" 11 | {{- end }} 12 | {{- range $pkg := $dataqueryPackages }} 13 | {{ $pkg }} "{{ $.Data.PackageRoot }}/{{ $pkg }}" 14 | {{- end }} 15 | ) 16 | {{- end }} 17 | 18 | func RegisterDefaultPlugins() { 19 | {{- if or (ne (len $panelPackages) 0) (ne (len $dataqueryPackages) 0) }} 20 | runtime := cog.NewRuntime() 21 | {{- end }} 22 | 23 | // Panelcfg variants 24 | {{- range $pkg := $panelPackages }} 25 | runtime.RegisterPanelcfgVariant({{ $pkg | formatPackageName }}.VariantConfig()) 26 | {{- end }} 27 | 28 | // Dataquery variants 29 | {{- range $pkg := $dataqueryPackages }} 30 | runtime.RegisterDataqueryVariant({{ $pkg | formatPackageName }}.VariantConfig()) 31 | {{- end }} 32 | } 33 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/assignment_Dashboard_withPanel.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withPanel" }} 2 | 3 | if ($panelResource->gridPos === null) { 4 | $panelResource->gridPos = new {{ "Dashboard\\GridPos" | fullNamespaceRef }}(); 5 | } 6 | // The panel either has no position set, or it is the first panel of the dashboard. 7 | // In that case, we position it on the grid 8 | if ($panelResource->gridPos->x === 0 && $panelResource->gridPos->y === 0) { 9 | $panelResource->gridPos->x = $this->currentX; 10 | $panelResource->gridPos->y = $this->currentY; 11 | } 12 | {{- end }} 13 | 14 | {{- define "post_assignment_Dashboard_withPanel" }} 15 | 16 | // Prepare the coordinates for the next panel 17 | $this->currentX += $panelResource->gridPos->w; 18 | $this->lastPanelHeight = max($this->lastPanelHeight, $panelResource->gridPos->h); 19 | 20 | // Check for grid width overflow? 21 | if ($this->currentX >= 24) { 22 | $this->currentX = 0; 23 | $this->currentY += $this->lastPanelHeight; 24 | $this->lastPanelHeight = 0; 25 | } 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /examples/typescript/custom-query/src/customQuery.ts: -------------------------------------------------------------------------------- 1 | import { Builder, Dataquery } from '@grafana/grafana-foundation-sdk/cog'; 2 | 3 | export interface CustomQuery { 4 | // refId and hide are expected on all queries 5 | refId?: string; 6 | hide?: boolean; 7 | 8 | 9 | // query is specific to the CustomQuery type 10 | query: string; 11 | 12 | // Let cog know that CustomQuery is a Dataquery variant 13 | _implementsDataqueryVariant(): void; 14 | } 15 | 16 | export const defaultCustomQuery = (): CustomQuery => ({ 17 | query: "", 18 | _implementsDataqueryVariant() { }, 19 | }); 20 | 21 | export class CustomQueryBuilder implements Builder<Dataquery> { 22 | private readonly internal: CustomQuery; 23 | 24 | constructor(query: string) { 25 | this.internal = defaultCustomQuery(); 26 | this.internal.query = query; 27 | } 28 | 29 | build(): CustomQuery { 30 | return this.internal; 31 | } 32 | 33 | refId(refId: string): this { 34 | this.internal.refId = refId; 35 | return this; 36 | } 37 | 38 | hide(hide: boolean): this { 39 | this.internal.hide = hide; 40 | return this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.cog/compiler/dataqueries_refid.yaml: -------------------------------------------------------------------------------- 1 | passes: 2 | ################# 3 | # DataSourceRef # 4 | ################# 5 | 6 | # We have to set refId not required since dashboards v2 schema fails if exists 7 | 8 | - fields_set_not_required: 9 | fields: [ 10 | athena.Dataquery.refId, 11 | azuremonitor.AzureMonitorQuery.refId, 12 | bigquery.Dataquery.refId, 13 | cloudwatch.CloudWatchMetricsQuery.refId, 14 | cloudwatch.CloudWatchLogsQuery.refId, 15 | cloudwatch.CloudWatchAnnotationQuery.refId, 16 | datasource.Dataquery.refId, 17 | elasticsearch.Dataquery.refId, 18 | expr.typeMath.refId, 19 | expr.typeReduce.refId, 20 | expr.typeResample.refId, 21 | expr.typeClassicConditions.refId, 22 | expr.typeThreshold.refId, 23 | expr.typeSql.refId, 24 | googlecloudmonitoring.CloudMonitoringQuery.refId, 25 | grafanapyroscope.Dataquery.refId, 26 | loki.Dataquery.refId, 27 | parca.Dataquery.refId, 28 | prometheus.Dataquery.refId, 29 | tempo.TempoQuery.refId, 30 | testdata.dataquery.refId, 31 | ] 32 | -------------------------------------------------------------------------------- /examples/go/grafana-agent-overview/discovery.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 5 | "github.com/grafana/grafana-foundation-sdk/go/units" 6 | ) 7 | 8 | func targetSyncTimeseries() *timeseries.PanelBuilder { 9 | return defaultTimeseries(). 10 | Title("Target Sync"). 11 | Description("Actual interval to sync the scrape pool."). 12 | Datasource(datasourceRef("$prometheus_datasource")). 13 | Unit(units.Seconds). 14 | WithTarget(prometheusQuery( 15 | `sum(rate(prometheus_target_sync_length_seconds_sum{job=~"$job", instance=~"$instance"}[$__rate_interval])) by (instance, scrape_job)`, 16 | "{{instance}}/{{scrape_job}}", 17 | )) 18 | } 19 | 20 | func targetsTimeseries() *timeseries.PanelBuilder { 21 | return defaultTimeseries(). 22 | Title("Targets"). 23 | Description("Discovered targets by prometheus service discovery."). 24 | Datasource(datasourceRef("$prometheus_datasource")). 25 | Unit(units.Short). 26 | WithTarget(prometheusQuery( 27 | `sum by (instance) (prometheus_sd_discovered_targets{job=~"$job", instance=~"$instance"})`, 28 | "{{instance}}", 29 | )) 30 | } 31 | -------------------------------------------------------------------------------- /.cog/templates/typescript/extra/docs/How-To/building-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Building a dashboard 2 | 3 | ```typescript 4 | import { DashboardBuilder, RowBuilder } from '@grafana/grafana-foundation-sdk/dashboard'; 5 | import { DataqueryBuilder } from '@grafana/grafana-foundation-sdk/prometheus'; 6 | import { PanelBuilder } from '@grafana/grafana-foundation-sdk/timeseries'; 7 | 8 | const builder = new DashboardBuilder('[TEST] Node Exporter / Raspberry') 9 | .uid('test-dashboard-raspberry') 10 | .tags(['generated', 'raspberrypi-node-integration']) 11 | 12 | .refresh('1m') 13 | .time({from: 'now-30m', to: 'now'}) 14 | .timezone('browser') 15 | 16 | .withRow(new RowBuilder('Overview')) 17 | .withPanel( 18 | new PanelBuilder() 19 | .title('Network Received') 20 | .unit('bps') 21 | .min(0) 22 | .withTarget( 23 | new DataqueryBuilder() 24 | .expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 25 | .legendFormat({{ `"{{ device }}"` }}) 26 | ) 27 | ) 28 | ; 29 | 30 | console.log(JSON.stringify(builder.build(), null, 2)); 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/python/custom-query/main.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.builders.dashboard import Dashboard 2 | from grafana_foundation_sdk.builders.timeseries import Panel as Timeseries 3 | from grafana_foundation_sdk.cog.encoder import JSONEncoder 4 | from grafana_foundation_sdk.cog.plugins import register_default_plugins 5 | from grafana_foundation_sdk.cog.runtime import register_dataquery_variant 6 | 7 | from src.customquery import custom_query_variant_config, CustomQueryBuilder 8 | 9 | 10 | if __name__ == "__main__": 11 | # Required to correctly unmarshal panels and dataqueries 12 | register_default_plugins() 13 | 14 | # This lets cog know about the newly created query type and how to unmarshal it. 15 | register_dataquery_variant(custom_query_variant_config()) 16 | 17 | dashboard = ( 18 | Dashboard("[Example] Custom query") 19 | .uid("example-custom-query") 20 | .with_panel( 21 | Timeseries() 22 | .title("Sample panel") 23 | .with_target(CustomQueryBuilder("query here")) 24 | ) 25 | ).build() 26 | 27 | print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard)) 28 | -------------------------------------------------------------------------------- /examples/typescript/red-method/src/common.ts: -------------------------------------------------------------------------------- 1 | import * as common from '@grafana/grafana-foundation-sdk/common'; 2 | import { PanelBuilder as TimeseriesBuilder } from '@grafana/grafana-foundation-sdk/timeseries'; 3 | 4 | export const defaultTimeseries = (): TimeseriesBuilder => { 5 | return new TimeseriesBuilder() 6 | .lineWidth(1) 7 | .fillOpacity(30) 8 | .showPoints(common.VisibilityMode.Never) 9 | .drawStyle(common.GraphDrawStyle.Line) 10 | .gradientMode(common.GraphGradientMode.Opacity) 11 | .spanNulls(false) 12 | .axisBorderShow(false) 13 | .lineInterpolation(common.LineInterpolation.Smooth) 14 | .legend( 15 | new common.VizLegendOptionsBuilder() 16 | .showLegend(true) 17 | .placement(common.LegendPlacement.Bottom) 18 | .displayMode(common.LegendDisplayMode.List) 19 | ) 20 | .tooltip( 21 | new common.VizTooltipOptionsBuilder() 22 | .mode(common.TooltipDisplayMode.Multi) 23 | .sort(common.SortOrder.Descending) 24 | ) 25 | .thresholdsStyle( 26 | new common.GraphThresholdsStyleConfigBuilder() 27 | .mode(common.GraphThresholdsStyleMode.Off) 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/variant_dataquery_field_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "variant_dataquery_field_unmarshal" -}} 2 | {{- $cog := importPkg "cog" }} 3 | if fields["{{ .Field.Name }}"] != nil { 4 | dataqueryTypeHint := "" 5 | {{ range $field := .Object.Type.Struct.Fields }} 6 | {{ if and ($field.Type.IsRef) (eq $field.Type.Ref.ReferredType "DataSourceRef") }} 7 | if resource.{{ $field.Name|formatFieldName }} != nil && resource.{{ $field.Name|formatFieldName }}.Type != nil { 8 | dataqueryTypeHint = *resource.{{ $field.Name|formatFieldName }}.Type 9 | } 10 | {{ end }} 11 | {{ end }} 12 | {{- if .Field.Type.IsArray -}} 13 | {{ .Field.Name }}, err := cog.UnmarshalDataqueryArray(fields["{{ .Field.Name }}"], dataqueryTypeHint) 14 | if err != nil { 15 | return err 16 | } 17 | resource.{{ .Field.Name|formatFieldName }} = {{ .Field.Name }} 18 | {{- else -}} 19 | {{ .Field.Name }}, err := cog.UnmarshalDataquery(fields["{{ .Field.Name }}"], dataqueryTypeHint) 20 | if err != nil { 21 | return err 22 | } 23 | resource.{{ .Field.Name|formatFieldName }} = {{ .Field.Name }} 24 | {{- end -}} 25 | } 26 | {{ end }} -------------------------------------------------------------------------------- /examples/go/custom-query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/grafana/grafana-foundation-sdk/go/cog" 8 | "github.com/grafana/grafana-foundation-sdk/go/cog/plugins" 9 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 10 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 11 | ) 12 | 13 | func main() { 14 | // Required to correctly unmarshal panels and dataqueries 15 | plugins.RegisterDefaultPlugins() 16 | 17 | // This lets cog know about the newly created query type and how to unmarshal it. 18 | cog.NewRuntime().RegisterDataqueryVariant(CustomQueryVariantConfig()) 19 | 20 | builder := dashboard.NewDashboardBuilder("[Example] Custom query"). 21 | Uid("example-custom-query"). 22 | WithPanel( 23 | timeseries.NewPanelBuilder(). 24 | Title("Sample panel"). 25 | WithTarget( 26 | NewCustomQueryBuilder("query here"), 27 | ), 28 | ) 29 | 30 | sampleDashboard, err := builder.Build() 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | fmt.Println(string(dashboardJson)) 41 | } 42 | -------------------------------------------------------------------------------- /examples/pulumi/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | linuxNodeOverview "github.com/grafana/grafana-foundation-sdk/examples/go/linux-node-overview/builder" 7 | "github.com/pulumi/pulumi/sdk/v3/go/pulumi" 8 | "github.com/pulumiverse/pulumi-grafana/sdk/go/grafana" 9 | ) 10 | 11 | func main() { 12 | pulumi.Run(func(ctx *pulumi.Context) error { 13 | grafanaProvider, err := grafana.NewProvider(ctx, "grafana", &grafana.ProviderArgs{ 14 | Auth: pulumi.String("admin:admin"), 15 | Url: pulumi.String("http://localhost:3000"), 16 | }) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | sampleDashboard, err := linuxNodeOverview.Build() 22 | if err != nil { 23 | return err 24 | } 25 | 26 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 27 | if err != nil { 28 | return err 29 | } 30 | 31 | pulumiDashboard, err := grafana.NewDashboard(ctx, *sampleDashboard.Uid, &grafana.DashboardArgs{ 32 | ConfigJson: pulumi.String(dashboardJson), 33 | }, pulumi.Provider(grafanaProvider)) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | ctx.Export("dashboardUID", pulumiDashboard.Uid) 39 | 40 | return nil 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/variant_dataquery_field_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "variant_dataquery_field_unmarshal" -}} 2 | {{- $dataqueryHint := "\"\"" -}} 3 | {{- range $candidate := .Object.Type.Struct.Fields -}} 4 | {{- if and ($candidate.Type.IsRef) (eq $candidate.Type.Ref.ReferredType "DataSourceRef") -}} 5 | {{- $dataqueryHint = include "dataquery_field_unmarshal_hint" (dict "Candidate" $candidate) -}} 6 | {{- end -}} 7 | {{- end -}} 8 | isset({{ .InputVar }}) ? (function ($in) { 9 | /** @var array{datasource?: array{type?: mixed}} $in */ 10 | $hint = {{ $dataqueryHint }}; 11 | {{ if .Type.IsArray -}} 12 | /** @var array<array<string, mixed>> $in */ 13 | return {{ "Cog\\Runtime"|fullNamespaceRef }}::get()->dataqueriesFromArray($in, $hint); 14 | {{- else }} 15 | /** @var array<string, mixed> $in */ 16 | return {{ "Cog\\Runtime"|fullNamespaceRef }}::get()->dataqueryFromArray($in, $hint); 17 | {{- end }} 18 | })({{ .InputVar }}) : null 19 | {{- end }} 20 | 21 | {{- define "dataquery_field_unmarshal_hint" -}} 22 | (isset($in["{{ .Candidate.Name }}"], $in["{{ .Candidate.Name }}"]["type"]) && is_string($in["{{ .Candidate.Name }}"]["type"])) ? $in["{{ .Candidate.Name }}"]["type"] : "" 23 | {{- end }} -------------------------------------------------------------------------------- /examples/python/red-method/src/common.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.builders import timeseries, common as common_builder 2 | from grafana_foundation_sdk.models import common 3 | 4 | 5 | def default_timeseries() -> timeseries.Panel: 6 | return ( 7 | timeseries.Panel() 8 | .line_width(1) 9 | .fill_opacity(30) 10 | .show_points(common.VisibilityMode.NEVER) 11 | .draw_style(common.GraphDrawStyle.LINE) 12 | .gradient_mode(common.GraphGradientMode.OPACITY) 13 | .span_nulls(False) 14 | .line_interpolation(common.LineInterpolation.SMOOTH) 15 | .legend( 16 | common_builder.VizLegendOptions() 17 | .display_mode(common.LegendDisplayMode.LIST) 18 | .placement(common.LegendPlacement.BOTTOM) 19 | .show_legend(True) 20 | ) 21 | .tooltip( 22 | common_builder.VizTooltipOptions() 23 | .mode(common.TooltipDisplayMode.MULTI) 24 | .sort(common.SortOrder.DESCENDING) 25 | ) 26 | .thresholds_style( 27 | common_builder.GraphThresholdsStyleConfig().mode( 28 | common.GraphThresholdsStyleMode.OFF 29 | ) 30 | ) 31 | ) 32 | -------------------------------------------------------------------------------- /scripts/libs/git.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function git_run() { 4 | local repo_dir="${1}" 5 | shift 6 | 7 | git -C "${repo_dir}" "$@" 8 | } 9 | 10 | function git_has_branch() { 11 | local repo_dir=${1} 12 | shift 13 | local branch=${1} 14 | shift 15 | 16 | # test local branches 17 | local in_local 18 | in_local=$(git_run "${repo_dir}" branch --list "${branch}") 19 | if [ "${in_local}" != "" ]; then 20 | echo "0" 21 | return 0 22 | fi 23 | 24 | # test remote branches 25 | local in_remote 26 | in_remote=$(git_run "${repo_dir}" ls-remote --heads origin "${branch}") 27 | if [ "${in_remote}" != "" ]; then 28 | echo "0" 29 | return 0 30 | fi 31 | 32 | echo "1" 33 | } 34 | 35 | function git_has_changes() { 36 | local repo_dir=${1} 37 | shift 38 | 39 | local changes 40 | changes=$(git_run "${repo_dir}" status --porcelain=v1) 41 | if [ "${changes}" != "" ]; then 42 | echo "0" 43 | return 0 44 | fi 45 | 46 | echo "1" 47 | } 48 | 49 | function git_create_orphan_branch() { 50 | local repo_dir=${1} 51 | shift 52 | local branch=${1} 53 | shift 54 | 55 | git_run "${repo_dir}" checkout --orphan "${branch}" 56 | git_run "${repo_dir}" rm -rf . 57 | } 58 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/schema_variant_dataquery.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "schema_variant_dataquery" -}} 2 | {{- $_ := apiDeclareFunction (dict 3 | "pkg" .Schema.Package 4 | "name" "variant_config" 5 | "comments" (listStr 6 | (print "variant_config returns the configuration related to " (.Schema.Metadata.Identifier|lower) " data queries.") 7 | "This configuration describes how to unmarshal it, convert it to code, …" 8 | ) 9 | "return" "variants.DataqueryConfig" 10 | ) -}} 11 | {{- $cogruntime := importModule "cogruntime" "..cog" "runtime" -}} 12 | {{- $fromJsonSetup := "" -}} 13 | {{- $fromJson := print (.Schema.EntryPoint|formatObjectName) ".from_json" -}} 14 | {{- if resolvesToDisjunction .Schema.EntryPointType -}} 15 | {{- $code := unmarshalForType .Schema.EntryPointType "data" "entrypoint" -}} 16 | {{- $fromJsonSetup = print $code.Setup "\n " -}} 17 | {{- $fromJson = print "lambda data: " $code.DecodingCall -}} 18 | {{- end -}} 19 | 20 | def variant_config() -> {{ $cogruntime }}.DataqueryConfig: 21 | {{ $fromJsonSetup }}return {{ $cogruntime }}.DataqueryConfig( 22 | identifier="{{ .Schema.Metadata.Identifier }}", 23 | from_json_hook={{ $fromJson }}, 24 | ) 25 | {{ end }} 26 | -------------------------------------------------------------------------------- /.cog/veneers/dashboard.python.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: python 4 | 5 | package: dashboard 6 | 7 | builders: 8 | ############## 9 | # Dashboards # 10 | ############## 11 | 12 | # We don't want these builders at all 13 | - omit: { by_object: DashboardDashboardTime } 14 | - omit: { by_object: ValueMappingResult } 15 | 16 | options: 17 | ############## 18 | # Dashboards # 19 | ############## 20 | 21 | # Time(from, to) instead of time(struct {From string `json:"from"`, To string `json:"to"`}{From: "lala", To: "lala}) 22 | - struct_fields_as_arguments: 23 | by_name: Dashboard.time 24 | 25 | ############## 26 | # Panels # 27 | ############## 28 | 29 | # WithOverride(matcher, properties) instead of WithOverride(struct{...}) 30 | - struct_fields_as_arguments: 31 | by_name: Panel.withOverride 32 | 33 | ######################## 34 | # AnnotationPermission # 35 | ######################## 36 | 37 | - rename: 38 | by_name: AnnotationPermission.dashboard 39 | as: dashboard_permissions 40 | - rename: 41 | by_name: AnnotationPermission.organization 42 | as: organization_permissions 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COG_VERSION = v0.0.46 2 | COG_DIR = $(shell go env GOPATH)/bin/cog-$(COG_VERSION) 3 | COG_BIN = $(COG_DIR)/cli 4 | 5 | GRAFANA_VERSION="v12.2.1" 6 | KIND_REGISTRY_VERSION="next" 7 | KIND_REGISTRY_PATH="./kind-registry" 8 | 9 | .PHONY: install-cog 10 | install-cog: echodir $(COG_BIN) 11 | 12 | .PHONY: echodir 13 | 14 | $(COG_BIN): 15 | @echo "Installing Cog version $(COG_VERSION)" 16 | @mkdir -p $(COG_DIR) 17 | GOBIN=$(COG_DIR) go install github.com/grafana/cog/cmd/cli@$(COG_VERSION) 18 | @touch $@ 19 | 20 | .PHONY: update-cog 21 | update-app-sdk: ## Update the Grafana App SDK dependency in go.mod 22 | @pwd 23 | go get github.com/grafana/cog@$(COG_VERSION) 24 | go mod tidy 25 | 26 | .PHONY: clone-kind-registry 27 | clone-kind-registry: 28 | ./scripts/fetch-kind-registry.sh 29 | 30 | .PHONY: generate 31 | generate: install-cog clone-kind-registry 32 | bash -c 'source ./scripts/versions.sh && \ 33 | $(COG_BIN) generate --config .cog/config.yaml \ 34 | --parameters "output_dir=%l,kind_registry_path=$(KIND_REGISTRY_PATH),kind_registry_version=$(KIND_REGISTRY_VERSION),grafana_version=$(GRAFANA_VERSION),release_branch=main,all_grafana_versions=$$ALL_GRAFANA_VERSIONS,cog_version=$$COG_VERSION",repository_templates_dir=""' 35 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboardv2beta1_DataQueryKind_custom_unmarshal" }} 2 | /** 3 | * @param array<string, mixed> $inputData 4 | */ 5 | public static function fromArray(array $inputData): self 6 | { 7 | /** @var {{ typeShape .Object.Type }} $inputData */ 8 | $data = $inputData; 9 | return new self( 10 | {{- range $field := .Object.Type.Struct.Fields }} 11 | {{ if eq $field.Name "spec" -}} 12 | {{ $field.Name }}: {{ template "dashboardv2beta1_DataQueryKind_spec_unmarshal" }} 13 | {{- else if or $field.Type.IsConcreteScalar $field.Type.IsConstantRef }} 14 | {{ continue }} 15 | {{- else -}} 16 | {{ $field.Name }}: {{ unmarshalForType $field.Type (print "$data[\"" $field.Name "\"]") }} 17 | {{- end -}}, 18 | {{- end }} 19 | ); 20 | } 21 | {{- end }} 22 | 23 | {{- define "dashboardv2beta1_DataQueryKind_spec_unmarshal" -}} 24 | isset($data['spec']) ? (function($input) { 25 | /** @var array<string, mixed> $spec */ 26 | $spec = $input['spec']; 27 | return \Grafana\Foundation\Cog\Runtime::get()->dataqueryFromArray($spec, $input['group'] ?? ''); 28 | })($data) : null 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /.cog/veneers/heatmap.common.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: all 4 | 5 | package: heatmap 6 | 7 | builders: ~ 8 | 9 | options: 10 | # ExemplarsColor(color: string) instead of Exemplars(exemplarsConfig: ExemplarConfig) 11 | # ExemplarConfig only includes (color: string) field. 12 | - struct_fields_as_arguments: 13 | by_builder: Panel.exemplars 14 | - rename: 15 | by_builder: Panel.exemplars 16 | as: exemplarsColor 17 | 18 | # ShowLegend/HideLegends instead of Legend(show: bool) 19 | - struct_fields_as_arguments: 20 | by_builder: Panel.legend 21 | - unfold_boolean: 22 | by_builder: Panel.legend 23 | true_as: showLegend 24 | false_as: hideLegend 25 | 26 | # Tooltip(show: bool, yHistogram: bool) split into: 27 | # - ShowTooltip/HideTooltip 28 | # - ShowYHistogram/HideYHistogram 29 | - struct_fields_as_options: 30 | by_builder: Panel.tooltip 31 | - unfold_boolean: 32 | by_builder: Panel.show 33 | true_as: showTooltip 34 | false_as: hideTooltip 35 | - unfold_boolean: 36 | by_builder: Panel.yHistogram 37 | true_as: showYHistogram 38 | false_as: hideYHistogram 39 | -------------------------------------------------------------------------------- /examples/typescript/grafana-agent-overview/src/discovery.ts: -------------------------------------------------------------------------------- 1 | import * as timeseries from "@grafana/grafana-foundation-sdk/timeseries"; 2 | import * as units from "@grafana/grafana-foundation-sdk/units"; 3 | import { defaultTimeseries, prometheusQuery } from "./common"; 4 | 5 | export const targetSyncTimeseries = (): timeseries.PanelBuilder => { 6 | return defaultTimeseries() 7 | .title("Target Sync") 8 | .description("Actual interval to sync the scrape pool.") 9 | .datasource({ uid: "$prometheus_datasource" }) 10 | .unit(units.Seconds) 11 | .withTarget(prometheusQuery( 12 | 'sum(rate(prometheus_target_sync_length_seconds_sum{job=~"$job", instance=~"$instance"}[$__rate_interval])) by (instance, scrape_job)', 13 | "{{instance}}/{{scrape_job}}" 14 | )); 15 | }; 16 | 17 | export const targetsTimeseries = (): timeseries.PanelBuilder => { 18 | return defaultTimeseries() 19 | .title("Targets") 20 | .description("Discovered targets by prometheus service discovery.") 21 | .datasource({ uid: "$prometheus_datasource" }) 22 | .unit(units.Short) 23 | .withTarget(prometheusQuery( 24 | 'sum by (instance) (prometheus_sd_discovered_targets{job=~"$job", instance=~"$instance"})', 25 | "{{instance}}" 26 | )); 27 | }; 28 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/docs/How-To/building-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Building a dashboard 2 | 3 | ```php 4 | <?php 5 | 6 | use Grafana\Foundation\Common; 7 | use Grafana\Foundation\Dashboard\DashboardBuilder; 8 | use Grafana\Foundation\Dashboard\RowBuilder; 9 | use Grafana\Foundation\Prometheus; 10 | use Grafana\Foundation\Timeseries; 11 | 12 | require_once __DIR__.'/vendor/autoload.php'; 13 | 14 | $builder = (new DashboardBuilder(title: 'Sample dashboard')) 15 | ->uid('generated-from-php') 16 | ->tags(['generated', 'from', 'php']) 17 | ->refresh('1m') 18 | ->time('now-30m', 'now') 19 | ->timezone(Common\Constants::TIME_ZONE_BROWSER) 20 | ->withRow(new RowBuilder('Overview')) 21 | ->withPanel( 22 | (new Timeseries\PanelBuilder()) 23 | ->title('Network received') 24 | ->unit('bps') 25 | ->min(0) 26 | ->withTarget( 27 | (new Prometheus\DataqueryBuilder()) 28 | ->expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 29 | ->legendFormat({{ `'{{ device }}'` }}) 30 | ) 31 | ) 32 | ; 33 | 34 | echo(json_encode($builder->build(), JSON_PRETTY_PRINT).PHP_EOL); 35 | ``` 36 | -------------------------------------------------------------------------------- /.cog/templates/typescript/overrides/api_reference_builder_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_builder_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Building a dashboard 5 | 6 | ```typescript 7 | import { DashboardBuilder, RowBuilder } from '@grafana/grafana-foundation-sdk/dashboard'; 8 | import { DataqueryBuilder } from '@grafana/grafana-foundation-sdk/prometheus'; 9 | import { PanelBuilder } from '@grafana/grafana-foundation-sdk/timeseries'; 10 | 11 | const builder = new DashboardBuilder('[TEST] Node Exporter / Raspberry') 12 | .uid('test-dashboard-raspberry') 13 | .tags(['generated', 'raspberrypi-node-integration']) 14 | 15 | .refresh('1m') 16 | .time({from: 'now-30m', to: 'now'}) 17 | .timezone('browser') 18 | 19 | .withRow(new RowBuilder('Overview')) 20 | .withPanel( 21 | new PanelBuilder() 22 | .title('Network Received') 23 | .unit('bps') 24 | .min(0) 25 | .withTarget( 26 | new DataqueryBuilder() 27 | .expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 28 | .legendFormat({{ `"{{ device }}"` }}) 29 | ) 30 | ) 31 | ; 32 | 33 | console.log(JSON.stringify(builder.build(), null, 2)); 34 | ``` 35 | {{ end }} 36 | -------------------------------------------------------------------------------- /.cog/templates/java/extra/src/main/java/com/grafana/foundation/cog/variants/PanelConfig.java: -------------------------------------------------------------------------------- 1 | package com.grafana.foundation.cog.variants; 2 | {{ if .Data.Config.GenerateConverters }} 3 | import com.grafana.foundation.dashboard.Panel; 4 | import com.grafana.foundation.cog.Converter; 5 | {{- end }} 6 | 7 | public class PanelConfig { 8 | private final Class<?> optionsClass; 9 | private final Class<?> fieldConfigClass; 10 | {{- if .Data.Config.GenerateConverters }} 11 | private final Converter<Panel> converter; 12 | {{- end }} 13 | 14 | public PanelConfig(Class<?> optionsClass, Class<?> fieldConfigClass{{ if .Data.Config.GenerateConverters }}, Converter<Panel> converter{{ end }}) { 15 | this.optionsClass = optionsClass; 16 | this.fieldConfigClass = fieldConfigClass; 17 | {{ if .Data.Config.GenerateConverters -}} 18 | this.converter = converter; 19 | {{- end }} 20 | } 21 | 22 | public Class<?> getOptionsClass() { 23 | return optionsClass; 24 | } 25 | 26 | public Class<?> getFieldConfigClass() { 27 | return fieldConfigClass; 28 | } 29 | 30 | {{- if .Data.Config.GenerateConverters }} 31 | public Converter<Panel> getConverter() { 32 | return converter; 33 | } 34 | {{- end }} 35 | } 36 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboard_Panel_field_fieldConfig_custom_strict_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboard_Panel_field_fieldConfig_custom_strict_unmarshal" -}} 2 | {{- /* Non-standard unmarshalling to parse the correct FieldConfig struct based on the panel type */}} 3 | if err := json.Unmarshal(fields["fieldConfig"], &resource.FieldConfig); err != nil { 4 | errs = append(errs, cog.MakeBuildErrors("fieldConfig", err)...) 5 | } 6 | 7 | variantCfg, found := cog.ConfigForPanelcfgVariant(resource.Type) 8 | if found && variantCfg.StrictFieldConfigUnmarshaler != nil { 9 | fakeFieldConfigSource := struct { 10 | Defaults struct { 11 | Custom json.RawMessage `json:"custom"` 12 | } `json:"defaults"` 13 | }{} 14 | if err := json.Unmarshal(fields["fieldConfig"], &fakeFieldConfigSource); err != nil { 15 | errs = append(errs, cog.MakeBuildErrors("fieldConfig", err)...) 16 | } 17 | 18 | if fakeFieldConfigSource.Defaults.Custom != nil { 19 | customFieldConfig, err := variantCfg.StrictFieldConfigUnmarshaler(fakeFieldConfigSource.Defaults.Custom) 20 | if err != nil { 21 | errs = append(errs, cog.MakeBuildErrors("fieldConfig.defaults.custom", err)...) 22 | } else { 23 | resource.FieldConfig.Defaults.Custom = customFieldConfig 24 | } 25 | } 26 | } 27 | 28 | {{ end }} 29 | -------------------------------------------------------------------------------- /examples/java/custom-panel/src/main/java/custompanel/CustomPanelBuilder.java: -------------------------------------------------------------------------------- 1 | package custompanel; 2 | 3 | import com.grafana.foundation.cog.Builder; 4 | import com.grafana.foundation.cog.variants.Dataquery; 5 | import com.grafana.foundation.dashboard.Panel; 6 | 7 | public class CustomPanelBuilder implements Builder<Panel> { 8 | private final Panel internal; 9 | 10 | public CustomPanelBuilder() { 11 | this.internal = new Panel(); 12 | this.internal.type = "custom-panel"; 13 | } 14 | 15 | public CustomPanelBuilder title(String title) { 16 | this.internal.title = title; 17 | return this; 18 | } 19 | 20 | public CustomPanelBuilder withTarget(Builder<Dataquery> target) { 21 | this.internal.targets.add(target.build()); 22 | return this; 23 | } 24 | 25 | public CustomPanelBuilder makeItBeautiful() { 26 | if (this.internal.options == null) { 27 | this.internal.options = new CustomPanelOptions(); 28 | } 29 | 30 | CustomPanelOptions options = (CustomPanelOptions) this.internal.options; 31 | options.setMakeItBeautiful(true); 32 | 33 | this.internal.options = options; 34 | return this; 35 | } 36 | 37 | @Override 38 | public Panel build() { 39 | return internal; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/java/red-method/src/main/java/red/Common.java: -------------------------------------------------------------------------------- 1 | package red; 2 | 3 | import com.grafana.foundation.common.*; 4 | import com.grafana.foundation.timeseries.PanelBuilder; 5 | 6 | public class Common { 7 | 8 | static PanelBuilder defaultTimeSeries() { 9 | return new PanelBuilder(). 10 | lineWidth(1.0). 11 | fillOpacity(30.0). 12 | showPoints(VisibilityMode.NEVER). 13 | drawStyle(GraphDrawStyle.LINE). 14 | gradientMode(GraphGradientMode.OPACITY). 15 | spanNulls(BoolOrFloat64.createBool(false)). 16 | axisBorderShow(false). 17 | lineInterpolation(LineInterpolation.SMOOTH). 18 | legend(new VizLegendOptionsBuilder(). 19 | displayMode(LegendDisplayMode.LIST). 20 | placement(LegendPlacement.BOTTOM). 21 | showLegend(true) 22 | ). 23 | tooltip(new VizTooltipOptionsBuilder(). 24 | mode(TooltipDisplayMode.MULTI). 25 | sort(SortOrder.DESCENDING) 26 | ). 27 | thresholdsStyle(new GraphThresholdsStyleConfigBuilder(). 28 | mode(GraphThresholdsStyleMode.OFF) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.cog/veneers/dashboardv2/dashboardv2beta1.go.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: go 4 | 5 | package: dashboardv2beta1 6 | 7 | options: 8 | ############# 9 | # Variables # 10 | ############# 11 | 12 | - disjunction_as_options: 13 | by_builder: Dashboard.variable 14 | - rename: 15 | by_builder: Dashboard.QueryVariableKind 16 | as: queryVariable 17 | - rename: 18 | by_builder: Dashboard.TextVariableKind 19 | as: textVariable 20 | - rename: 21 | by_builder: Dashboard.ConstantVariableKind 22 | as: constantVariable 23 | - rename: 24 | by_builder: Dashboard.DatasourceVariableKind 25 | as: datasourceVariable 26 | - rename: 27 | by_builder: Dashboard.IntervalVariableKind 28 | as: intervalVariable 29 | - rename: 30 | by_builder: Dashboard.CustomVariableKind 31 | as: customVariable 32 | - rename: 33 | by_builder: Dashboard.GroupByVariableKind 34 | as: groupByVariable 35 | - rename: 36 | by_builder: Dashboard.AdhocVariableKind 37 | as: adhocVariable 38 | 39 | ########## 40 | # Panels # 41 | ########## 42 | 43 | # WithOverride(matcher, properties) instead of WithOverride(struct{...}) 44 | - struct_fields_as_arguments: 45 | by_name: VizConfigKind.override 46 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/api_reference_builder_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_builder_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Building a dashboard 5 | 6 | ```php 7 | use Grafana\Foundation\Common; 8 | use Grafana\Foundation\Dashboard\DashboardBuilder; 9 | use Grafana\Foundation\Dashboard\RowBuilder; 10 | use Grafana\Foundation\Prometheus; 11 | use Grafana\Foundation\Timeseries; 12 | 13 | require_once __DIR__.'/vendor/autoload.php'; 14 | 15 | $builder = (new DashboardBuilder(title: 'Sample dashboard')) 16 | ->uid('generated-from-php') 17 | ->tags(['generated', 'from', 'php']) 18 | ->refresh('1m') 19 | ->time('now-30m', 'now') 20 | ->timezone(Common\Constants::TIME_ZONE_BROWSER) 21 | ->withRow(new RowBuilder('Overview')) 22 | ->withPanel( 23 | (new Timeseries\PanelBuilder()) 24 | ->title('Network received') 25 | ->unit('bps') 26 | ->min(0) 27 | ->withTarget( 28 | (new Prometheus\DataqueryBuilder()) 29 | ->expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 30 | ->legendFormat({{ `'{{ device }}'` }}) 31 | ) 32 | ) 33 | ; 34 | 35 | echo(json_encode($builder->build(), JSON_PRETTY_PRINT).PHP_EOL); 36 | ``` 37 | {{ end }} 38 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/api_reference_builder_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_builder_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Building a dashboard 5 | 6 | ```python 7 | from grafana_foundation_sdk.builders.dashboard import Dashboard, Row 8 | from grafana_foundation_sdk.builders.prometheus import Dataquery as PrometheusQuery 9 | from grafana_foundation_sdk.builders.timeseries import Panel as Timeseries 10 | from grafana_foundation_sdk.models.common import TimeZoneBrowser 11 | 12 | def build_dashboard() -> Dashboard: 13 | return ( 14 | Dashboard("[TEST] Node Exporter / Raspberry") 15 | .uid("test-dashboard-raspberry") 16 | .tags(["generated", "raspberrypi-node-integration"]) 17 | 18 | .refresh("1m") 19 | .time("now-30m", "now") 20 | .timezone(TimeZoneBrowser) 21 | 22 | .with_row(Row("Overview")) 23 | .with_panel( 24 | Timeseries() 25 | .title("Network Received") 26 | .unit("bps") 27 | .min_val(0) 28 | .with_target( 29 | PrometheusQuery() 30 | .expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 31 | .legend_format({{ `"{{ device }}"` }}) 32 | ) 33 | ) 34 | ) 35 | ``` 36 | {{ end }} 37 | -------------------------------------------------------------------------------- /.cog/repository_templates/python/.github/workflows/python-release.tmpl: -------------------------------------------------------------------------------- 1 | name: Python Release 2 | on: 3 | push: 4 | branches: 5 | - '{{ .Extra.ReleaseBranch|replace "+" "\\+" }}' 6 | 7 | env: 8 | PYTHON_VERSION: '3.12' 9 | 10 | jobs: 11 | release: 12 | name: Build and release 13 | runs-on: ubuntu-latest 14 | 15 | permissions: 16 | contents: read 17 | id-token: write # mandatory for trusted publishing 18 | 19 | environment: 20 | name: pypi 21 | url: https://pypi.org/p/grafana_foundation_sdk 22 | 23 | defaults: 24 | run: 25 | shell: bash 26 | working-directory: ./python 27 | 28 | steps: 29 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 30 | with: 31 | persist-credentials: false 32 | 33 | - name: Setup Python {{ `${{ env.PYTHON_VERSION }}` }} 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: {{ `${{ env.PYTHON_VERSION }}` }} 37 | 38 | - name: Install pypa/build 39 | run: python3 -m pip install build --user 40 | 41 | - name: Build a binary wheel and a source tarball 42 | run: python3 -m build 43 | 44 | - name: Publish distribution 📦 to PyPI 45 | uses: pypa/gh-action-pypi-publish@release/v1 46 | with: 47 | packages-dir: python/dist/ 48 | attestations: false 49 | -------------------------------------------------------------------------------- /examples/go/red-method/common.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/grafana/grafana-foundation-sdk/go/cog" 5 | "github.com/grafana/grafana-foundation-sdk/go/common" 6 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 7 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 8 | ) 9 | 10 | func grafanaDatasourceRef() dashboard.DataSourceRef { 11 | return dashboard.DataSourceRef{ 12 | Uid: cog.ToPtr("grafana"), 13 | Type: cog.ToPtr("grafana"), 14 | } 15 | } 16 | 17 | func defaultTimeseries() *timeseries.PanelBuilder { 18 | return timeseries.NewPanelBuilder(). 19 | LineWidth(1). 20 | FillOpacity(30). 21 | ShowPoints(common.VisibilityModeNever). 22 | DrawStyle(common.GraphDrawStyleLine). 23 | GradientMode(common.GraphGradientModeOpacity). 24 | SpanNulls(common.BoolOrFloat64{Bool: cog.ToPtr[bool](false)}). 25 | AxisBorderShow(false). 26 | LineInterpolation(common.LineInterpolationSmooth). 27 | Legend(common.NewVizLegendOptionsBuilder(). 28 | DisplayMode(common.LegendDisplayModeList). 29 | Placement(common.LegendPlacementBottom). 30 | ShowLegend(true), 31 | ). 32 | Tooltip(common.NewVizTooltipOptionsBuilder(). 33 | Mode(common.TooltipDisplayModeMulti). 34 | Sort(common.SortOrderDescending), 35 | ). 36 | ThresholdsStyle( 37 | common.NewGraphThresholdsStyleConfigBuilder(). 38 | Mode(common.GraphThresholdsStyleModeOff), 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/src/Cog/PanelcfgConfig.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace {{ .Data.NamespaceRoot }}\Cog; 4 | {{- $convertReturn := "\\Grafana\\Foundation\\Dashboard\\Panel" -}} 5 | {{- if (objectExists "dashboardv2beta1" "VizConfigKind") -}} 6 | {{- $convertReturn = "mixed" -}} 7 | {{- end -}} 8 | 9 | final class PanelcfgConfig 10 | { 11 | public readonly string $identifier; 12 | 13 | /** 14 | * @var (callable({{ $convertReturn }}): string)|null 15 | */ 16 | public $convert; 17 | 18 | /** 19 | * @var (callable(array<string, mixed>): object)|null 20 | */ 21 | public $optionsFromArray; 22 | 23 | /** 24 | * @var (callable(array<string, mixed>): object)|null 25 | */ 26 | public $fieldConfigFromArray; 27 | 28 | /** 29 | * @param (callable({{ $convertReturn }}): string)|null $convert 30 | * @param (callable(array<string, mixed>): object)|null $optionsFromArray 31 | * @param (callable(array<string, mixed>): object)|null $fieldConfigFromArray 32 | */ 33 | public function __construct(string $identifier, ?callable $convert = null, ?callable $optionsFromArray = null, ?callable $fieldConfigFromArray = null) 34 | { 35 | $this->identifier = $identifier; 36 | $this->convert = $convert; 37 | $this->optionsFromArray = $optionsFromArray; 38 | $this->fieldConfigFromArray = $fieldConfigFromArray; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.cog/templates/python/extra/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "grafana_foundation_sdk" 7 | description = "A set of tools, types and libraries for building and manipulating Grafana objects." 8 | keywords = [ 9 | "observability", 10 | "sdk", 11 | "grafana", 12 | "logs", 13 | "traces", 14 | "metrics" 15 | ] 16 | version = "{{ if eq .Extra.GrafanaVersion "next" }}{{ .Extra.BuildTimestamp }}{{ else }}{{ .Extra.BuildTimestamp }}!{{ .Extra.GrafanaVersion|registryToSemver }}{{ end }}" 17 | dependencies = [] 18 | requires-python = ">=3.11" 19 | classifiers = [ 20 | "Development Status :: 3 - Alpha", 21 | "Intended Audience :: Developers", 22 | "Intended Audience :: System Administrators", 23 | "License :: OSI Approved :: Apache Software License", 24 | "Operating System :: OS Independent", 25 | "Programming Language :: Python :: 3.11", 26 | "Programming Language :: Python :: 3.12", 27 | "Topic :: Software Development :: Libraries", 28 | "Topic :: System :: Monitoring", 29 | ] 30 | readme = "README.md" 31 | authors = [ 32 | { name = "Grafana Labs" }, 33 | ] 34 | 35 | [project.urls] 36 | Homepage = "https://github.com/grafana/grafana-foundation-sdk" 37 | Repository = "https://github.com/grafana/grafana-foundation-sdk.git" 38 | Issues = "https://github.com/grafana/grafana-foundation-sdk/issues" 39 | -------------------------------------------------------------------------------- /.cog/compiler/dashboardv2beta1.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/compiler_passes.json 2 | 3 | passes: 4 | ###################### 5 | # Dashboard v2beta1 # 6 | ###################### 7 | 8 | # Some future-proofing 9 | - constant_to_enum: 10 | objects: 11 | - dashboardv2beta1.RepeatMode 12 | 13 | # To make our composability veneers shenanigans work 14 | - retype_field: 15 | field: dashboardv2beta1.VizConfigSpec.options 16 | as: 17 | kind: scalar 18 | scalar: { scalar_kind: any } 19 | nullable: true 20 | 21 | - retype_field: 22 | field: dashboardv2beta1.FieldConfig.custom 23 | as: 24 | kind: scalar 25 | scalar: { scalar_kind: any } 26 | nullable: true 27 | 28 | - retype_field: 29 | field: dashboardv2beta1.DataQueryKind.spec 30 | as: 31 | kind: scalar 32 | scalar: { scalar_kind: any } 33 | nullable: true 34 | 35 | # Is this object meant for frontend usage only? 36 | - omit: 37 | objects: [ dashboardv2beta1.VariableCustomFormatterFn ] 38 | 39 | - retype_field: 40 | field: dashboardv2beta1.CustomVariableValue.formatter 41 | as: 42 | kind: scalar 43 | scalar: { scalar_kind: string } 44 | nullable: true 45 | 46 | - rename_object: 47 | from: dashboardv2beta1.DashboardSpec 48 | to: Dashboard 49 | -------------------------------------------------------------------------------- /.cog/veneers/dashboardv2/dashboardv2beta1.java.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/veneers.json 2 | 3 | language: java 4 | 5 | package: dashboardv2beta1 6 | 7 | options: 8 | ############# 9 | # Variables # 10 | ############# 11 | 12 | - disjunction_as_options: 13 | by_builder: Dashboard.variable 14 | - rename: 15 | by_builder: Dashboard.queryVariableKind 16 | as: queryVariable 17 | - rename: 18 | by_builder: Dashboard.textVariableKind 19 | as: textVariable 20 | - rename: 21 | by_builder: Dashboard.constantVariableKind 22 | as: constantVariable 23 | - rename: 24 | by_builder: Dashboard.datasourceVariableKind 25 | as: datasourceVariable 26 | - rename: 27 | by_builder: Dashboard.intervalVariableKind 28 | as: intervalVariable 29 | - rename: 30 | by_builder: Dashboard.customVariableKind 31 | as: customVariable 32 | - rename: 33 | by_builder: Dashboard.groupByVariableKind 34 | as: groupByVariable 35 | - rename: 36 | by_builder: Dashboard.adhocVariableKind 37 | as: adhocVariable 38 | 39 | - disjunction_as_options: 40 | by_builder: QueryVariable.query 41 | 42 | ########## 43 | # Panels # 44 | ########## 45 | 46 | # WithOverride(matcher, properties) instead of WithOverride(struct{...}) 47 | - struct_fields_as_arguments: 48 | by_name: VizConfigKind.override 49 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_dashboardv2beta1_DataQueryKind_custom_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_dashboardv2beta1_DataQueryKind_custom_unmarshal" }} 2 | {{- $fmt := importStdPkg "fmt" -}} 3 | {{- $json := importStdPkg "encoding/json" -}} 4 | func (resource *{{ .Object.Name|upperCamelCase }}) UnmarshalJSON(raw []byte) error { 5 | if raw == nil { 6 | return nil 7 | } 8 | 9 | fields := make(map[string]json.RawMessage) 10 | if err := json.Unmarshal(raw, &fields); err != nil { 11 | return err 12 | } 13 | {{- range $field := .Object.Type.Struct.Fields }} 14 | {{- if eq $field.Name "spec" }} 15 | {{ continue }} 16 | {{- else }} 17 | if fields["{{ $field.Name }}"] != nil { 18 | if err := json.Unmarshal(fields["{{ $field.Name }}"], &resource.{{ $field.Name|upperCamelCase }}); err != nil { 19 | return fmt.Errorf("error decoding field '{{ $field.Name }}': %w", err) 20 | } 21 | } 22 | {{- end }} 23 | {{- end }} 24 | 25 | {{ template "unmarshal_dashboardv2beta1_DataQueryKind_spec" }} 26 | 27 | return nil 28 | } 29 | 30 | {{ end }} 31 | 32 | {{- define "unmarshal_dashboardv2beta1_DataQueryKind_spec" -}} 33 | {{- $fmt := importStdPkg "fmt" -}} 34 | {{- $cog := importPkg "cog" }} 35 | if fields["spec"] != nil { 36 | dataquery, err := cog.UnmarshalDataquery(fields["spec"], resource.Group) 37 | if err != nil { 38 | return fmt.Errorf("error decoding field 'spec': %w", err) 39 | } 40 | resource.Spec = dataquery 41 | } 42 | {{- end -}} 43 | -------------------------------------------------------------------------------- /.cog/repository_templates/typescript/.github/workflows/typescript-release.tmpl: -------------------------------------------------------------------------------- 1 | name: TypeScript Release 2 | on: 3 | push: 4 | branches: 5 | - '{{ .Extra.ReleaseBranch|replace "+" "\\+" }}' 6 | 7 | env: 8 | NODE_VERSION: '20' 9 | 10 | jobs: 11 | release: 12 | name: Build and release 13 | runs-on: ubuntu-latest 14 | 15 | permissions: 16 | contents: read 17 | id-token: write 18 | 19 | environment: 20 | name: typescript 21 | url: https://www.npmjs.com/package/@grafana/grafana-foundation-sdk 22 | 23 | defaults: 24 | run: 25 | shell: bash 26 | working-directory: ./typescript 27 | 28 | steps: 29 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 30 | with: 31 | persist-credentials: false 32 | 33 | - name: Use Node.js {{ `${{ env.NODE_VERSION }}` }} 34 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 35 | with: 36 | node-version: {{ `${{ env.NODE_VERSION }}` }} 37 | scope: '@grafana' 38 | registry-url: 'https://registry.npmjs.org' 39 | 40 | - name: Ensure npm latest version 41 | run: npm install -g npm@latest 42 | 43 | - name: Install dependencies 44 | run: npm install 45 | 46 | - name: Build 47 | run: npm run build 48 | 49 | - name: Publish to NPM registry 50 | run: npm publish --access public --tag {{ .Extra.TypescriptTag }} 51 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/src/Cog/DataqueryConfig.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace {{ .Data.NamespaceRoot }}\Cog; 4 | 5 | final class DataqueryConfig 6 | { 7 | public readonly string $identifier; 8 | 9 | /** 10 | * @var callable(array<string, mixed>): Dataquery 11 | */ 12 | public $fromArray; 13 | 14 | /** 15 | * @var (callable(Dataquery): string)|null 16 | */ 17 | public $convert; 18 | 19 | {{- if objectExists "dashboardv2beta1" "DataQueryKind" }} 20 | /** 21 | * @var (callable(\Grafana\Foundation\Dashboardv2beta1\DataQueryKind): string)|null 22 | */ 23 | public $convertv2; 24 | {{- end }} 25 | 26 | /** 27 | * @param callable(array<string, mixed>): Dataquery $fromArray 28 | * @param (callable(Dataquery): string)|null $convert 29 | {{- if objectExists "dashboardv2beta1" "DataQueryKind" }} 30 | * @param (callable(\Grafana\Foundation\Dashboardv2beta1\DataQueryKind): string)|null $convertv2 31 | {{- end }} 32 | */ 33 | public function __construct(string $identifier, callable $fromArray, ?callable $convert = null{{ if objectExists "dashboardv2beta1" "DataQueryKind" }}, ?callable $convertv2 = null{{ end }}) 34 | { 35 | $this->identifier = $identifier; 36 | $this->fromArray = $fromArray; 37 | $this->convert = $convert; 38 | {{- if objectExists "dashboardv2beta1" "DataQueryKind" }} 39 | $this->convertv2 = $convertv2; 40 | {{- end }} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/java/linux-node-overview/src/main/java/linuxnode/linux/Network.java: -------------------------------------------------------------------------------- 1 | package linuxnode.linux; 2 | 3 | import com.grafana.foundation.timeseries.PanelBuilder; 4 | import com.grafana.foundation.units.Constants; 5 | 6 | public class Network { 7 | 8 | public static PanelBuilder networkReceivedTimeseries() { 9 | return Common.defaultTimeSeries(). 10 | title("Network Received"). 11 | description("Network received (bits/s)"). 12 | min(0.0). 13 | unit(Constants.BitsPerSecondSI). 14 | fillOpacity(0.0). 15 | withTarget( 16 | Common.basicPrometheusQuery("rate(node_network_receive_bytes_total{job=\"integrations/raspberrypi-node\", instance=\"$instance\", device!=\"lo\"}[$__rate_interval]) * 8", "{{ device }}") 17 | ); 18 | } 19 | public static PanelBuilder networkTransmittedTimeseries() { 20 | return Common.defaultTimeSeries(). 21 | title("Network Transmitted"). 22 | description("Network transmitted (bits/s)"). 23 | min(0.0). 24 | unit(Constants.BitsPerSecondSI). 25 | fillOpacity(0.0). 26 | withTarget( 27 | Common.basicPrometheusQuery("rate(node_network_transmit_bytes_total{job=\"integrations/raspberrypi-node\", instance=\"$instance\", device!=\"lo\"}[$__rate_interval]) * 8", "{{ device }}") 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/python/grafana-agent-overview/src/discovery.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.builders import timeseries 2 | from grafana_foundation_sdk.models.dashboard import DataSourceRef 3 | from grafana_foundation_sdk.models import units 4 | 5 | from .common import prometheus_query, default_timeseries 6 | 7 | 8 | def target_sync_timeseries() -> timeseries.Panel: 9 | return ( 10 | default_timeseries() 11 | .title("Target Sync") 12 | .description("Actual interval to sync the scrape pool.") 13 | .datasource(DataSourceRef(uid="$prometheus_datasource")) 14 | .unit(units.Seconds) 15 | .with_target( 16 | prometheus_query( 17 | query='sum(rate(prometheus_target_sync_length_seconds_sum{job=~"$job", instance=~"$instance"}[$__rate_interval])) by (instance, scrape_job)', 18 | legend="{{instance}}/{{scrape_job}}", 19 | ) 20 | ) 21 | ) 22 | 23 | 24 | def target_timeseries() -> timeseries.Panel: 25 | return ( 26 | default_timeseries() 27 | .title("Targets") 28 | .description("Discovered targets by prometheus service discovery.") 29 | .datasource(DataSourceRef(uid="$prometheus_datasource")) 30 | .unit(units.Short) 31 | .with_target( 32 | prometheus_query( 33 | query='sum by (instance) (prometheus_sd_discovered_targets{job=~"$job", instance=~"$instance"})', 34 | legend="{{instance}}", 35 | ) 36 | ) 37 | ) 38 | -------------------------------------------------------------------------------- /examples/typescript/grafana-agent-overview/src/overview.ts: -------------------------------------------------------------------------------- 1 | import * as common from '@grafana/grafana-foundation-sdk/common'; 2 | import * as table from "@grafana/grafana-foundation-sdk/table"; 3 | import { tablePrometheusQuery } from "./common"; 4 | 5 | export const runningInstancesTable = (): table.PanelBuilder => { 6 | return new table.PanelBuilder() 7 | .title("Running Instances") 8 | .description("General statistics of running grafana agent instances.") 9 | .height(7) 10 | .span(24) 11 | .footer( 12 | new common.TableFooterOptionsBuilder() 13 | .countRows(false) 14 | .reducer(["sum"]) 15 | ) 16 | .datasource({ uid: "$prometheus_datasource" }) 17 | .withTarget( 18 | tablePrometheusQuery('count by (instance, version) (agent_build_info{job=~"$job", instance=~"$instance"})', "A"), 19 | ) 20 | .withTarget( 21 | tablePrometheusQuery('max by (instance) (time() - process_start_time_seconds{job=~"$job", instance=~"$instance"})', "B"), 22 | ) 23 | // Transformations 24 | .withTransformation({ 25 | id: "merge", 26 | options: {} 27 | }) 28 | .withTransformation({ 29 | id: "organize", 30 | options: { 31 | excludeByName: { 32 | "Time": true, 33 | "Value #A": true, 34 | }, 35 | "renameByName": { 36 | "Value #B": "Uptime", 37 | }, 38 | }, 39 | }) 40 | // Overrides 41 | .overrideByName("Value #B", [ 42 | { id: "unit", value: "s" }, 43 | ]); 44 | }; 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/0-bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: New bug report 2 | description: Report a new bug 3 | title: "[Bug]: <title>" 4 | labels: [bug] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to fill out this bug report! 11 | 12 | This is helpful for us to improve the library and make it better :smile: 13 | 14 | - type: markdown 15 | attributes: 16 | value: | 17 | # 18 | 19 | - type: dropdown 20 | id: version 21 | attributes: 22 | label: version 23 | description: What's version are you using? 24 | options: 25 | - 11.3.x 26 | - 11.2.x 27 | - 11.1.x 28 | - 11.0.x 29 | - 10.4.x 30 | - 10.3.x 31 | - 10.2.x 32 | - 10.1.x 33 | default: 0 34 | validations: 35 | required: true 36 | 37 | - type: checkboxes 38 | id: language 39 | attributes: 40 | label: language 41 | description: What language(s) are you using? 42 | options: 43 | - label: Go 44 | - label: Java 45 | - label: PHP 46 | - label: Python 47 | - label: Typescript 48 | 49 | - type: textarea 50 | attributes: 51 | label: Describe your bug 52 | description: Put code examples could be helpful for us. 53 | validations: 54 | required: true 55 | 56 | - type: textarea 57 | attributes: 58 | label: What did you expect to happen? 59 | validations: 60 | required: true 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-missing-fields.yaml: -------------------------------------------------------------------------------- 1 | name: Missing fields 2 | description: Report a missing field/builder in any model. 3 | title: "[Schemas]: Missing <field> field for <model>" 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Code generated is based on schemas and they could miss some fields. 10 | - type: markdown 11 | attributes: 12 | value: | 13 | # 14 | 15 | - type: dropdown 16 | id: version 17 | attributes: 18 | label: version 19 | description: What's version are you using? 20 | options: 21 | - 11.3.x 22 | - 11.2.x 23 | - 11.1.x 24 | - 11.0.x 25 | - 10.4.x 26 | - 10.3.x 27 | - 10.2.x 28 | - 10.1.x 29 | default: 0 30 | validations: 31 | required: true 32 | 33 | - type: input 34 | id: schema 35 | attributes: 36 | label: Missing field. 37 | description: In which model is the field missing? 38 | placeholder: "Example: Missing abc field in Dashboard" 39 | validations: 40 | required: true 41 | 42 | - type: input 43 | id: documentation 44 | attributes: 45 | label: Link to documentation. 46 | description: Link the documentation where is the field defined. 47 | placeholder: "Example: https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/view-dashboard-json-model/#panel-size-and-position" 48 | 49 | - type: textarea 50 | attributes: 51 | label: Do you want to add any other information? 52 | 53 | -------------------------------------------------------------------------------- /examples/php/custom-query/src/Custom/Query.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace App\Custom; 4 | 5 | use Grafana\Foundation\Cog; 6 | 7 | class Query implements \JsonSerializable, Cog\Dataquery 8 | { 9 | // RefId and Hide are expected on all queries 10 | public string $refId; 11 | public ?bool $hide; 12 | 13 | // Expr is specific to the Query type 14 | public string $expr; 15 | 16 | /** 17 | * @param string|null $expr 18 | * @param string|null $refId 19 | * @param bool|null $hide 20 | */ 21 | public function __construct(?string $expr = null, ?string $refId = null, ?bool $hide = null) 22 | { 23 | $this->expr = $expr ?: ""; 24 | $this->refId = $refId ?: ""; 25 | $this->hide = $hide; 26 | } 27 | 28 | /** 29 | * @param array{expr?: string, refId?: string, hide?: bool} $data 30 | */ 31 | public static function fromArray(array $data): self 32 | { 33 | return new self( 34 | expr: $data["expr"] ?? null, 35 | refId: $data["refId"] ?? null, 36 | hide: $data["hide"] ?? null, 37 | ); 38 | } 39 | 40 | public function jsonSerialize(): array 41 | { 42 | $data = [ 43 | "expr" => $this->expr, 44 | "refId" => $this->refId, 45 | ]; 46 | if (isset($this->hide)) { 47 | $data["hide"] = $this->hide; 48 | } 49 | return $data; 50 | } 51 | 52 | public function dataqueryType(): string 53 | { 54 | return "custom"; 55 | } 56 | } -------------------------------------------------------------------------------- /examples/php/custom-query/src/Custom/QueryBuilder.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace App\Custom; 4 | 5 | use Grafana\Foundation\Cog; 6 | 7 | /** 8 | * @implements Cog\Builder<Query> 9 | */ 10 | class QueryBuilder implements Cog\Builder 11 | { 12 | protected Query $internal; 13 | 14 | public function __construct(string $query) 15 | { 16 | $this->internal = new Query(expr: $query); 17 | } 18 | 19 | /** 20 | * @return Query 21 | */ 22 | public function build() 23 | { 24 | return $this->internal; 25 | } 26 | 27 | /** 28 | * The actual expression/query that will be evaluated by Prometheus 29 | */ 30 | public function expr(string $expr): static 31 | { 32 | $this->internal->expr = $expr; 33 | return $this; 34 | } 35 | 36 | /** 37 | * A unique identifier for the query within the list of targets. 38 | * In server side expressions, the refId is used as a variable name to identify results. 39 | * By default, the UI will assign A->Z; however setting meaningful names may be useful. 40 | */ 41 | public function refId(string $refId): static 42 | { 43 | $this->internal->refId = $refId; 44 | return $this; 45 | } 46 | 47 | /** 48 | * If hide is set to true, Grafana will filter out the response(s) associated with this query before returning it to the panel. 49 | */ 50 | public function hide(bool $hide): static 51 | { 52 | $this->internal->hide = $hide; 53 | return $this; 54 | } 55 | } -------------------------------------------------------------------------------- /.cog/templates/java/overrides/assigment_Dashboard_WithPanel.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withPanel" }} 2 | 3 | if (panelOrRowPanel.panel.gridPos == null) { 4 | panelOrRowPanel.panel.gridPos = new GridPos(); 5 | } 6 | if (panelOrRowPanel.panel.gridPos.x == null) { 7 | panelOrRowPanel.panel.gridPos.x = 0; 8 | } 9 | if (panelOrRowPanel.panel.gridPos.y == null) { 10 | panelOrRowPanel.panel.gridPos.y = 0; 11 | } 12 | if (panelOrRowPanel.panel.gridPos.w == null) { 13 | panelOrRowPanel.panel.gridPos.w = 0; 14 | } 15 | if (panelOrRowPanel.panel.gridPos.h == null) { 16 | panelOrRowPanel.panel.gridPos.h = 0; 17 | } 18 | // The panel either has no position set, or it is the first panel of the dashboard. 19 | // In that case, we position it on the grid 20 | if (panelOrRowPanel.panel.gridPos.x == 0 && panelOrRowPanel.panel.gridPos.y == 0) { 21 | panelOrRowPanel.panel.gridPos.x = this.currentX; 22 | panelOrRowPanel.panel.gridPos.y = this.currentY; 23 | } 24 | {{- end }} 25 | 26 | {{- define "post_assignment_Dashboard_withPanel" }} 27 | 28 | // Prepare the coordinates for the next panel 29 | this.currentX += panelOrRowPanel.panel.gridPos.w; 30 | this.lastPanelHeight = java.lang.Math.max(this.lastPanelHeight, panelOrRowPanel.panel.gridPos.h); 31 | 32 | // Check for grid width overflow? 33 | if (this.currentX >= 24) { 34 | this.currentX = 0; 35 | this.currentY += this.lastPanelHeight; 36 | this.lastPanelHeight = 0; 37 | } 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /examples/typescript/alert-rule/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | QueryBuilder, 3 | RuleBuilder, 4 | RuleGroupBuilder, 5 | } from '@grafana/grafana-foundation-sdk/alerting'; 6 | 7 | import { DataqueryBuilder} from '@grafana/grafana-foundation-sdk/prometheus'; 8 | import { TypeThresholdBuilder } from '@grafana/grafana-foundation-sdk/expr'; 9 | 10 | const datasourceUid = 'DS_PROMETHEUS_UID'; 11 | const ruleGroupUid = '1m'; 12 | const folderUid = 'folder-foo-uid'; 13 | 14 | const queryA = new QueryBuilder('A') 15 | .datasourceUid(datasourceUid) 16 | .relativeTimeRange({ from: 600, to: 0 }) 17 | .model( 18 | new DataqueryBuilder() 19 | .expr('vector(1)') 20 | .instant() 21 | .refId('A') 22 | ); 23 | 24 | const queryB = new QueryBuilder('B') 25 | .datasourceUid('__expr__') 26 | .model( 27 | new TypeThresholdBuilder().conditions([{ 28 | evaluator: { type: 'gt', params: [0] }, 29 | }, 30 | ]) 31 | .datasource({ uid: '__expr__', type: '__expr__' }) 32 | .expression('A') 33 | .refId('B') 34 | ); 35 | 36 | const ruleBuilder = new RuleBuilder('[Example] Rule') 37 | .queries([queryA, queryB]) 38 | .condition('B') 39 | .forDuration('0m') 40 | .ruleGroup(ruleGroupUid) 41 | .folderUID(folderUid) 42 | .labels({ severity: 'critical' }) 43 | .annotations({ summary: 'Demo rule' }); 44 | 45 | const ruleGroupBuilder = new RuleGroupBuilder(ruleGroupUid) 46 | .interval(60) 47 | .folderUid(folderUid) 48 | .rules([ruleBuilder]); 49 | console.log(JSON.stringify(ruleGroupBuilder.build(), null, 2)); 50 | 51 | -------------------------------------------------------------------------------- /.cog/templates/python/extra/docs/How-To/building-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Building a dashboard 2 | 3 | ```python 4 | from grafana_foundation_sdk.builders.dashboard import Dashboard, Row 5 | from grafana_foundation_sdk.builders import prometheus, timeseries 6 | from grafana_foundation_sdk.cog.encoder import JSONEncoder 7 | from grafana_foundation_sdk.models.common import TimeZoneBrowser 8 | from grafana_foundation_sdk.models import units 9 | 10 | 11 | def build_dashboard() -> Dashboard: 12 | builder = ( 13 | Dashboard("[TEST] Node Exporter / Raspberry") 14 | .uid("test-dashboard-raspberry") 15 | .tags(["generated", "raspberrypi-node-integration"]) 16 | .refresh("1m") 17 | .time("now-30m", "now") 18 | .timezone(TimeZoneBrowser) 19 | .with_row(Row("Overview")) 20 | .with_panel( 21 | timeseries.Panel() 22 | .title("Network Received") 23 | .unit(units.BitsPerSecondSI) 24 | .min(0) 25 | .with_target( 26 | prometheus.Dataquery() 27 | .expr( 28 | 'rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8' 29 | ) 30 | .legend_format({{ `"{{ device }}"` }}) 31 | ) 32 | ) 33 | ) 34 | 35 | return builder 36 | 37 | 38 | if __name__ == "__main__": 39 | dashboard = build_dashboard().build() 40 | encoder = JSONEncoder(sort_keys=True, indent=2) 41 | 42 | print(encoder.encode(dashboard)) 43 | ``` 44 | -------------------------------------------------------------------------------- /.cog/templates/typescript/overrides/assignment_Dashboard_withRow.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withRow" }} 2 | 3 | // Position the row on the grid 4 | if (!rowPanelResource.gridPos || (rowPanelResource.gridPos.x == 0 && rowPanelResource.gridPos.y == 0)) { 5 | rowPanelResource.gridPos = { 6 | x: 0, // beginning of the line 7 | y: this.currentY + this.lastPanelHeight, 8 | 9 | h: 1, 10 | w: 24, // full width 11 | }; 12 | } 13 | {{- end }} 14 | 15 | {{- define "post_assignment_Dashboard_withRow" }} 16 | 17 | // Reset the state for the next row 18 | this.currentX = 0; 19 | this.currentY = rowPanelResource.gridPos.y + 1; 20 | this.lastPanelHeight = 0; 21 | 22 | // Position the row's panels on the grid 23 | rowPanelResource.panels.forEach(panel => { 24 | if (!panel.gridPos) { 25 | panel.gridPos = dashboard.defaultGridPos(); 26 | } 27 | 28 | // The panel either has no position set, or it is the first panel of the dashboard. 29 | // In that case, we position it on the grid 30 | if (panel.gridPos.x == 0 && panel.gridPos.y == 0) { 31 | panel.gridPos.x = this.currentX; 32 | panel.gridPos.y = this.currentY; 33 | } 34 | 35 | // Prepare the coordinates for the next panel 36 | this.currentX += panel.gridPos.w; 37 | this.lastPanelHeight = Math.max(this.lastPanelHeight, panel.gridPos.h); 38 | 39 | // Check for grid width overflow? 40 | if (this.currentX >= 24) { 41 | this.currentX = 0; 42 | this.currentY += this.lastPanelHeight; 43 | this.lastPanelHeight = 0; 44 | } 45 | }); 46 | {{- end }} 47 | -------------------------------------------------------------------------------- /examples/java/grafana-agent-overview/src/main/java/agent/Discovery.java: -------------------------------------------------------------------------------- 1 | package agent; 2 | 3 | import com.grafana.foundation.timeseries.PanelBuilder; 4 | import com.grafana.foundation.units.Constants; 5 | 6 | import static agent.Common.*; 7 | 8 | public class Discovery { 9 | static PanelBuilder targetSyncTimeSeries() { 10 | return defaultTimeSeries(). 11 | title("Target Sync"). 12 | description("Actual interval to sync the scrape pool."). 13 | datasource(datasourceRef()). 14 | unit(Constants.Seconds). 15 | withTarget( 16 | prometheusQuery( 17 | "sum(rate(prometheus_target_sync_length_seconds_sum{job=~\"$job\", instance=~\"$instance\"}[$__rate_interval])) by (instance, scrape_job)", 18 | "{{instance}}/{{scrape_job}}" 19 | ) 20 | ); 21 | } 22 | 23 | static PanelBuilder targetsTimeSeries() { 24 | return defaultTimeSeries(). 25 | title("Targets"). 26 | description("Discovered targets by prometheus service discovery."). 27 | datasource(datasourceRef()). 28 | unit(Constants.Short). 29 | withTarget( 30 | prometheusQuery( 31 | "sum by (instance) (prometheus_sd_discovered_targets{job=~\"$job\", instance=~\"$instance\"})", 32 | "{{instance}}" 33 | ) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.cog/templates/python/overrides/assignment_Dashboard_withRow.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withRow" }} 2 | 3 | # Position the row on the grid 4 | if row_panel_resource.grid_pos is None or (row_panel_resource.grid_pos.x == 0 and row_panel_resource.grid_pos.y == 0): 5 | row_panel_resource.grid_pos = dashboard.GridPos( 6 | x=0, 7 | y=self.__current_y + self.__last_panel_height, 8 | h=1, 9 | w=24, 10 | ) 11 | {{- end }} 12 | 13 | {{- define "post_assignment_Dashboard_withRow" }} 14 | 15 | # Reset the state for the next row 16 | self.__current_x = 0 17 | self.__current_y = row_panel_resource.grid_pos.y + 1 18 | self.__last_panel_height = 0 19 | 20 | # Position the row's panels on the grid 21 | for panel in row_panel_resource.panels: 22 | # Position the panel on the grid 23 | if panel.grid_pos is None: 24 | panel.grid_pos = dashboard.GridPos() 25 | 26 | # The panel either has no position set, or it is the first panel of the dashboard. 27 | # In that case, we position it on the grid 28 | if panel.grid_pos.x == 0 and panel.grid_pos.y == 0: 29 | panel.grid_pos.x = self.__current_x 30 | panel.grid_pos.y = self.__current_y 31 | 32 | # Prepare the coordinates for the next panel 33 | self.__current_x += panel.grid_pos.w 34 | self.__last_panel_height = max(self.__last_panel_height, panel.grid_pos.h) 35 | 36 | # Check for grid width overflow? 37 | if self.__current_x >= 24: 38 | self.__current_x = 0 39 | self.__current_y += self.__last_panel_height 40 | self.__last_panel_height = 0 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /.cog/templates/go/extra/docs/How-To/building-a-dashboard.md: -------------------------------------------------------------------------------- 1 | # Building a dashboard 2 | 3 | ```go 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | 10 | "github.com/grafana/grafana-foundation-sdk/go/common" 11 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 12 | "github.com/grafana/grafana-foundation-sdk/go/prometheus" 13 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 14 | ) 15 | 16 | func main() { 17 | builder := dashboard.NewDashboardBuilder("Sample dashboard"). 18 | Uid("generated-from-go"). 19 | Tags([]string{"generated", "from", "go"}). 20 | Refresh("1m"). 21 | Time("now-30m", "now"). 22 | Timezone(common.TimeZoneBrowser). 23 | WithRow(dashboard.NewRowBuilder("Overview")). 24 | WithPanel( 25 | timeseries.NewPanelBuilder(). 26 | Title("Network Received"). 27 | Unit("bps"). 28 | Min(0). 29 | WithTarget( 30 | prometheus.NewDataqueryBuilder(). 31 | Expr(`rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8`). 32 | LegendFormat({{ `"{{ device }}"` }}), 33 | ), 34 | ) 35 | 36 | sampleDashboard, err := builder.Build() 37 | if err != nil { 38 | panic(err) 39 | } 40 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | fmt.Println(string(dashboardJson)) 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/api_reference_object_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_object_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Marshalling to JSON 5 | 6 | ```go 7 | package main 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | "os" 13 | 14 | "github.com/grafana/grafana-foundation-sdk/go/cog" 15 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 16 | ) 17 | 18 | func main() { 19 | sampleDashboard := &dashboard.Dashboard{ 20 | Uid: cog.ToPtr("sample-dashboard-uid"), 21 | Title: cog.ToPtr("Sample dashboard"), 22 | } 23 | 24 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | fmt.Println(string(dashboardJson)) 30 | } 31 | ``` 32 | 33 | ### Unmarshalling from JSON 34 | 35 | ```go 36 | package main 37 | 38 | import ( 39 | "encoding/json" 40 | "fmt" 41 | "os" 42 | 43 | "github.com/grafana/grafana-foundation-sdk/go/cog/plugins" 44 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 45 | ) 46 | 47 | func main() { 48 | // Required to correctly unmarshal panels and dataqueries 49 | plugins.RegisterDefaultPlugins() 50 | 51 | dashboardJSON, err := os.ReadFile("dashboard.json") 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | sampleDashboard := &dashboard.Dashboard{} 57 | if err := json.Unmarshal(dashboardJSON, sampleDashboard); err != nil { 58 | panic(fmt.Sprintf("%s", err)) 59 | } 60 | 61 | fmt.Printf("%#v\n", sampleDashboard) 62 | } 63 | ``` 64 | {{ end }} 65 | -------------------------------------------------------------------------------- /examples/go/grafana-agent-overview/overview.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/grafana/grafana-foundation-sdk/go/common" 5 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 6 | "github.com/grafana/grafana-foundation-sdk/go/table" 7 | "github.com/grafana/grafana-foundation-sdk/go/units" 8 | ) 9 | 10 | func runningInstancesTable() *table.PanelBuilder { 11 | return table.NewPanelBuilder(). 12 | Title("Running Instances"). 13 | Description("General statistics of running grafana agent instances."). 14 | Height(7). 15 | Span(24). 16 | Footer(common.NewTableFooterOptionsBuilder().CountRows(false).Reducer([]string{"sum"})). 17 | Datasource(datasourceRef("$prometheus_datasource")). 18 | WithTarget( 19 | tablePrometheusQuery(`count by (instance, version) (agent_build_info{job=~"$job", instance=~"$instance"})`, "A"), 20 | ). 21 | WithTarget( 22 | tablePrometheusQuery(`max by (instance) (time() - process_start_time_seconds{job=~"$job", instance=~"$instance"})`, "B"), 23 | ). 24 | // Transformations 25 | WithTransformation(dashboard.DataTransformerConfig{ 26 | Id: "merge", 27 | Options: map[string]any{}, 28 | }). 29 | WithTransformation(dashboard.DataTransformerConfig{ 30 | Id: "organize", 31 | Options: map[string]any{ 32 | "excludeByName": map[string]any{ 33 | "Time": true, 34 | "Value #A": true, 35 | }, 36 | "renameByName": map[string]any{ 37 | "Value #B": "Uptime", 38 | }, 39 | }, 40 | }). 41 | // Overrides 42 | OverrideByName("Value #B", []dashboard.DynamicConfigValue{ 43 | {Id: "unit", Value: units.Seconds}, 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /.cog/repository_templates/common/README.md: -------------------------------------------------------------------------------- 1 | # Grafana Foundation SDK 2 | 3 | A set of foundational tools meant to be used to manipulate Grafana resources 4 | as-code. 5 | 6 | **_Raw types_** and **_builder libraries_** are provided for a range 7 | of versions of Grafana, in the following languages: 8 | 9 | * Go 10 | * Java 11 | * PHP 12 | * Python 13 | * Typescript 14 | 15 | > [!NOTE] 16 | > The content of this repository is generated by [`cog`][cog] from 17 | > [schemas exposed by Grafana][kind-registry]. 18 | 19 | > [!TIP] 20 | > This branch contains **types and builders generated for Grafana `{{ .Extra.GrafanaVersion }}`.** 21 | 22 | ## Navigating the SDK 23 | 24 | The following table can be used to select a version of the SDK suitable for 25 | your Grafana instance. 26 | 27 | | Grafana Version | `cog` Version | Branch | 28 | | ------------------------------ | ------------- | ------ | 29 | {{- range $version := (split ";" .Extra.AllGrafanaVersions) }} 30 | | `{{ $version }}`{{ if eq $version "next" }} (Grafana's main branch){{ end }} | `v0.0.x` | [{{ $version }}+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/{{ $version }}%2Bcog-v0.0.x) | 31 | {{- end }} 32 | 33 | ## Maturity 34 | 35 | The code in this repository should be considered as "public preview". While it is used by Grafana Labs in production, it still is under active development. 36 | 37 | > [!NOTE] 38 | > Bugs and issues are handled solely by Engineering teams. On-call support or SLAs are not available. 39 | 40 | ## License 41 | 42 | [Apache 2.0 License](./LICENSE) 43 | 44 | [cog]: <https://github.com/grafana/cog> 45 | [kind-registry]: <https://github.com/grafana/kind-registry> 46 | -------------------------------------------------------------------------------- /examples/go/grafana-openapi-client-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/grafana/grafana-foundation-sdk/examples/go/grafana-openapi-client-go 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/go-openapi/strfmt v0.23.0 7 | github.com/grafana/grafana-foundation-sdk/go v0.0.0-20250501220944-e22983a17bec 8 | github.com/grafana/grafana-openapi-client-go v0.0.0-20241018134006-9d96c2007bd8 9 | ) 10 | 11 | require ( 12 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 13 | github.com/go-logr/logr v1.4.2 // indirect 14 | github.com/go-logr/stdr v1.2.2 // indirect 15 | github.com/go-openapi/analysis v0.23.0 // indirect 16 | github.com/go-openapi/errors v0.22.0 // indirect 17 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 18 | github.com/go-openapi/jsonreference v0.21.0 // indirect 19 | github.com/go-openapi/loads v0.22.0 // indirect 20 | github.com/go-openapi/runtime v0.28.0 // indirect 21 | github.com/go-openapi/spec v0.21.0 // indirect 22 | github.com/go-openapi/swag v0.23.0 // indirect 23 | github.com/go-openapi/validate v0.24.0 // indirect 24 | github.com/google/uuid v1.6.0 // indirect 25 | github.com/josharian/intern v1.0.0 // indirect 26 | github.com/mailru/easyjson v0.7.7 // indirect 27 | github.com/mitchellh/mapstructure v1.5.0 // indirect 28 | github.com/oklog/ulid v1.3.1 // indirect 29 | github.com/opentracing/opentracing-go v1.2.0 // indirect 30 | go.mongodb.org/mongo-driver v1.17.1 // indirect 31 | go.opentelemetry.io/otel v1.31.0 // indirect 32 | go.opentelemetry.io/otel/metric v1.31.0 // indirect 33 | go.opentelemetry.io/otel/trace v1.31.0 // indirect 34 | golang.org/x/sync v0.8.0 // indirect 35 | gopkg.in/yaml.v3 v3.0.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/assignment_Dashboard_withRow.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "pre_assignment_Dashboard_withRow" }} 2 | 3 | // Position the row on the grid 4 | if rowPanelResource.GridPos == nil || (rowPanelResource.GridPos.X == 0 && rowPanelResource.GridPos.Y == 0) { 5 | rowPanelResource.GridPos = &GridPos{ 6 | X: 0, // beginning of the line 7 | Y: builder.currentY + builder.lastPanelHeight, 8 | 9 | H: 1, 10 | W: 24, // full width 11 | } 12 | } 13 | {{- end }} 14 | 15 | {{- define "post_assignment_Dashboard_withRow" }} 16 | 17 | // Reset the state for the next row 18 | builder.currentX = 0 19 | builder.currentY = rowPanelResource.GridPos.Y + 1 20 | builder.lastPanelHeight = 0 21 | 22 | // Position the row's panels on the grid 23 | for _, panel := range rowPanelResource.Panels { 24 | // If the panel does not have a GridPos set, set it to the default one. 25 | if panel.GridPos == nil { 26 | panel.GridPos = NewGridPos() 27 | } 28 | 29 | // The panel either has no position set, or it is the first panel of the dashboard. 30 | // In that case, we position it on the grid 31 | if panel.GridPos.X == 0 && panel.GridPos.Y == 0 { 32 | panel.GridPos.X = builder.currentX 33 | panel.GridPos.Y = builder.currentY 34 | } 35 | 36 | // Prepare the coordinates for the next panel 37 | builder.currentX += panel.GridPos.W 38 | builder.lastPanelHeight = max(builder.lastPanelHeight, panel.GridPos.H) 39 | 40 | // Check for grid width overflow? 41 | if builder.currentX >= 24 { 42 | builder.currentX = 0 43 | builder.currentY += builder.lastPanelHeight 44 | builder.lastPanelHeight = 0 45 | } 46 | } 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /.cog/templates/php/overrides/api_reference_package_dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_package_dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Building a dashboard 5 | 6 | ```php 7 | use Grafana\Foundation\Common; 8 | use Grafana\Foundation\Dashboard\DashboardBuilder; 9 | use Grafana\Foundation\Dashboard\RowBuilder; 10 | use Grafana\Foundation\Prometheus; 11 | use Grafana\Foundation\Timeseries; 12 | 13 | require_once __DIR__.'/vendor/autoload.php'; 14 | 15 | $builder = (new DashboardBuilder(title: 'Sample dashboard')) 16 | ->uid('generated-from-php') 17 | ->tags(['generated', 'from', 'php']) 18 | ->refresh('1m') 19 | ->time('now-30m', 'now') 20 | ->timezone(Common\Constants::TIME_ZONE_BROWSER) 21 | ->withRow(new RowBuilder('Overview')) 22 | ->withPanel( 23 | (new Timeseries\PanelBuilder()) 24 | ->title('Network received') 25 | ->unit('bps') 26 | ->min(0) 27 | ->withTarget( 28 | (new Prometheus\DataqueryBuilder()) 29 | ->expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8') 30 | ->legendFormat({{ `'{{ device }}'` }}) 31 | ) 32 | ) 33 | ; 34 | 35 | echo(json_encode($builder->build(), JSON_PRETTY_PRINT).PHP_EOL); 36 | ``` 37 | 38 | ### Unmarshaling a dashboard 39 | 40 | ```php 41 | use Grafana\Foundation\Dashboard\Dashboard; 42 | 43 | require_once __DIR__.'/vendor/autoload.php'; 44 | 45 | $dashboardJSON = file_get_contents(__DIR__.'/dashboard.json'); 46 | 47 | $dashboard = Dashboard::fromArray(json_decode($dashboardJSON, true)); 48 | 49 | var_dump($dashboard); 50 | ``` 51 | {{ end }} 52 | -------------------------------------------------------------------------------- /examples/java/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains a collection of Grafana dashboards written in Java. 4 | 5 | Each example showcases different aspects of building dashboards as code: 6 | 7 | * [`alert-rule`](./alert-rule): definition and usage of alert rule and rule group types 8 | * [`custom-panel`](./custom-panel): definition and usage of a _custom_ Panel type 9 | * [`custom-query`](./custom-query): definition and usage of a _custom_ Query type 10 | * [`grafana-agent-overview`](./grafana-agent-overview): 11 | * reproduction of the "Grafana Agent Overview" dashboard from 12 | the [Grafana Agent integration](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-grafana-agent/) 13 | available in Grafana Cloud. 14 | * dashboard variables 15 | * `table` panels 16 | * `timeseries` panels 17 | * `prometheus` queries 18 | * [`linux-node-overview`](./linux-node-overview): 19 | * reproduction of the "Grafana Agent Overview" dashboard from 20 | the [Linux Server integration](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-linux-node/#dashboards) 21 | available in Grafana Cloud. 22 | * dashboard variables 23 | * dashboard links 24 | * `stat` panels 25 | * `table` panels 26 | * `timeseries` panels 27 | * `prometheus` queries 28 | * [`red-method`](./red-method): 29 | * example of a dashboard following 30 | the [RED method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/#the-red-method) 31 | 32 | ## Running the examples 33 | 34 | From an example's folder: 35 | 36 | ```console 37 | $ gradle run 38 | ``` 39 | -------------------------------------------------------------------------------- /examples/python/custom-panel/src/custompanel.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Self 2 | 3 | from grafana_foundation_sdk.cog import builder 4 | from grafana_foundation_sdk.cog import runtime as cogruntime 5 | from grafana_foundation_sdk.builders.dashboard import Panel as PanelBuilder 6 | from grafana_foundation_sdk.models import dashboard 7 | 8 | 9 | class CustomPanelOptions: 10 | make_beautiful: bool 11 | 12 | def __init__(self, make_beautiful: bool = False): 13 | self.make_beautiful = make_beautiful 14 | 15 | def to_json(self) -> dict[str, object]: 16 | return { 17 | "makeBeautiful": self.make_beautiful, 18 | } 19 | 20 | @classmethod 21 | def from_json(cls, data: dict[str, Any]) -> Self: 22 | args: dict[str, Any] = {} 23 | 24 | if "makeBeautiful" in data: 25 | args["make_beautiful"] = data["makeBeautiful"] 26 | 27 | return cls(**args) 28 | 29 | 30 | def custom_panel_variant_config() -> cogruntime.PanelCfgConfig: 31 | return cogruntime.PanelCfgConfig( 32 | # plugin ID 33 | identifier="custom-panel", 34 | options_from_json_hook=CustomPanelOptions.from_json, 35 | ) 36 | 37 | 38 | class CustomPanelBuilder(PanelBuilder, builder.Builder[dashboard.Panel]): 39 | def __init__(self): 40 | super().__init__() 41 | # plugin ID 42 | self._internal.type_val = "custom-panel" 43 | 44 | def make_beautiful(self) -> Self: 45 | if self._internal.options is None: 46 | self._internal.options = CustomPanelOptions() 47 | 48 | assert isinstance(self._internal.options, CustomPanelOptions) 49 | 50 | self._internal.options.make_beautiful = True 51 | 52 | return self 53 | -------------------------------------------------------------------------------- /examples/typescript/grafana-agent-overview/src/common.ts: -------------------------------------------------------------------------------- 1 | import * as common from '@grafana/grafana-foundation-sdk/common'; 2 | import * as prometheus from '@grafana/grafana-foundation-sdk/prometheus'; 3 | import { PanelBuilder as TimeseriesBuilder } from '@grafana/grafana-foundation-sdk/timeseries'; 4 | 5 | export const prometheusQuery = (query: string, legend: string): prometheus.DataqueryBuilder => { 6 | return new prometheus.DataqueryBuilder() 7 | .expr(query) 8 | .legendFormat(legend); 9 | }; 10 | 11 | export const tablePrometheusQuery = (query: string, ref: string): prometheus.DataqueryBuilder => { 12 | return new prometheus.DataqueryBuilder() 13 | .expr(query) 14 | .instant() 15 | .format(prometheus.PromQueryFormat.Table) 16 | .refId(ref); 17 | }; 18 | 19 | export const defaultTimeseries = (): TimeseriesBuilder => { 20 | return new TimeseriesBuilder() 21 | .height(7) 22 | .span(12) 23 | .lineWidth(1) 24 | .fillOpacity(0) 25 | .pointSize(5) 26 | .showPoints(common.VisibilityMode.Auto) 27 | .drawStyle(common.GraphDrawStyle.Line) 28 | .gradientMode(common.GraphGradientMode.None) 29 | .spanNulls(false) 30 | .axisBorderShow(false) 31 | .legend( 32 | new common.VizLegendOptionsBuilder() 33 | .showLegend(true) 34 | .placement(common.LegendPlacement.Bottom) 35 | .displayMode(common.LegendDisplayMode.List) 36 | ) 37 | .tooltip( 38 | new common.VizTooltipOptionsBuilder() 39 | .mode(common.TooltipDisplayMode.Single) 40 | .sort(common.SortOrder.None) 41 | ) 42 | .thresholdsStyle( 43 | new common.GraphThresholdsStyleConfigBuilder() 44 | .mode(common.GraphThresholdsStyleMode.Off) 45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/api_reference_builder_dashboard_Dashboard_extra.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "api_reference_builder_dashboard_Dashboard_extra" -}} 2 | ## Examples 3 | 4 | ### Building a dashboard 5 | 6 | ```go 7 | package main 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | 13 | "github.com/grafana/grafana-foundation-sdk/go/common" 14 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 15 | "github.com/grafana/grafana-foundation-sdk/go/prometheus" 16 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 17 | ) 18 | 19 | func main() { 20 | builder := dashboard.NewDashboardBuilder("Sample dashboard"). 21 | Uid("generated-from-go"). 22 | Tags([]string{"generated", "from", "go"}). 23 | Refresh("1m"). 24 | Time("now-30m", "now"). 25 | Timezone(common.TimeZoneBrowser). 26 | WithRow(dashboard.NewRowBuilder("Overview")). 27 | WithPanel( 28 | timeseries.NewPanelBuilder(). 29 | Title("Network Received"). 30 | Unit("bps"). 31 | Min(0). 32 | WithTarget( 33 | prometheus.NewDataqueryBuilder(). 34 | Expr(`rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8`). 35 | LegendFormat({{ `"{{ device }}"` }}), 36 | ), 37 | ) 38 | 39 | sampleDashboard, err := builder.Build() 40 | if err != nil { 41 | panic(err) 42 | } 43 | dashboardJson, err := json.MarshalIndent(sampleDashboard, "", " ") 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | fmt.Println(string(dashboardJson)) 49 | } 50 | ``` 51 | {{ end }} 52 | -------------------------------------------------------------------------------- /.cog/templates/typescript/extra/docs/How-To/custom-panel-type.md: -------------------------------------------------------------------------------- 1 | # Defining a custom panel type 2 | 3 | While the SDK ships with support for all core panels, it can be extended for 4 | private/third-party plugins. 5 | 6 | To do so, define a type and a builder for the custom panel's options: 7 | 8 | ```typescript 9 | // customPanel.ts 10 | import * as dashboard from '@grafana/grafana-foundation-sdk/dashboard'; 11 | 12 | export interface CustomPanelOptions { 13 | makeBeautiful?: boolean; 14 | } 15 | 16 | export const defaultCustomPanelOptions = (): CustomPanelOptions => ({ 17 | }); 18 | 19 | export class CustomPanelBuilder extends dashboard.PanelBuilder { 20 | constructor() { 21 | super(); 22 | this.internal.type = "custom-panel"; // panel plugin ID 23 | } 24 | 25 | makeBeautiful(): this { 26 | if (!this.internal.options) { 27 | this.internal.options = defaultCustomPanelOptions(); 28 | } 29 | this.internal.options.makeBeautiful = true; 30 | return this; 31 | } 32 | } 33 | ``` 34 | 35 | The custom panel type can now be used as usual to build a dashboard: 36 | 37 | ```typescript 38 | import { DashboardBuilder, RowBuilder } from '@grafana/grafana-foundation-sdk/dashboard'; 39 | import { CustomPanelBuilder } from "./customPanel"; 40 | 41 | const builder = new DashboardBuilder('Custom panel type') 42 | .uid('test-custom-panel-type') 43 | 44 | .refresh('1m') 45 | .time({ from: 'now-30m', to: 'now' }) 46 | 47 | .withRow(new RowBuilder('Overview')) 48 | .withPanel( 49 | new CustomPanelBuilder() 50 | .title('Sample custom panel') 51 | .makeBeautiful() 52 | ); 53 | 54 | console.log(JSON.stringify(builder.build(), null, 2)); 55 | ``` 56 | -------------------------------------------------------------------------------- /scripts/docs/_pull_versions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit on error. Append "|| true" if you expect an error. 4 | set -o errexit 5 | # Exit on error inside any functions or subshells. 6 | set -o errtrace 7 | # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR 8 | set -o nounset 9 | # Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` 10 | set -o pipefail 11 | 12 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 13 | source "${__dir}/../versions.sh" 14 | 15 | tmp_dir=${1:-"./"} 16 | 17 | FOUNDATION_SDK_REPO=${FOUNDATION_SDK_REPO:-'https://github.com/grafana/grafana-foundation-sdk.git'} 18 | 19 | # Cleanup potential leftovers from previous runs. 20 | rm -rf "${tmp_dir}/tmp-foundation-sdk" 21 | rm -rf "${tmp_dir}/versions" 22 | 23 | mkdir -p "${tmp_dir}/versions" 24 | git clone "${FOUNDATION_SDK_REPO}" "${tmp_dir}/tmp-foundation-sdk" 25 | 26 | for version in ${ALL_GRAFANA_VERSIONS//;/ } ; do 27 | full_version="${version}+cog-${COG_VERSION}" 28 | 29 | echo "🪧 Pulling documentation for Foundation SDK version ${full_version}" 30 | 31 | git -C "${tmp_dir}/tmp-foundation-sdk" fetch origin "${full_version}" 32 | git -C "${tmp_dir}/tmp-foundation-sdk" checkout "${full_version}" 33 | git -C "${tmp_dir}/tmp-foundation-sdk" pull --ff-only origin "${full_version}" 34 | 35 | find "${tmp_dir}/tmp-foundation-sdk" -maxdepth 1 -mindepth 1 -type d -print | while read -r target; do 36 | language_name=${target#"${tmp_dir}/tmp-foundation-sdk"} 37 | 38 | if [ -d "${target}/docs" ]; then 39 | mkdir -p "${tmp_dir}/versions/${full_version}/${language_name}" 40 | cp -R ${target}/docs/* "${tmp_dir}/versions/${full_version}/${language_name}" 41 | fi 42 | done 43 | done 44 | -------------------------------------------------------------------------------- /.cog/compiler/testdata_passes.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/compiler_passes.json 2 | 3 | passes: 4 | - anonymous_structs_to_named: {} 5 | 6 | # To stay close to the pre-v11 object names 7 | 8 | - omit: 9 | objects: 10 | - 'testdata.Testdata' 11 | - 'testdata.TestdataTestdataTargetsDatasource' 12 | 13 | - rename_object: 14 | from: 'testdata.TestdataTestdataTargets' 15 | to: 'dataquery' 16 | 17 | - schema_set_entry_point: 18 | package: testdata 19 | entry_point: dataquery 20 | 21 | - retype_field: 22 | field: testdata.dataquery.datasource 23 | as: 24 | kind: ref 25 | ref: { referred_pkg: common, referred_type: DataSourceRef } 26 | 27 | - rename_object: 28 | from: 'testdata.TestdataTestdataTargetsCsvWave' 29 | to: 'CSVWave' 30 | 31 | - rename_object: 32 | from: 'testdata.TestdataTestdataTargetsNodes' 33 | to: 'NodesQuery' 34 | 35 | - rename_object: 36 | from: 'testdata.TestdataTestdataTargetsPulseWave' 37 | to: 'PulseWaveQuery' 38 | 39 | - rename_object: 40 | from: 'testdata.TestdataTestdataTargetsResultAssertions' 41 | to: 'ResultAssertions' 42 | 43 | - rename_object: 44 | from: 'testdata.TestdataTestdataTargetsSimKey' 45 | to: 'Key' 46 | 47 | - rename_object: 48 | from: 'testdata.TestdataTestdataTargetsSim' 49 | to: 'SimulationQuery' 50 | 51 | - rename_object: 52 | from: 'testdata.TestdataTestdataTargetsStream' 53 | to: 'StreamingQuery' 54 | 55 | - rename_object: 56 | from: 'testdata.TestdataTestdataTargetsTimeRange' 57 | to: 'TimeRange' 58 | 59 | - rename_object: 60 | from: 'testdata.TestdataTestdataTargetsUsa' 61 | to: 'USAQuery' 62 | -------------------------------------------------------------------------------- /examples/python/grafana-agent-overview/src/common.py: -------------------------------------------------------------------------------- 1 | from grafana_foundation_sdk.builders import ( 2 | timeseries, 3 | common as common_builder, 4 | prometheus, 5 | ) 6 | from grafana_foundation_sdk.models import common, prometheus as prom 7 | 8 | 9 | def prometheus_query(query: str, legend: str) -> prometheus.Dataquery: 10 | return prometheus.Dataquery().expr(query).legend_format(legend) 11 | 12 | 13 | def table_prometheus_query(query: str, ref_id: str) -> prometheus.Dataquery: 14 | return ( 15 | prometheus.Dataquery() 16 | .expr(query) 17 | .format(prom.PromQueryFormat.TABLE) 18 | .instant() 19 | .interval_factor(2) 20 | .ref_id(ref_id) 21 | ) 22 | 23 | 24 | def default_timeseries() -> timeseries.Panel: 25 | return ( 26 | timeseries.Panel() 27 | .height(7) 28 | .span(12) 29 | .line_width(1) 30 | .fill_opacity(0) 31 | .point_size(5) 32 | .show_points(common.VisibilityMode.AUTO) 33 | .draw_style(common.GraphDrawStyle.LINE) 34 | .gradient_mode(common.GraphGradientMode.NONE) 35 | .span_nulls(False) 36 | .axis_border_show(False) 37 | .legend( 38 | common_builder.VizLegendOptions() 39 | .display_mode(common.LegendDisplayMode.LIST) 40 | .placement(common.LegendPlacement.BOTTOM) 41 | .show_legend(True) 42 | ) 43 | .tooltip( 44 | common_builder.VizTooltipOptions() 45 | .mode(common.TooltipDisplayMode.SINGLE) 46 | .sort(common.SortOrder.NONE) 47 | ) 48 | .thresholds_style( 49 | common_builder.GraphThresholdsStyleConfig().mode( 50 | common.GraphThresholdsStyleMode.OFF 51 | ) 52 | ) 53 | ) 54 | -------------------------------------------------------------------------------- /examples/php/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains a collection of Grafana dashboards written in PHP. 4 | 5 | Each example showcases different aspects of building dashboards as code: 6 | 7 | * [`custom-panel`](./custom-panel): definition and usage of a _custom_ Panel type 8 | * [`custom-query`](./custom-query): definition and usage of a _custom_ Query type 9 | * [`grafana-agent-overview`](./grafana-agent-overview): 10 | * reproduction of the "Grafana Agent Overview" dashboard from 11 | the [Grafana Agent integration](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-grafana-agent/) 12 | available in Grafana Cloud. 13 | * dashboard variables 14 | * `table` panels 15 | * `timeseries` panels 16 | * `prometheus` queries 17 | * [`linux-node-overview`](./linux-node-overview): 18 | * reproduction of the "Grafana Agent Overview" dashboard from 19 | the [Linux Server integration](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-linux-node/#dashboards) 20 | available in Grafana Cloud. 21 | * dashboard variables 22 | * dashboard links 23 | * `stat` panels 24 | * `table` panels 25 | * `timeseries` panels 26 | * `prometheus` queries 27 | * [`red-method`](./red-method): 28 | * example of a dashboard following 29 | the [RED method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/#the-red-method) 30 | 31 | ## Running the examples 32 | 33 | From an example's folder: 34 | 35 | ```console 36 | $ composer install 37 | $ php index.php 38 | ``` 39 | 40 | > [!NOTE] 41 | > [Grizzly](https://github.com/grafana/grizzly/) can be used to preview the examples locally: 42 | > 43 | > `grr serve -w -S 'php index.php' .` 44 | -------------------------------------------------------------------------------- /.cog/templates/php/extra/src/Cog/UnknownDataquery.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace {{ .Data.NamespaceRoot }}\Cog; 4 | 5 | /** 6 | * @implements \ArrayAccess<string, mixed> 7 | */ 8 | final class UnknownDataquery implements \ArrayAccess, \JsonSerializable, Dataquery 9 | { 10 | /** 11 | * @var array<string, mixed> 12 | */ 13 | private $data = []; 14 | 15 | /** 16 | * @param array<string, mixed> $data 17 | */ 18 | public function __construct(array $data) 19 | { 20 | $this->data = $data; 21 | } 22 | 23 | /** 24 | * @return array<string, mixed> 25 | */ 26 | public function toArray(): array 27 | { 28 | return $this->data; 29 | } 30 | 31 | public function dataqueryType(): string 32 | { 33 | return 'unknown'; 34 | } 35 | 36 | /** 37 | * @param string $offset 38 | * @param mixed $value 39 | */ 40 | public function offsetSet($offset, $value): void 41 | { 42 | $this->data[$offset] = $value; 43 | } 44 | 45 | /** 46 | * @param string $offset 47 | */ 48 | public function offsetExists($offset): bool 49 | { 50 | return \array_key_exists($offset, $this->data); 51 | } 52 | 53 | /** 54 | * @param string $offset 55 | */ 56 | public function offsetUnset($offset): void 57 | { 58 | unset($this->data[$offset]); 59 | } 60 | 61 | /** 62 | * @param string $offset 63 | */ 64 | public function offsetGet($offset): mixed 65 | { 66 | if (!\array_key_exists($offset, $this->data)) { 67 | throw new \ValueError("offset '$offset' does not exist"); 68 | } 69 | return $this->data[$offset] ?? null; 70 | } 71 | 72 | public function jsonSerialize(): mixed 73 | { 74 | return $this->data; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /.cog/templates/go/overrides/object_common_TableFooterOptions_custom_unmarshal.tmpl: -------------------------------------------------------------------------------- 1 | {{- define "object_common_TableFooterOptions_custom_unmarshal" }} 2 | {{- /* Non-standard unmarshalling needed because even though the "fields" field 3 | is typed as []string in the schema, Grafana uses `""` as an empty value 4 | */ -}} 5 | {{- $fmt := importStdPkg "fmt" -}} 6 | {{- $json := importStdPkg "encoding/json" -}} 7 | func (resource *{{ .Object.Name|upperCamelCase }}) UnmarshalJSON(raw []byte) error { 8 | if raw == nil { 9 | return nil 10 | } 11 | 12 | fields := make(map[string]json.RawMessage) 13 | if err := json.Unmarshal(raw, &fields); err != nil { 14 | return err 15 | } 16 | {{- range $field := .Object.Type.Struct.Fields }} 17 | {{- if eq $field.Name "fields" }} 18 | {{ template "unmarshal_common_TableFooterOptions_fields" }} 19 | {{- else }} 20 | if fields["{{ $field.Name }}"] != nil { 21 | if err := json.Unmarshal(fields["{{ $field.Name }}"], &resource.{{ $field.Name|upperCamelCase }}); err != nil { 22 | return fmt.Errorf("error decoding field '{{ $field.Name }}': %w", err) 23 | } 24 | } 25 | {{- end }} 26 | {{- end }} 27 | 28 | return nil 29 | } 30 | 31 | {{ end }} 32 | 33 | {{- define "unmarshal_common_TableFooterOptions_fields" -}} 34 | rawFields := fields["fields"] 35 | 36 | if rawFields != nil { 37 | if len(rawFields) != 0 && rawFields[0] == '"' { 38 | var field string 39 | if err := json.Unmarshal(rawFields, &field); err != nil { 40 | return fmt.Errorf("error decoding field 'fields': %w", err) 41 | } 42 | resource.Fields = []string{field} 43 | } else { 44 | if err := json.Unmarshal(rawFields, &resource.Fields); err != nil { 45 | return fmt.Errorf("error decoding field 'fields': %w", err) 46 | } 47 | } 48 | } 49 | {{- end -}} 50 | -------------------------------------------------------------------------------- /examples/go/linux-node-overview/builder/host.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "github.com/grafana/grafana-foundation-sdk/go/cog" 5 | "github.com/grafana/grafana-foundation-sdk/go/common" 6 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 7 | "github.com/grafana/grafana-foundation-sdk/go/stat" 8 | "github.com/grafana/grafana-foundation-sdk/go/units" 9 | ) 10 | 11 | func uptimeStat() *stat.PanelBuilder { 12 | return defaultStat(). 13 | Title("Uptime"). 14 | Description("The duration of time that has passed since the last reboot or system start."). 15 | WithTarget( 16 | prometheusQuery(`time() - node_boot_time_seconds{job=~"integrations/(node_exporter|unix)",cluster=~"$cluster",job=~"$job",instance=~"$instance"}`, ""), 17 | ). 18 | Unit(units.DurationSeconds). 19 | Thresholds(dashboard.NewThresholdsConfigBuilder(). 20 | Mode(dashboard.ThresholdsModeAbsolute). 21 | Steps([]dashboard.Threshold{ 22 | {Value: nil, Color: "orange"}, 23 | {Value: cog.ToPtr[float64](600), Color: "text"}, 24 | }), 25 | ) 26 | } 27 | 28 | func hostnameStat() *stat.PanelBuilder { 29 | return unameStat( 30 | "Hostname", 31 | "System's hostname.", 32 | "nodename", 33 | ) 34 | } 35 | 36 | func kernelVersionStat() *stat.PanelBuilder { 37 | return unameStat( 38 | "Kernel version", 39 | "Kernel version of linux host.", 40 | "release", 41 | ) 42 | } 43 | 44 | func osStat() *stat.PanelBuilder { 45 | return defaultStat(). 46 | Title("OS"). 47 | Description("Operating system."). 48 | WithTarget( 49 | tablePrometheusQuery(`node_os_info{job=~"integrations/(node_exporter|unix)",cluster=~"$cluster",job=~"$job",instance=~"$instance"}`, "A"), 50 | ). 51 | ReduceOptions(common.NewReduceDataOptionsBuilder().Calcs([]string{"lastNotNull"}).Fields("/^pretty_name$/")). 52 | ColorMode(common.BigValueColorModeNone) 53 | } 54 | -------------------------------------------------------------------------------- /examples/go/grafana-agent-overview/common.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/grafana/grafana-foundation-sdk/go/cog" 5 | "github.com/grafana/grafana-foundation-sdk/go/common" 6 | "github.com/grafana/grafana-foundation-sdk/go/dashboard" 7 | "github.com/grafana/grafana-foundation-sdk/go/prometheus" 8 | "github.com/grafana/grafana-foundation-sdk/go/timeseries" 9 | ) 10 | 11 | func datasourceRef(uid string) dashboard.DataSourceRef { 12 | return dashboard.DataSourceRef{Uid: &uid} 13 | } 14 | 15 | func prometheusQuery(query string, legend string) *prometheus.DataqueryBuilder { 16 | return prometheus.NewDataqueryBuilder(). 17 | Expr(query). 18 | LegendFormat(legend) 19 | } 20 | 21 | func tablePrometheusQuery(query string, refID string) *prometheus.DataqueryBuilder { 22 | return prometheus.NewDataqueryBuilder(). 23 | Expr(query). 24 | Format(prometheus.PromQueryFormatTable). 25 | Instant(). 26 | IntervalFactor(2). 27 | RefId(refID) 28 | } 29 | 30 | func defaultTimeseries() *timeseries.PanelBuilder { 31 | return timeseries.NewPanelBuilder(). 32 | Height(7). 33 | Span(12). 34 | LineWidth(1). 35 | FillOpacity(0). 36 | PointSize(5). 37 | ShowPoints(common.VisibilityModeAuto). 38 | DrawStyle(common.GraphDrawStyleLine). 39 | GradientMode(common.GraphGradientModeNone). 40 | Legend(common.NewVizLegendOptionsBuilder(). 41 | DisplayMode(common.LegendDisplayModeList). 42 | Placement(common.LegendPlacementBottom). 43 | ShowLegend(true), 44 | ). 45 | Tooltip(common.NewVizTooltipOptionsBuilder(). 46 | Mode(common.TooltipDisplayModeSingle). 47 | Sort(common.SortOrderNone), 48 | ). 49 | ThresholdsStyle( 50 | common.NewGraphThresholdsStyleConfigBuilder(). 51 | Mode(common.GraphThresholdsStyleModeOff), 52 | ). 53 | SpanNulls(common.BoolOrFloat64{Bool: cog.ToPtr(false)}). 54 | AxisBorderShow(false) 55 | } 56 | --------------------------------------------------------------------------------