| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | package plugins | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/google/go-cmp/cmp" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/org" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func Test_ReadPluginJSON(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name       string | 
					
						
							|  |  |  | 		pluginJSON func(t *testing.T) io.ReadCloser | 
					
						
							|  |  |  | 		expected   JSONData | 
					
						
							|  |  |  | 		err        error | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Valid plugin", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				reader, err := os.Open("manager/testdata/test-app/plugin.json") | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				return reader | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "test-app", | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 				Type: TypeApp, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Name: "Test App", | 
					
						
							|  |  |  | 				Info: Info{ | 
					
						
							|  |  |  | 					Author: InfoLink{ | 
					
						
							|  |  |  | 						Name: "Test Inc.", | 
					
						
							|  |  |  | 						URL:  "http://test.com", | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					Description: "Official Grafana Test App & Dashboard bundle", | 
					
						
							|  |  |  | 					Version:     "1.0.0", | 
					
						
							|  |  |  | 					Links: []InfoLink{ | 
					
						
							|  |  |  | 						{Name: "Project site", URL: "http://project.com"}, | 
					
						
							|  |  |  | 						{Name: "License & Terms", URL: "http://license.com"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					Logos: Logos{ | 
					
						
							|  |  |  | 						Small: "img/logo_small.png", | 
					
						
							|  |  |  | 						Large: "img/logo_large.png", | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					Screenshots: []Screenshots{ | 
					
						
							|  |  |  | 						{Path: "img/screenshot1.png", Name: "img1"}, | 
					
						
							|  |  |  | 						{Path: "img/screenshot2.png", Name: "img2"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2024-02-14 21:30:24 +08:00
										 |  |  | 					Updated:  "2015-02-10", | 
					
						
							|  |  |  | 					Keywords: []string{"test"}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks:        []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents:   []AddedComponent{}, | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "3.x.x", | 
					
						
							|  |  |  | 					Plugins: []Dependency{ | 
					
						
							| 
									
										
										
										
											2025-04-10 22:02:05 +08:00
										 |  |  | 						{Type: "datasource", ID: "graphite", Name: "Graphite"}, | 
					
						
							|  |  |  | 						{Type: "panel", ID: "graph", Name: "Graph"}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Includes: []*Includes{ | 
					
						
							| 
									
										
										
										
											2024-07-30 02:56:09 +08:00
										 |  |  | 					{Name: "Nginx Connections", Path: "dashboards/connections.json", Type: "dashboard", Role: org.RoleViewer, Action: ActionAppAccess}, | 
					
						
							|  |  |  | 					{Name: "Nginx Memory", Path: "dashboards/memory.json", Type: "dashboard", Role: org.RoleViewer, Action: ActionAppAccess}, | 
					
						
							|  |  |  | 					{Name: "Nginx Panel", Type: "panel", Role: org.RoleViewer, Action: ActionAppAccess}, | 
					
						
							|  |  |  | 					{Name: "Nginx Datasource", Type: "datasource", Role: org.RoleViewer, Action: ActionAppAccess}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				Backend: false, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Invalid plugin JSON", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				reader, err := os.Open("manager/testdata/invalid-plugin-json/plugin.json") | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				return reader | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			err: ErrInvalidPluginJSON, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Default value overrides", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "grafana-piechart-panel", | 
					
						
							|  |  |  | 					"name": "This will be overwritten", | 
					
						
							|  |  |  | 					"type": "panel", | 
					
						
							|  |  |  | 					"includes": [ | 
					
						
							|  |  |  | 						{"type": "dashboard", "name": "Pie Charts", "path": "dashboards/demo.json"} | 
					
						
							|  |  |  | 					] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "grafana-piechart-panel", | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 				Type: TypePanel, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Name: "Pie Chart (old)", | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedLinks:      []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:  []AddedFunction{}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				Includes: []*Includes{ | 
					
						
							|  |  |  | 					{Name: "Pie Charts", Path: "dashboards/demo.json", Type: "dashboard", Role: org.RoleViewer}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-06-07 11:09:29 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Phlare<>Pyroscope rebranding -- hardcoded alias", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "grafana-pyroscope-datasource", | 
					
						
							| 
									
										
										
										
											2024-02-14 21:30:24 +08:00
										 |  |  | 					"type": "datasource", | 
					
						
							| 
									
										
										
										
											2023-09-29 23:20:37 +08:00
										 |  |  | 					"aliasIDs": ["phlare"] | 
					
						
							| 
									
										
										
										
											2023-06-07 11:09:29 +08:00
										 |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							| 
									
										
										
										
											2023-09-29 23:20:37 +08:00
										 |  |  | 				ID:       "grafana-pyroscope-datasource", | 
					
						
							|  |  |  | 				AliasIDs: []string{"phlare"}, // Hardcoded from the parser
 | 
					
						
							|  |  |  | 				Type:     TypeDataSource, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedLinks:      []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:  []AddedFunction{}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-07 11:09:29 +08:00
										 |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaDependency: "", | 
					
						
							|  |  |  | 					GrafanaVersion:    "*", | 
					
						
							|  |  |  | 					Plugins:           []Dependency{}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2023-06-07 11:09:29 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read the latest versions of extensions information (v2)", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-extensions-app", | 
					
						
							|  |  |  | 					"name": "Extensions App", | 
					
						
							|  |  |  | 					"type": "app", | 
					
						
							|  |  |  | 					"extensions": { | 
					
						
							|  |  |  | 						"addedLinks": [ | 
					
						
							|  |  |  | 							{ | 
					
						
							|  |  |  | 								"title": "Added link 1", | 
					
						
							|  |  |  | 								"description": "Added link 1 description", | 
					
						
							|  |  |  | 								"targets": ["grafana/dashboard/panel/menu"] | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						], | 
					
						
							|  |  |  | 						"addedComponents": [ | 
					
						
							|  |  |  | 							{ | 
					
						
							|  |  |  | 								"title": "Added component 1", | 
					
						
							|  |  |  | 								"description": "Added component 1 description", | 
					
						
							|  |  |  | 								"targets": ["grafana/user/profile/tab"] | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						], | 
					
						
							|  |  |  | 						"exposedComponents": [ | 
					
						
							|  |  |  | 							{ | 
					
						
							|  |  |  | 								"title": "Exposed component 1", | 
					
						
							|  |  |  | 								"description": "Exposed component 1 description", | 
					
						
							|  |  |  | 								"id": "myorg-extensions-app/component-1/v1" | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						], | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 						"addedFunctions": [ | 
					
						
							|  |  |  |               {"targets": ["foo/bar"], "title":"some hook"} | 
					
						
							|  |  |  |             ], | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 						"extensionPoints": [ | 
					
						
							|  |  |  | 							{ | 
					
						
							|  |  |  | 								"title": "Extension point 1", | 
					
						
							|  |  |  | 								"description": "Extension points 1 description", | 
					
						
							|  |  |  | 								"id": "myorg-extensions-app/extensions-point-1/v1" | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						] | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "myorg-extensions-app", | 
					
						
							|  |  |  | 				Name: "Extensions App", | 
					
						
							|  |  |  | 				Type: TypeApp, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks: []AddedLink{ | 
					
						
							|  |  |  | 						{Title: "Added link 1", Description: "Added link 1 description", Targets: []string{"grafana/dashboard/panel/menu"}}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{ | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 						{Title: "Added component 1", Description: "Added component 1 description", Targets: []string{"grafana/user/profile/tab"}}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					ExposedComponents: []ExposedComponent{ | 
					
						
							|  |  |  | 						{Id: "myorg-extensions-app/component-1/v1", Title: "Exposed component 1", Description: "Exposed component 1 description"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					ExtensionPoints: []ExtensionPoint{ | 
					
						
							|  |  |  | 						{Id: "myorg-extensions-app/extensions-point-1/v1", Title: "Extension point 1", Description: "Extension points 1 description"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedFunctions: []AddedFunction{ | 
					
						
							|  |  |  | 						{Targets: []string{"foo/bar"}, Title: "some hook"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read deprecated extensions info (v1) and parse it as v2", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-extensions-app", | 
					
						
							|  |  |  | 					"name": "Extensions App", | 
					
						
							|  |  |  | 					"type": "app", | 
					
						
							|  |  |  | 					"extensions": [ | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							"extensionPointId": "grafana/dashboard/panel/menu", | 
					
						
							|  |  |  | 							"title": "Added link 1", | 
					
						
							|  |  |  | 							"description": "Added link 1 description", | 
					
						
							|  |  |  | 							"type": "link" | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							"extensionPointId": "grafana/dashboard/panel/menu", | 
					
						
							|  |  |  | 							"title": "Added link 2", | 
					
						
							|  |  |  | 							"description": "Added link 2 description", | 
					
						
							|  |  |  | 							"type": "link" | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						{ | 
					
						
							|  |  |  | 							"extensionPointId": "grafana/user/profile/tab", | 
					
						
							|  |  |  | 							"title": "Added component 1", | 
					
						
							|  |  |  | 							"description": "Added component 1 description", | 
					
						
							|  |  |  | 							"type": "component" | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "myorg-extensions-app", | 
					
						
							|  |  |  | 				Name: "Extensions App", | 
					
						
							|  |  |  | 				Type: TypeApp, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks: []AddedLink{ | 
					
						
							|  |  |  | 						{Title: "Added link 1", Description: "Added link 1 description", Targets: []string{"grafana/dashboard/panel/menu"}}, | 
					
						
							|  |  |  | 						{Title: "Added link 2", Description: "Added link 2 description", Targets: []string{"grafana/dashboard/panel/menu"}}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{ | 
					
						
							|  |  |  | 						{Title: "Added component 1", Description: "Added component 1 description", Targets: []string{"grafana/user/profile/tab"}}, | 
					
						
							|  |  |  | 					}, | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "works if extensions info is empty", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-extensions-app", | 
					
						
							|  |  |  | 					"name": "Extensions App", | 
					
						
							|  |  |  | 					"type": "app", | 
					
						
							|  |  |  | 					"extensions": [] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "myorg-extensions-app", | 
					
						
							|  |  |  | 				Name: "Extensions App", | 
					
						
							|  |  |  | 				Type: TypeApp, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedLinks:      []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:  []AddedFunction{}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "works if extensions info is completely missing", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-extensions-app", | 
					
						
							|  |  |  | 					"name": "Extensions App", | 
					
						
							|  |  |  | 					"type": "app" | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "myorg-extensions-app", | 
					
						
							|  |  |  | 				Name: "Extensions App", | 
					
						
							|  |  |  | 				Type: TypeApp, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedLinks:      []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents: []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:  []AddedFunction{}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read extensions related dependencies", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-extensions-app", | 
					
						
							|  |  |  | 					"name": "Extensions App", | 
					
						
							|  |  |  | 					"type": "app", | 
					
						
							|  |  |  | 					"dependencies": { | 
					
						
							|  |  |  | 						"grafanaDependency": "10.0.0", | 
					
						
							|  |  |  | 						"extensions": { | 
					
						
							|  |  |  | 							"exposedComponents": ["myorg-extensions-app/component-1/v1"] | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:   "myorg-extensions-app", | 
					
						
							|  |  |  | 				Name: "Extensions App", | 
					
						
							|  |  |  | 				Type: TypeApp, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks:        []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents:   []AddedComponent{}, | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion:    "*", | 
					
						
							|  |  |  | 					GrafanaDependency: "10.0.0", | 
					
						
							|  |  |  | 					Plugins:           []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{"myorg-extensions-app/component-1/v1"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2025-03-31 12:38:38 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read languages in a datasource plugin", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-languages-datasource", | 
					
						
							|  |  |  | 					"name": "Languages Datasource", | 
					
						
							|  |  |  | 					"type": "datasource", | 
					
						
							|  |  |  | 					"languages": ["en-US", "pt-BR"] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:        "myorg-languages-datasource", | 
					
						
							|  |  |  | 				Name:      "Languages Datasource", | 
					
						
							|  |  |  | 				Type:      TypeDataSource, | 
					
						
							|  |  |  | 				Languages: []string{"en-US", "pt-BR"}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks:        []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents:   []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							|  |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read languages in a panel plugin", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-languages-panel", | 
					
						
							|  |  |  | 					"name": "Languages Panel", | 
					
						
							|  |  |  | 					"type": "panel", | 
					
						
							|  |  |  | 					"languages": ["en-US", "pt-BR"] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:        "myorg-languages-panel", | 
					
						
							|  |  |  | 				Name:      "Languages Panel", | 
					
						
							|  |  |  | 				Type:      TypePanel, | 
					
						
							|  |  |  | 				Languages: []string{"en-US", "pt-BR"}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks:        []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents:   []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							|  |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "can read languages in an app plugin", | 
					
						
							|  |  |  | 			pluginJSON: func(t *testing.T) io.ReadCloser { | 
					
						
							|  |  |  | 				pJSON := `{ | 
					
						
							|  |  |  | 					"id": "myorg-languages-app", | 
					
						
							|  |  |  | 					"name": "Languages App", | 
					
						
							|  |  |  | 					"type": "app", | 
					
						
							|  |  |  | 					"languages": ["en-US", "pt-BR"] | 
					
						
							|  |  |  | 				}` | 
					
						
							|  |  |  | 				return io.NopCloser(strings.NewReader(pJSON)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expected: JSONData{ | 
					
						
							|  |  |  | 				ID:        "myorg-languages-app", | 
					
						
							|  |  |  | 				Name:      "Languages App", | 
					
						
							|  |  |  | 				Type:      TypeApp, | 
					
						
							|  |  |  | 				Languages: []string{"en-US", "pt-BR"}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Extensions: Extensions{ | 
					
						
							|  |  |  | 					AddedLinks:        []AddedLink{}, | 
					
						
							|  |  |  | 					AddedComponents:   []AddedComponent{}, | 
					
						
							|  |  |  | 					AddedFunctions:    []AddedFunction{}, | 
					
						
							|  |  |  | 					ExposedComponents: []ExposedComponent{}, | 
					
						
							|  |  |  | 					ExtensionPoints:   []ExtensionPoint{}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Dependencies: Dependencies{ | 
					
						
							|  |  |  | 					GrafanaVersion: "*", | 
					
						
							|  |  |  | 					Plugins:        []Dependency{}, | 
					
						
							|  |  |  | 					Extensions: ExtensionsDependencies{ | 
					
						
							|  |  |  | 						ExposedComponents: []string{}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			p := tt.pluginJSON(t) | 
					
						
							|  |  |  | 			got, err := ReadPluginJSON(p) | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Check if the test returns the same error as expected
 | 
					
						
							|  |  |  | 			// (unneccary to check further if there is an error at this point)
 | 
					
						
							|  |  |  | 			if tt.err == nil && err != nil { | 
					
						
							|  |  |  | 				t.Errorf("Error while reading pluginJSON: %+v", err) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Check if the test returns the same error as expected
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 			if tt.err != nil { | 
					
						
							|  |  |  | 				require.ErrorIs(t, err, tt.err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Check if the test returns the expected pluginJSONData
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 			if !cmp.Equal(got, tt.expected) { | 
					
						
							|  |  |  | 				t.Errorf("Unexpected pluginJSONData: %v", cmp.Diff(got, tt.expected)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Should be able to close the reader
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 			require.NoError(t, p.Close()) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func Test_validatePluginJSON(t *testing.T) { | 
					
						
							|  |  |  | 	type args struct { | 
					
						
							|  |  |  | 		data JSONData | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name string | 
					
						
							|  |  |  | 		args args | 
					
						
							|  |  |  | 		err  error | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Valid case", | 
					
						
							|  |  |  | 			args: args{ | 
					
						
							|  |  |  | 				data: JSONData{ | 
					
						
							|  |  |  | 					ID:   "grafana-plugin-id", | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 					Type: TypeDataSource, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Invalid plugin ID", | 
					
						
							|  |  |  | 			args: args{ | 
					
						
							|  |  |  | 				data: JSONData{ | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 					Type: TypePanel, | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			err: ErrInvalidPluginJSON, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "Invalid plugin type", | 
					
						
							|  |  |  | 			args: args{ | 
					
						
							|  |  |  | 				data: JSONData{ | 
					
						
							|  |  |  | 					ID:   "grafana-plugin-id", | 
					
						
							|  |  |  | 					Type: "test", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			err: ErrInvalidPluginJSON, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			if err := validatePluginJSON(tt.args.data); !errors.Is(err, tt.err) { | 
					
						
							|  |  |  | 				t.Errorf("validatePluginJSON() = %v, want %v", err, tt.err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |