Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support defining custom unmarshallers for objects #587

Merged
merged 3 commits into from
Oct 9, 2024

Conversation

K-Phoen
Copy link
Member

@K-Phoen K-Phoen commented Oct 9, 2024

It's fairly common for "old" dashboards to refer to datasources by their UID as a string, instead of the DataSourceRef used now.

Because of this, these dashboards can't be unmarshalled from JSON.

To remedy this type of issue, I suggest we make unmarshal functions overridable via configuration-provided templates.

If this PR is accepted, I'll implement the same capability for other languages.

@K-Phoen K-Phoen self-assigned this Oct 9, 2024
@K-Phoen K-Phoen requested a review from a team as a code owner October 9, 2024 12:01
Copy link

github-actions bot commented Oct 9, 2024

Note: in addition to the changes introduced by this PR, the diff includes unreleased changes living in main.

🔎 Changes to grafana-foundation-sdk@next+cog-v0.0.x

diff --git a/LICENSE.md b/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index bab21a4c..962b3249 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ of versions of Grafana, in the following languages:
 > [schemas exposed by Grafana][kind-registry].
 
 > [!TIP]
-> This branch contains **types and builders generated for Grafana `main`.**
+> This branch contains **types and builders generated for Grafana `next`.**
 
 ## Navigating the SDK
 
@@ -26,10 +26,6 @@ your Grafana instance.
 | Grafana Version                | `cog` Version | Branch |
 | ------------------------------ | ------------- | ------ |
 | `next` (Grafana's main branch) | `v0.0.x`      | [next+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/next%2Bcog-v0.0.x) |
-| `v10.4.x`                      | `v0.0.x`      | [v10.4.x+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/v10.4.x%2Bcog-v0.0.x) |
-| `v10.3.x`                      | `v0.0.x`      | [v10.3.x+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/v10.3.x%2Bcog-v0.0.x) |
-| `v10.2.x`                      | `v0.0.x`      | [v10.2.x+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/v10.2.x%2Bcog-v0.0.x) |
-| `v10.1.x`                      | `v0.0.x`      | [v10.1.x+cog-v0.0.x](https://github.com/grafana/grafana-foundation-sdk/tree/v10.1.x%2Bcog-v0.0.x) |
 
 ## Maturity
 
diff --git a/go/LICENSE.md b/go/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/go/LICENSE.md
+++ b/go/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/go/alerting/types_gen.go b/go/alerting/types_gen.go
index a346ebb8..29299769 100644
--- a/go/alerting/types_gen.go
+++ b/go/alerting/types_gen.go
@@ -4,6 +4,7 @@ package alerting
 
 import (
 	"encoding/json"
+	"fmt"
 	"time"
 
 	cog "github.com/grafana/grafana-foundation-sdk/go/cog"
@@ -29,25 +30,25 @@ func (resource *Query) UnmarshalJSON(raw []byte) error {
 
 	if fields["datasourceUid"] != nil {
 		if err := json.Unmarshal(fields["datasourceUid"], &resource.DatasourceUid); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'datasourceUid': %w", err)
 		}
 	}
 
 	if fields["queryType"] != nil {
 		if err := json.Unmarshal(fields["queryType"], &resource.QueryType); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'queryType': %w", err)
 		}
 	}
 
 	if fields["refId"] != nil {
 		if err := json.Unmarshal(fields["refId"], &resource.RefId); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'refId': %w", err)
 		}
 	}
 
 	if fields["relativeTimeRange"] != nil {
 		if err := json.Unmarshal(fields["relativeTimeRange"], &resource.RelativeTimeRange); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'relativeTimeRange': %w", err)
 		}
 	}
 
diff --git a/go/cog/variants/variants.go b/go/cog/variants/variants.go
index 01f5c3a2..20f11423 100644
--- a/go/cog/variants/variants.go
+++ b/go/cog/variants/variants.go
@@ -2,7 +2,9 @@
 
 package variants
 
-import "reflect"
+import (
+	"reflect"
+)
 
 type PanelcfgConfig struct {
 	Identifier             string
diff --git a/go/dashboard/types_gen.go b/go/dashboard/types_gen.go
index c31bcc1f..205d2861 100644
--- a/go/dashboard/types_gen.go
+++ b/go/dashboard/types_gen.go
@@ -732,6 +732,28 @@ type DataSourceRef struct {
 	Uid *string `json:"uid,omitempty"`
 }
 
+func (resource *DataSourceRef) UnmarshalJSON(raw []byte) error {
+	if raw == nil {
+		return nil
+	}
+
+	if raw[0] == '"' {
+		var datasourceUid string
+		if err := json.Unmarshal(raw, &datasourceUid); err != nil {
+			return err
+		}
+		resource.Uid = &datasourceUid
+	} else {
+		type original DataSourceRef
+
+		if err := json.Unmarshal(raw, (*original)(resource)); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (resource DataSourceRef) Equals(other DataSourceRef) bool {
 	if resource.Type == nil && other.Type != nil || resource.Type != nil && other.Type == nil {
 		return false
@@ -1500,127 +1522,127 @@ func (resource *Panel) UnmarshalJSON(raw []byte) error {
 
 	if fields["type"] != nil {
 		if err := json.Unmarshal(fields["type"], &resource.Type); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'type': %w", err)
 		}
 	}
 
 	if fields["id"] != nil {
 		if err := json.Unmarshal(fields["id"], &resource.Id); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'id': %w", err)
 		}
 	}
 
 	if fields["pluginVersion"] != nil {
 		if err := json.Unmarshal(fields["pluginVersion"], &resource.PluginVersion); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'pluginVersion': %w", err)
 		}
 	}
 
 	if fields["title"] != nil {
 		if err := json.Unmarshal(fields["title"], &resource.Title); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'title': %w", err)
 		}
 	}
 
 	if fields["description"] != nil {
 		if err := json.Unmarshal(fields["description"], &resource.Description); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'description': %w", err)
 		}
 	}
 
 	if fields["transparent"] != nil {
 		if err := json.Unmarshal(fields["transparent"], &resource.Transparent); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'transparent': %w", err)
 		}
 	}
 
 	if fields["datasource"] != nil {
 		if err := json.Unmarshal(fields["datasource"], &resource.Datasource); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'datasource': %w", err)
 		}
 	}
 
 	if fields["gridPos"] != nil {
 		if err := json.Unmarshal(fields["gridPos"], &resource.GridPos); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'gridPos': %w", err)
 		}
 	}
 
 	if fields["links"] != nil {
 		if err := json.Unmarshal(fields["links"], &resource.Links); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'links': %w", err)
 		}
 	}
 
 	if fields["repeat"] != nil {
 		if err := json.Unmarshal(fields["repeat"], &resource.Repeat); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'repeat': %w", err)
 		}
 	}
 
 	if fields["repeatDirection"] != nil {
 		if err := json.Unmarshal(fields["repeatDirection"], &resource.RepeatDirection); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'repeatDirection': %w", err)
 		}
 	}
 
 	if fields["maxPerRow"] != nil {
 		if err := json.Unmarshal(fields["maxPerRow"], &resource.MaxPerRow); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'maxPerRow': %w", err)
 		}
 	}
 
 	if fields["maxDataPoints"] != nil {
 		if err := json.Unmarshal(fields["maxDataPoints"], &resource.MaxDataPoints); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'maxDataPoints': %w", err)
 		}
 	}
 
 	if fields["transformations"] != nil {
 		if err := json.Unmarshal(fields["transformations"], &resource.Transformations); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'transformations': %w", err)
 		}
 	}
 
 	if fields["interval"] != nil {
 		if err := json.Unmarshal(fields["interval"], &resource.Interval); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'interval': %w", err)
 		}
 	}
 
 	if fields["timeFrom"] != nil {
 		if err := json.Unmarshal(fields["timeFrom"], &resource.TimeFrom); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'timeFrom': %w", err)
 		}
 	}
 
 	if fields["timeShift"] != nil {
 		if err := json.Unmarshal(fields["timeShift"], &resource.TimeShift); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'timeShift': %w", err)
 		}
 	}
 
 	if fields["hideTimeOverride"] != nil {
 		if err := json.Unmarshal(fields["hideTimeOverride"], &resource.HideTimeOverride); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'hideTimeOverride': %w", err)
 		}
 	}
 
 	if fields["libraryPanel"] != nil {
 		if err := json.Unmarshal(fields["libraryPanel"], &resource.LibraryPanel); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'libraryPanel': %w", err)
 		}
 	}
 
 	if fields["cacheTimeout"] != nil {
 		if err := json.Unmarshal(fields["cacheTimeout"], &resource.CacheTimeout); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'cacheTimeout': %w", err)
 		}
 	}
 
 	if fields["queryCachingTTL"] != nil {
 		if err := json.Unmarshal(fields["queryCachingTTL"], &resource.QueryCachingTTL); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'queryCachingTTL': %w", err)
 		}
 	}
 
diff --git a/go/librarypanel/types_gen.go b/go/librarypanel/types_gen.go
index f9a15698..a9fa0378 100644
--- a/go/librarypanel/types_gen.go
+++ b/go/librarypanel/types_gen.go
@@ -4,6 +4,7 @@ package librarypanel
 
 import (
 	"encoding/json"
+	"fmt"
 	"reflect"
 	"time"
 
@@ -226,121 +227,121 @@ func (resource *LibrarypanelLibraryPanelModel) UnmarshalJSON(raw []byte) error {
 
 	if fields["type"] != nil {
 		if err := json.Unmarshal(fields["type"], &resource.Type); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'type': %w", err)
 		}
 	}
 
 	if fields["pluginVersion"] != nil {
 		if err := json.Unmarshal(fields["pluginVersion"], &resource.PluginVersion); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'pluginVersion': %w", err)
 		}
 	}
 
 	if fields["title"] != nil {
 		if err := json.Unmarshal(fields["title"], &resource.Title); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'title': %w", err)
 		}
 	}
 
 	if fields["description"] != nil {
 		if err := json.Unmarshal(fields["description"], &resource.Description); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'description': %w", err)
 		}
 	}
 
 	if fields["transparent"] != nil {
 		if err := json.Unmarshal(fields["transparent"], &resource.Transparent); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'transparent': %w", err)
 		}
 	}
 
 	if fields["datasource"] != nil {
 		if err := json.Unmarshal(fields["datasource"], &resource.Datasource); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'datasource': %w", err)
 		}
 	}
 
 	if fields["links"] != nil {
 		if err := json.Unmarshal(fields["links"], &resource.Links); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'links': %w", err)
 		}
 	}
 
 	if fields["repeat"] != nil {
 		if err := json.Unmarshal(fields["repeat"], &resource.Repeat); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'repeat': %w", err)
 		}
 	}
 
 	if fields["repeatDirection"] != nil {
 		if err := json.Unmarshal(fields["repeatDirection"], &resource.RepeatDirection); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'repeatDirection': %w", err)
 		}
 	}
 
 	if fields["maxPerRow"] != nil {
 		if err := json.Unmarshal(fields["maxPerRow"], &resource.MaxPerRow); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'maxPerRow': %w", err)
 		}
 	}
 
 	if fields["maxDataPoints"] != nil {
 		if err := json.Unmarshal(fields["maxDataPoints"], &resource.MaxDataPoints); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'maxDataPoints': %w", err)
 		}
 	}
 
 	if fields["transformations"] != nil {
 		if err := json.Unmarshal(fields["transformations"], &resource.Transformations); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'transformations': %w", err)
 		}
 	}
 
 	if fields["interval"] != nil {
 		if err := json.Unmarshal(fields["interval"], &resource.Interval); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'interval': %w", err)
 		}
 	}
 
 	if fields["timeFrom"] != nil {
 		if err := json.Unmarshal(fields["timeFrom"], &resource.TimeFrom); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'timeFrom': %w", err)
 		}
 	}
 
 	if fields["timeShift"] != nil {
 		if err := json.Unmarshal(fields["timeShift"], &resource.TimeShift); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'timeShift': %w", err)
 		}
 	}
 
 	if fields["hideTimeOverride"] != nil {
 		if err := json.Unmarshal(fields["hideTimeOverride"], &resource.HideTimeOverride); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'hideTimeOverride': %w", err)
 		}
 	}
 
 	if fields["cacheTimeout"] != nil {
 		if err := json.Unmarshal(fields["cacheTimeout"], &resource.CacheTimeout); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'cacheTimeout': %w", err)
 		}
 	}
 
 	if fields["queryCachingTTL"] != nil {
 		if err := json.Unmarshal(fields["queryCachingTTL"], &resource.QueryCachingTTL); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'queryCachingTTL': %w", err)
 		}
 	}
 
 	if fields["options"] != nil {
 		if err := json.Unmarshal(fields["options"], &resource.Options); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'options': %w", err)
 		}
 	}
 
 	if fields["fieldConfig"] != nil {
 		if err := json.Unmarshal(fields["fieldConfig"], &resource.FieldConfig); err != nil {
-			return err
+			return fmt.Errorf("error decoding field 'fieldConfig': %w", err)
 		}
 	}
 
diff --git a/go/preferences/types_gen.go b/go/preferences/types_gen.go
index 8f16b40e..02c9cf03 100644
--- a/go/preferences/types_gen.go
+++ b/go/preferences/types_gen.go
@@ -2,7 +2,9 @@
 
 package preferences
 
-import "reflect"
+import (
+	"reflect"
+)
 
 // Spec defines user, team or org Grafana preferences
 // swagger:model Preferences
diff --git a/java/LICENSE.md b/java/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/java/LICENSE.md
+++ b/java/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/php/LICENSE.md b/php/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/php/LICENSE.md
+++ b/php/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/php/src/Annotationslist/VariantConfig.php b/php/src/Annotationslist/VariantConfig.php
index d6bfc3ae..a2523c0e 100644
--- a/php/src/Annotationslist/VariantConfig.php
+++ b/php/src/Annotationslist/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'annolist',
             optionsFromArray: [\Grafana\Foundation\Annotationslist\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Azuremonitor/AzureMonitorQuery.php b/php/src/Azuremonitor/AzureMonitorQuery.php
index 61b69652..2ccbf673 100644
--- a/php/src/Azuremonitor/AzureMonitorQuery.php
+++ b/php/src/Azuremonitor/AzureMonitorQuery.php
@@ -248,4 +248,9 @@ class AzureMonitorQuery implements \JsonSerializable, \Grafana\Foundation\Cog\Da
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "grafana-azure-monitor-datasource";
+    }
 }
diff --git a/php/src/Barchart/VariantConfig.php b/php/src/Barchart/VariantConfig.php
index 4c1d1628..85531585 100644
--- a/php/src/Barchart/VariantConfig.php
+++ b/php/src/Barchart/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'barchart',
             optionsFromArray: [\Grafana\Foundation\Barchart\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Barchart\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Barchart\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Bargauge/VariantConfig.php b/php/src/Bargauge/VariantConfig.php
index 0b2f3331..efafda7e 100644
--- a/php/src/Bargauge/VariantConfig.php
+++ b/php/src/Bargauge/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'bargauge',
             optionsFromArray: [\Grafana\Foundation\Bargauge\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Candlestick/VariantConfig.php b/php/src/Candlestick/VariantConfig.php
index 512c47a6..89d3a1f0 100644
--- a/php/src/Candlestick/VariantConfig.php
+++ b/php/src/Candlestick/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'candlestick',
             optionsFromArray: [\Grafana\Foundation\Candlestick\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Candlestick\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Candlestick\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Canvas/VariantConfig.php b/php/src/Canvas/VariantConfig.php
index 1e34d755..b5b3dba5 100644
--- a/php/src/Canvas/VariantConfig.php
+++ b/php/src/Canvas/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'canvas',
             optionsFromArray: [\Grafana\Foundation\Canvas\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Cloudwatch/CloudWatchAnnotationQuery.php b/php/src/Cloudwatch/CloudWatchAnnotationQuery.php
index 1ff4e141..66fcb27e 100644
--- a/php/src/Cloudwatch/CloudWatchAnnotationQuery.php
+++ b/php/src/Cloudwatch/CloudWatchAnnotationQuery.php
@@ -230,4 +230,9 @@ class CloudWatchAnnotationQuery implements \JsonSerializable, \Grafana\Foundatio
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "cloudwatch";
+    }
 }
diff --git a/php/src/Cloudwatch/CloudWatchLogsQuery.php b/php/src/Cloudwatch/CloudWatchLogsQuery.php
index ba366e4e..2650c6d0 100644
--- a/php/src/Cloudwatch/CloudWatchLogsQuery.php
+++ b/php/src/Cloudwatch/CloudWatchLogsQuery.php
@@ -160,4 +160,9 @@ class CloudWatchLogsQuery implements \JsonSerializable, \Grafana\Foundation\Cog\
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "cloudwatch";
+    }
 }
diff --git a/php/src/Cloudwatch/CloudWatchMetricsQuery.php b/php/src/Cloudwatch/CloudWatchMetricsQuery.php
index 03c456bb..a81c0018 100644
--- a/php/src/Cloudwatch/CloudWatchMetricsQuery.php
+++ b/php/src/Cloudwatch/CloudWatchMetricsQuery.php
@@ -280,4 +280,9 @@ class CloudWatchMetricsQuery implements \JsonSerializable, \Grafana\Foundation\C
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "cloudwatch";
+    }
 }
diff --git a/php/src/Cog/Dataquery.php b/php/src/Cog/Dataquery.php
index fcab07a1..7597050a 100644
--- a/php/src/Cog/Dataquery.php
+++ b/php/src/Cog/Dataquery.php
@@ -4,4 +4,5 @@ namespace Grafana\Foundation\Cog;
 
 interface Dataquery
 {
+    public function dataqueryType(): string;
 }
diff --git a/php/src/Cog/DataqueryConfig.php b/php/src/Cog/DataqueryConfig.php
index 96c0f2ff..5e889d4e 100644
--- a/php/src/Cog/DataqueryConfig.php
+++ b/php/src/Cog/DataqueryConfig.php
@@ -11,12 +11,19 @@ final class DataqueryConfig
      */
     public $fromArray;
 
+    /**
+     * @var (callable(Dataquery): string)|null
+     */
+    public $convert;
+
     /**
      * @param callable(array<string, mixed>): Dataquery $fromArray
+     * @param (callable(Dataquery): string)|null $convert
      */
-    public function __construct(string $identifier, callable $fromArray)
+    public function __construct(string $identifier, callable $fromArray, ?callable $convert)
     {
         $this->identifier = $identifier;
         $this->fromArray = $fromArray;
+        $this->convert = $convert;
     }
 }
diff --git a/php/src/Cog/PanelcfgConfig.php b/php/src/Cog/PanelcfgConfig.php
index ead53a75..829db752 100644
--- a/php/src/Cog/PanelcfgConfig.php
+++ b/php/src/Cog/PanelcfgConfig.php
@@ -6,6 +6,11 @@ final class PanelcfgConfig
 {
     public readonly string $identifier;
 
+    /**
+     * @var (callable(\Grafana\Foundation\Dashboard\Panel): string)|null
+     */
+    public $convert;
+
     /**
      * @var (callable(array<string, mixed>): object)|null
      */
@@ -17,12 +22,14 @@ final class PanelcfgConfig
     public $fieldConfigFromArray;
 
     /**
+     * @param (callable(\Grafana\Foundation\Dashboard\Panel): string)|null $convert
      * @param (callable(array<string, mixed>): object)|null $optionsFromArray
      * @param (callable(array<string, mixed>): object)|null $fieldConfigFromArray
      */
-    public function __construct(string $identifier, ?callable $optionsFromArray = null, ?callable $fieldConfigFromArray = null)
+    public function __construct(string $identifier, ?callable $convert = null, ?callable $optionsFromArray = null, ?callable $fieldConfigFromArray = null)
     {
         $this->identifier = $identifier;
+        $this->convert = $convert;
         $this->optionsFromArray = $optionsFromArray;
         $this->fieldConfigFromArray = $fieldConfigFromArray;
     }
diff --git a/php/src/Cog/Runtime.php b/php/src/Cog/Runtime.php
index d7592553..a3927b3c 100644
--- a/php/src/Cog/Runtime.php
+++ b/php/src/Cog/Runtime.php
@@ -120,4 +120,32 @@ final class Runtime
         }
         return $queries;
     }
+
+    public function convertPanelToCode(\Grafana\Foundation\Dashboard\Panel $panel, string $panelType): string
+    {
+        if (!$this->panelcfgVariantExists($panelType)) {
+            return '/* could not convert panel to PHP */';
+        }
+
+        $convert = $this->panelcfgVariants[$panelType]->convert;
+        if ($convert === null) {
+            return '/* could not convert panel to PHP */';
+        }
+
+        return $convert($panel);
+    }
+
+    public function convertDataqueryToCode(Dataquery $dataquery): string
+    {
+        if (!isset($this->dataqueryVariants[$dataquery->dataqueryType()])) {
+            return '/* could not convert dataquery to PHP */';
+        }
+
+        $convert = $this->dataqueryVariants[$dataquery->dataqueryType()]->convert;
+        if ($convert === null) {
+            return '/* could not convert dataquery to PHP */';
+        }
+
+        return $convert($dataquery);
+    }
 }
diff --git a/php/src/Cog/UnknownDataquery.php b/php/src/Cog/UnknownDataquery.php
index 4c6940ac..c7189251 100644
--- a/php/src/Cog/UnknownDataquery.php
+++ b/php/src/Cog/UnknownDataquery.php
@@ -20,6 +20,11 @@ final class UnknownDataquery implements \ArrayAccess, \JsonSerializable, Dataque
 	    $this->data = $data;
 	}
 
+    public function dataqueryType(): string
+    {
+        return 'unknown';
+    }
+
     /**
      * @param string $offset
      * @param mixed $value
diff --git a/php/src/Dashboardlist/VariantConfig.php b/php/src/Dashboardlist/VariantConfig.php
index c54cca39..279f2d86 100644
--- a/php/src/Dashboardlist/VariantConfig.php
+++ b/php/src/Dashboardlist/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'dashlist',
             optionsFromArray: [\Grafana\Foundation\Dashboardlist\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Datagrid/VariantConfig.php b/php/src/Datagrid/VariantConfig.php
index 55263c65..e329b016 100644
--- a/php/src/Datagrid/VariantConfig.php
+++ b/php/src/Datagrid/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'datagrid',
             optionsFromArray: [\Grafana\Foundation\Datagrid\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Debug/VariantConfig.php b/php/src/Debug/VariantConfig.php
index ee499f3a..a4af2932 100644
--- a/php/src/Debug/VariantConfig.php
+++ b/php/src/Debug/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'debug',
             optionsFromArray: [\Grafana\Foundation\Debug\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Elasticsearch/Dataquery.php b/php/src/Elasticsearch/Dataquery.php
index 1d25d9e8..5b2cb003 100644
--- a/php/src/Elasticsearch/Dataquery.php
+++ b/php/src/Elasticsearch/Dataquery.php
@@ -203,4 +203,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "elasticsearch";
+    }
 }
diff --git a/php/src/Expr/TypeClassicConditions.php b/php/src/Expr/TypeClassicConditions.php
index a382c115..e4b14708 100644
--- a/php/src/Expr/TypeClassicConditions.php
+++ b/php/src/Expr/TypeClassicConditions.php
@@ -155,4 +155,9 @@ class TypeClassicConditions implements \JsonSerializable, \Grafana\Foundation\Co
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Expr/TypeMath.php b/php/src/Expr/TypeMath.php
index 97a996bd..b23cd474 100644
--- a/php/src/Expr/TypeMath.php
+++ b/php/src/Expr/TypeMath.php
@@ -151,4 +151,9 @@ class TypeMath implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Expr/TypeReduce.php b/php/src/Expr/TypeReduce.php
index b5da5a06..e56d4444 100644
--- a/php/src/Expr/TypeReduce.php
+++ b/php/src/Expr/TypeReduce.php
@@ -183,4 +183,9 @@ class TypeReduce implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Expr/TypeResample.php b/php/src/Expr/TypeResample.php
index 7c9e1c00..4f21860c 100644
--- a/php/src/Expr/TypeResample.php
+++ b/php/src/Expr/TypeResample.php
@@ -190,4 +190,9 @@ class TypeResample implements \JsonSerializable, \Grafana\Foundation\Cog\Dataque
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Expr/TypeSql.php b/php/src/Expr/TypeSql.php
index 1ba8e0a0..27eaa9b7 100644
--- a/php/src/Expr/TypeSql.php
+++ b/php/src/Expr/TypeSql.php
@@ -148,4 +148,9 @@ class TypeSql implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Expr/TypeThreshold.php b/php/src/Expr/TypeThreshold.php
index bd68fc8c..05071c36 100644
--- a/php/src/Expr/TypeThreshold.php
+++ b/php/src/Expr/TypeThreshold.php
@@ -165,4 +165,9 @@ class TypeThreshold implements \JsonSerializable, \Grafana\Foundation\Cog\Dataqu
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "__expr__";
+    }
 }
diff --git a/php/src/Gauge/VariantConfig.php b/php/src/Gauge/VariantConfig.php
index 3a95003e..e174b135 100644
--- a/php/src/Gauge/VariantConfig.php
+++ b/php/src/Gauge/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'gauge',
             optionsFromArray: [\Grafana\Foundation\Gauge\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Geomap/VariantConfig.php b/php/src/Geomap/VariantConfig.php
index 7ef3298d..ed2d5594 100644
--- a/php/src/Geomap/VariantConfig.php
+++ b/php/src/Geomap/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'geomap',
             optionsFromArray: [\Grafana\Foundation\Geomap\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Googlecloudmonitoring/CloudMonitoringQuery.php b/php/src/Googlecloudmonitoring/CloudMonitoringQuery.php
index 6ecf8eba..ee74fd3e 100644
--- a/php/src/Googlecloudmonitoring/CloudMonitoringQuery.php
+++ b/php/src/Googlecloudmonitoring/CloudMonitoringQuery.php
@@ -166,4 +166,9 @@ class CloudMonitoringQuery implements \JsonSerializable, \Grafana\Foundation\Cog
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "cloud-monitoring";
+    }
 }
diff --git a/php/src/Grafanapyroscope/Dataquery.php b/php/src/Grafanapyroscope/Dataquery.php
index 3a7944a9..20e70f93 100644
--- a/php/src/Grafanapyroscope/Dataquery.php
+++ b/php/src/Grafanapyroscope/Dataquery.php
@@ -133,4 +133,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "grafanapyroscope";
+    }
 }
diff --git a/php/src/Heatmap/VariantConfig.php b/php/src/Heatmap/VariantConfig.php
index 2d0f2076..290516d5 100644
--- a/php/src/Heatmap/VariantConfig.php
+++ b/php/src/Heatmap/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'heatmap',
             optionsFromArray: [\Grafana\Foundation\Heatmap\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Heatmap\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Heatmap\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Histogram/VariantConfig.php b/php/src/Histogram/VariantConfig.php
index e97a46f4..1a8de0ab 100644
--- a/php/src/Histogram/VariantConfig.php
+++ b/php/src/Histogram/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'histogram',
             optionsFromArray: [\Grafana\Foundation\Histogram\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Histogram\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Histogram\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Logs/VariantConfig.php b/php/src/Logs/VariantConfig.php
index 09113b7c..b34e7c77 100644
--- a/php/src/Logs/VariantConfig.php
+++ b/php/src/Logs/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'logs',
             optionsFromArray: [\Grafana\Foundation\Logs\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Loki/Dataquery.php b/php/src/Loki/Dataquery.php
index d3f4320c..0808774b 100644
--- a/php/src/Loki/Dataquery.php
+++ b/php/src/Loki/Dataquery.php
@@ -165,4 +165,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "loki";
+    }
 }
diff --git a/php/src/News/VariantConfig.php b/php/src/News/VariantConfig.php
index 8f9962d1..35d37af4 100644
--- a/php/src/News/VariantConfig.php
+++ b/php/src/News/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'news',
             optionsFromArray: [\Grafana\Foundation\News\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Nodegraph/VariantConfig.php b/php/src/Nodegraph/VariantConfig.php
index da162970..56c02b03 100644
--- a/php/src/Nodegraph/VariantConfig.php
+++ b/php/src/Nodegraph/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'nodeGraph',
             optionsFromArray: [\Grafana\Foundation\Nodegraph\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Parca/Dataquery.php b/php/src/Parca/Dataquery.php
index 67952534..15da4099 100644
--- a/php/src/Parca/Dataquery.php
+++ b/php/src/Parca/Dataquery.php
@@ -100,4 +100,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "parca";
+    }
 }
diff --git a/php/src/Piechart/VariantConfig.php b/php/src/Piechart/VariantConfig.php
index 8b0d35cb..59ddd525 100644
--- a/php/src/Piechart/VariantConfig.php
+++ b/php/src/Piechart/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'piechart',
             optionsFromArray: [\Grafana\Foundation\Piechart\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Piechart\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Piechart\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Prometheus/Dataquery.php b/php/src/Prometheus/Dataquery.php
index 0df4fcf7..06b56301 100644
--- a/php/src/Prometheus/Dataquery.php
+++ b/php/src/Prometheus/Dataquery.php
@@ -181,4 +181,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "prometheus";
+    }
 }
diff --git a/php/src/Stat/VariantConfig.php b/php/src/Stat/VariantConfig.php
index c95d39c1..40d49ab3 100644
--- a/php/src/Stat/VariantConfig.php
+++ b/php/src/Stat/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'stat',
             optionsFromArray: [\Grafana\Foundation\Stat\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Statetimeline/VariantConfig.php b/php/src/Statetimeline/VariantConfig.php
index 5a49bf8f..29230c3e 100644
--- a/php/src/Statetimeline/VariantConfig.php
+++ b/php/src/Statetimeline/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'state-timeline',
             optionsFromArray: [\Grafana\Foundation\Statetimeline\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Statetimeline\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Statetimeline\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Statushistory/VariantConfig.php b/php/src/Statushistory/VariantConfig.php
index aa6006a9..08615c6c 100644
--- a/php/src/Statushistory/VariantConfig.php
+++ b/php/src/Statushistory/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'status-history',
             optionsFromArray: [\Grafana\Foundation\Statushistory\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Statushistory\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Statushistory\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Table/VariantConfig.php b/php/src/Table/VariantConfig.php
index 8ba4c4af..04498879 100644
--- a/php/src/Table/VariantConfig.php
+++ b/php/src/Table/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'table',
             optionsFromArray: [\Grafana\Foundation\Table\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Table\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Table\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Tempo/TempoQuery.php b/php/src/Tempo/TempoQuery.php
index 1d34cef7..30033360 100644
--- a/php/src/Tempo/TempoQuery.php
+++ b/php/src/Tempo/TempoQuery.php
@@ -251,4 +251,9 @@ class TempoQuery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "tempo";
+    }
 }
diff --git a/php/src/Testdata/Dataquery.php b/php/src/Testdata/Dataquery.php
index 6dac9404..83bdc0a6 100644
--- a/php/src/Testdata/Dataquery.php
+++ b/php/src/Testdata/Dataquery.php
@@ -440,4 +440,9 @@ class Dataquery implements \JsonSerializable, \Grafana\Foundation\Cog\Dataquery
         }
         return $data;
     }
+
+    public function dataqueryType(): string
+    {
+        return "";
+    }
 }
diff --git a/php/src/Text/VariantConfig.php b/php/src/Text/VariantConfig.php
index 49ee7394..73560229 100644
--- a/php/src/Text/VariantConfig.php
+++ b/php/src/Text/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'text',
             optionsFromArray: [\Grafana\Foundation\Text\Options::class, 'fromArray'],
-            fieldConfigFromArray: null
+            fieldConfigFromArray: null,
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Timeseries/VariantConfig.php b/php/src/Timeseries/VariantConfig.php
index c4b1c035..c8dd99f0 100644
--- a/php/src/Timeseries/VariantConfig.php
+++ b/php/src/Timeseries/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'timeseries',
             optionsFromArray: [\Grafana\Foundation\Timeseries\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Timeseries\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Timeseries\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Trend/VariantConfig.php b/php/src/Trend/VariantConfig.php
index 61b3c894..a225ed39 100644
--- a/php/src/Trend/VariantConfig.php
+++ b/php/src/Trend/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'trend',
             optionsFromArray: [\Grafana\Foundation\Trend\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Trend\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Trend\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/php/src/Xychart/VariantConfig.php b/php/src/Xychart/VariantConfig.php
index 427124e0..184136a2 100644
--- a/php/src/Xychart/VariantConfig.php
+++ b/php/src/Xychart/VariantConfig.php
@@ -9,7 +9,7 @@ final class VariantConfig
         return new \Grafana\Foundation\Cog\PanelcfgConfig(
             identifier: 'xychart',
             optionsFromArray: [\Grafana\Foundation\Xychart\Options::class, 'fromArray'],
-            fieldConfigFromArray: [\Grafana\Foundation\Xychart\FieldConfig::class, 'fromArray']
+            fieldConfigFromArray: [\Grafana\Foundation\Xychart\FieldConfig::class, 'fromArray'],
         );
     }
 }
\ No newline at end of file
diff --git a/python/LICENSE.md b/python/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/python/LICENSE.md
+++ b/python/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/typescript/LICENSE.md b/typescript/LICENSE.md
index c319da33..bfbbbcf3 100644
--- a/typescript/LICENSE.md
+++ b/typescript/LICENSE.md
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-Copyright [yyyy] [name of copyright owner]
+Copyright 2024 Grafana Labs
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.

@K-Phoen K-Phoen merged commit dabec54 into main Oct 9, 2024
13 checks passed
@K-Phoen K-Phoen deleted the overridable-unmarshal branch October 9, 2024 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants