diff --git a/config/plugin.schema.json b/config/plugin.schema.json
index 503b441b..1898cd46 100644
--- a/config/plugin.schema.json
+++ b/config/plugin.schema.json
@@ -3,19 +3,19 @@
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"title": "plugin.json",
- "description": "The plugin.json file is required for all plugins. When Grafana starts, it scans the plugin folders and mounts every folder that contains a plugin.json file unless the folder contains a subfolder named dist. In that case, Grafana mounts the dist folder instead.",
+ "description": "The `plugin.json` file is required for all plugins. When Grafana starts, it scans the plugin folders and mounts every folder that contains a `plugin.json` file unless the folder contains a subfolder named `dist`. In that case, Grafana mounts the `dist` folder instead.",
"required": ["type", "name", "id", "info", "dependencies"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Unique name of the plugin. If the plugin is published on grafana.com, then the plugin `id` has to follow the naming conventions.",
- "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
+ "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
"description": "Plugin type.",
- "enum": ["app", "datasource", "panel", "renderer", "secretsmanager"]
+ "enum": ["app", "datasource", "panel", "renderer"]
},
"info": {
"type": "object",
@@ -99,11 +99,18 @@
"additionalProperties": false,
"properties": {
"name": {
- "type": "string"
+ "type": "string",
+ "description": "Display name of the link. Special names with predefined behavior:
• `documentation` - sets Documentation link on plugins detail page
• `repository` - used to determine and link to repository of the plugin
• `license` - sets License link on plugins detail page
• `raise issue` - sets `Raise an Issue` link on plugins detail page
• `sponsorship` - sets `Sponsor this developer` link on plugins detail page to direct users to how they can support your work"
},
"url": {
"type": "string",
- "format": "uri"
+ "format": "uri",
+ "description": "URL value to use for this specific link."
+ },
+ "target": {
+ "type": "string",
+ "description": "A string that indicates where to display the linked resource",
+ "enum": ["_blank", "_self", "_parent", "_top"]
}
}
}
@@ -147,7 +154,7 @@
},
"version": {
"type": "string",
- "description": "Project version of this commit, e.g. `6.7.x`.",
+ "description": "[SemVer](https://semver.org/) version of this commit, e.g. `6.7.1`.",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)|(\\%VERSION\\%)$"
}
}
@@ -170,11 +177,11 @@
"grafanaDependency": {
"type": "string",
"description": "Required Grafana version for this plugin. Validated using https://github.com/npm/node-semver.",
- "pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)(\\.[0-9x\\*]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x]+)(\\.[0-9x]+))?(\\-[0-9]+)?$"
+ "pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?)?$"
},
"plugins": {
"type": "array",
- "description": "An array of required plugins on which this plugin depends.",
+ "description": "An array of required plugins on which this plugin depends. Only non-core (that is, external plugins) have to be specified in this list.",
"additionalItems": false,
"items": {
"type": "object",
@@ -183,17 +190,30 @@
"properties": {
"id": {
"type": "string",
- "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
+ "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
- "enum": ["app", "datasource", "panel", "secretsmanager"]
+ "enum": ["app", "datasource", "panel"]
},
"name": {
"type": "string"
}
}
}
+ },
+ "extensions": {
+ "type": "object",
+ "description": "Plugin extensions that this plugin depends on.",
+ "properties": {
+ "exposedComponents": {
+ "type": "array",
+ "description": "An array of exposed component ids that this plugin depends on.",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
}
}
},
@@ -217,6 +237,10 @@
"type": "boolean",
"description": "If the plugin has a backend component."
},
+ "buildMode": {
+ "type": "string",
+ "description": "The build mode of the plugin. This field is set automatically at build time, so it should not be provided manually."
+ },
"builtIn": {
"type": "boolean",
"description": "[internal only] Indicates whether the plugin is developed and shipped as part of Grafana. Also known as a 'core plugin'."
@@ -259,7 +283,7 @@
},
"type": {
"type": "string",
- "enum": ["dashboard", "page", "panel", "datasource", "secretsmanager"]
+ "enum": ["dashboard", "page", "panel", "datasource"]
},
"name": {
"type": "string"
@@ -275,7 +299,7 @@
},
"action": {
"type": "string",
- "description": "The RBAC action a user must have to see this page in the navigation menu."
+ "description": "The RBAC action a user must have to see this page in the navigation menu. **Warning**: unless the action targets the plugin, only the action is verified, not what it applies to."
},
"path": {
"type": "string",
@@ -304,6 +328,10 @@
"type": "boolean",
"description": "For data source plugins, if the plugin supports metric queries. Used to enable the plugin in the panel editor."
},
+ "multiValueFilterOperators": {
+ "type": "boolean",
+ "description": "For data source plugins, if the plugin supports multi value operators in adhoc filters."
+ },
"pascalName": {
"type": "string",
"description": "[internal only] The PascalCase name for the plugin. Used for creating machine-friendly identifiers, typically in code generation. If not provided, defaults to name, but title-cased and sanitized (only alphabetical characters allowed).",
@@ -311,7 +339,11 @@
},
"preload": {
"type": "boolean",
- "description": "Initialize plugin on startup. By default, the plugin initializes on first use. Useful for app plugins that should load without user interaction."
+ "description": "Initialize plugin on startup. By default, the plugin initializes on first use, but when preload is set to true the plugin loads when the Grafana web app loads the first time. Only applicable to app plugins. When setting to `true`, implement [frontend code splitting](https://grafana.com/developers/plugin-tools/get-started/best-practices#app-plugins) to minimise performance implications."
+ },
+ "suggestions": {
+ "type": "boolean",
+ "description": "For panel plugins. If set to true, the plugin's suggestions supplier will be invoked and any suggestions returned will be included in the Suggestions pane in the Panel Editor."
},
"queryOptions": {
"type": "object",
@@ -334,10 +366,10 @@
},
"routes": {
"type": "array",
- "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](/developers/plugin-tools/create-a-plugin/extend-a-plugin/add-authentication-for-data-source-plugins).",
+ "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-authentication-for-data-source-plugins).",
"items": {
"type": "object",
- "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](/developers/plugin-tools/create-a-plugin/extend-a-plugin/add-authentication-for-data-source-plugins).",
+ "description": "",
"additionalProperties": false,
"properties": {
"path": {
@@ -346,7 +378,7 @@
},
"method": {
"type": "string",
- "description": "For data source plugins. Route method matches the HTTP verb like GET or POST. Multiple methods can be provided as a comma-separated list."
+ "description": "For data source plugins. Route method matches the HTTP verb like `GET` or `POST`. Multiple methods can be provided as a comma-separated list."
},
"url": {
"type": "string",
@@ -360,7 +392,7 @@
},
"reqAction": {
"type": "string",
- "description": "The RBAC action a user must have to use this route."
+ "description": "The RBAC action a user must have to use this route. **Warning**: unless the action targets the plugin (or a nested datasource plugin), only the action is verified, not what it applies to."
},
"headers": {
"type": "array",
@@ -476,8 +508,8 @@
},
"state": {
"type": "string",
- "description": "Marks a plugin as a pre-release.",
- "enum": ["alpha", "beta"]
+ "description": "Describes plugins life cycle status",
+ "enum": ["alpha", "beta", "stable", "deprecated"]
},
"streaming": {
"type": "boolean",
@@ -489,20 +521,22 @@
},
"iam": {
"type": "object",
- "description": "Identity and Access Management.",
+ "description": "Grafana reads the Identity and Access Management section and initializes a service account for the plugin, with a tailored set of [Grafana RBAC permissions](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/#rbac-permissions-actions-and-scopes). Grafana will share the service account's bearer token with the plugin backend using the `GF_PLUGIN_APP_CLIENT_SECRET` environment variable. Requires Grafana version 10.3.0 or later. Currently, this is behind the `externalServiceAccounts` feature toggle. To try this feature out, follow this [guide](https://grafana.com/developers/plugin-tools/how-to-guides/app-plugins/implement-rbac-in-app-plugins).",
"properties": {
"permissions": {
"type": "array",
- "description": "Permissions are the permissions that the plugin needs its associated service account to have",
+ "description": "Permissions are the permissions that the plugin needs its associated service account to have to query Grafana.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
- "type": "string"
+ "type": "string",
+ "description": "Action, for example: `teams:read`."
},
"scope": {
- "type": "string"
+ "type": "string",
+ "description": "The scope that the plugin needs to access e.g: `teams:*`."
}
}
}
@@ -511,14 +545,14 @@
},
"roles": {
"type": "array",
- "description": "List of RBAC roles and their default assignments.",
+ "description": "List of RBAC roles defined by the plugin and their default assignments to basic roles (`Viewer`, `Editor`, `Admin`, `Grafana Admin`). Requires Grafana version 9.4.0 or later.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"role": {
"type": "object",
- "description": "RBAC role definition to bundle related RBAC permissions on the plugin.",
+ "description": "A role groups your plugin's related RBAC permissions (ex: `Projects Admin` would group permissions to create, read, write and delete projects). The RBAC actions defined in your role must start with your plugin `id` (ex: `grafana-test-app.projects:read`).",
"additionalProperties": false,
"properties": {
"name": {
@@ -527,7 +561,7 @@
},
"description": {
"type": "string",
- "description": "Describe the aim of the role."
+ "description": "Describes the aim of the role."
},
"permissions": {
"type": "array",
@@ -549,7 +583,7 @@
},
"grants": {
"type": "array",
- "description": "Default assignments of the role to Grafana basic roles (Viewer, Editor, Admin, Grafana Admin)",
+ "description": "Default assignments of the role to Grafana basic roles (`Viewer`, `Editor`, `Admin`, `Grafana Admin`).",
"items": {
"type": "string"
}
@@ -558,32 +592,141 @@
}
},
"extensions": {
- "type": "array",
- "description": "The list of extensions that the plugin registers under other extension points.",
- "items": {
- "type": "object",
- "properties": {
- "extensionPointId": {
- "type": "string"
- },
- "title": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "type": {
- "type": "string",
- "enum": ["link", "component"]
+ "type": "object",
+ "description": "Plugin extensions are a way to extend either the UI of core Grafana or other plugins.",
+ "properties": {
+ "addedComponents": {
+ "type": "array",
+ "description": "This list must contain all component extensions that your plugin registers to other extension points using [`.addComponent()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addcomponent). **Components that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/user/profile/tab\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": " The title of your component extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your component extension."
+ }
+ },
+ "required": ["targets", "title"]
}
},
- "required": ["extensionPointId", "title", "type"]
+ "addedLinks": {
+ "type": "array",
+ "description": "This list must contain all link extensions that your plugin registers to other extension points using [`.addLink()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addlink). **Links that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/dashboard/panel/menu\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your link extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your link extension."
+ }
+ },
+ "required": ["targets", "title"]
+ }
+ },
+ "addedFunctions": {
+ "type": "array",
+ "description": "This list must contain all function extensions that your plugin registers to other extension points using [`.addedFunctions()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addfunction). **Functions that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/dashboard/panel/menu\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your function extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your function extension."
+ }
+ },
+ "required": ["targets", "title"]
+ }
+ },
+ "exposedComponents": {
+ "type": "array",
+ "description": "This list must contain all components that your plugin exposes using [`.exposeComponent()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#exposecomponent). **Components that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "A unique identifier for your exposed component. This is used to reference the component in other plugins. It must be in the following format: `{PLUGIN_ID}/name-of-component/v1`. It is recommended to add a version suffix to prevent future breaking changes. E.g.: `myorg-extensions-app/exposed-component/v1`.",
+ "pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your exposed component."
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your exposed component."
+ }
+ },
+ "required": ["id"]
+ }
+ },
+ "extensionPoints": {
+ "type": "array",
+ "description": "This list must contain all extension points that your plugin defines using [`usePluginLinks()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#usepluginlinks) or [`usePluginComponents()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#useplugincomponents). **Extension points that are not listed in here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "A unique identifier for your extension point. This is used to reference the extension point in other plugins. It must be in the following format: `{PLUGIN_ID}/name-of-my-extension-point/v1`. It is recommended to add a version suffix to prevent future breaking changes. E.g.: `myorg-extensions-app/extension-point/v1`.",
+ "pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your extension point."
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your extension point."
+ }
+ },
+ "required": ["id"]
+ }
+ }
}
},
- "apiVersion": {
- "type": "string",
- "description": "[internal only] The API version for the plugin. Used for Datasource API servers. This metadata is temporary and will be removed in the future.",
- "pattern": "^v([\\d]+)(?:(alpha|beta)([\\d]+))?$"
+ "languages": {
+ "type": "array",
+ "description": "The list of languages supported by the plugin. Each entry should be a locale identifier in the format `language-COUNTRY` (for example `en-US`, `es-ES`, `de-DE`).",
+ "items": {
+ "type": "string"
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/pkg/analysis/passes/metadatavalid/testdata/schema.json b/pkg/analysis/passes/metadatavalid/testdata/schema.json
index 6c590417..1898cd46 100644
--- a/pkg/analysis/passes/metadatavalid/testdata/schema.json
+++ b/pkg/analysis/passes/metadatavalid/testdata/schema.json
@@ -3,19 +3,19 @@
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"title": "plugin.json",
- "description": "The plugin.json file is required for all plugins. When Grafana starts, it scans the plugin folders and mounts every folder that contains a plugin.json file unless the folder contains a subfolder named dist. In that case, Grafana mounts the dist folder instead.",
+ "description": "The `plugin.json` file is required for all plugins. When Grafana starts, it scans the plugin folders and mounts every folder that contains a `plugin.json` file unless the folder contains a subfolder named `dist`. In that case, Grafana mounts the `dist` folder instead.",
"required": ["type", "name", "id", "info", "dependencies"],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Unique name of the plugin. If the plugin is published on grafana.com, then the plugin `id` has to follow the naming conventions.",
- "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
+ "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
"description": "Plugin type.",
- "enum": ["app", "datasource", "panel", "renderer", "secretsmanager"]
+ "enum": ["app", "datasource", "panel", "renderer"]
},
"info": {
"type": "object",
@@ -99,11 +99,18 @@
"additionalProperties": false,
"properties": {
"name": {
- "type": "string"
+ "type": "string",
+ "description": "Display name of the link. Special names with predefined behavior:
• `documentation` - sets Documentation link on plugins detail page
• `repository` - used to determine and link to repository of the plugin
• `license` - sets License link on plugins detail page
• `raise issue` - sets `Raise an Issue` link on plugins detail page
• `sponsorship` - sets `Sponsor this developer` link on plugins detail page to direct users to how they can support your work"
},
"url": {
"type": "string",
- "format": "uri"
+ "format": "uri",
+ "description": "URL value to use for this specific link."
+ },
+ "target": {
+ "type": "string",
+ "description": "A string that indicates where to display the linked resource",
+ "enum": ["_blank", "_self", "_parent", "_top"]
}
}
}
@@ -147,7 +154,7 @@
},
"version": {
"type": "string",
- "description": "Project version of this commit, e.g. `6.7.x`.",
+ "description": "[SemVer](https://semver.org/) version of this commit, e.g. `6.7.1`.",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)|(\\%VERSION\\%)$"
}
}
@@ -170,29 +177,39 @@
"grafanaDependency": {
"type": "string",
"description": "Required Grafana version for this plugin. Validated using https://github.com/npm/node-semver.",
- "pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)(\\.[0-9x\\*]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x]+)(\\.[0-9x]+))?(\\-[0-9]+)?$"
+ "pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?)?$"
},
"plugins": {
"type": "array",
- "description": "An array of required plugins on which this plugin depends.",
+ "description": "An array of required plugins on which this plugin depends. Only non-core (that is, external plugins) have to be specified in this list.",
"additionalItems": false,
"items": {
"type": "object",
"description": "Plugin dependency. Used to display information about plugin dependencies in the Grafana UI.",
- "required": ["id", "name", "type", "version"],
+ "required": ["id", "name", "type"],
"properties": {
"id": {
"type": "string",
- "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource|secretsmanager)$"
+ "pattern": "^[0-9a-z]+\\-([0-9a-z]+\\-)?(app|panel|datasource)$"
},
"type": {
"type": "string",
- "enum": ["app", "datasource", "panel", "secretsmanager"]
+ "enum": ["app", "datasource", "panel"]
},
"name": {
"type": "string"
- },
- "version": {
+ }
+ }
+ }
+ },
+ "extensions": {
+ "type": "object",
+ "description": "Plugin extensions that this plugin depends on.",
+ "properties": {
+ "exposedComponents": {
+ "type": "array",
+ "description": "An array of exposed component ids that this plugin depends on.",
+ "items": {
"type": "string"
}
}
@@ -220,6 +237,10 @@
"type": "boolean",
"description": "If the plugin has a backend component."
},
+ "buildMode": {
+ "type": "string",
+ "description": "The build mode of the plugin. This field is set automatically at build time, so it should not be provided manually."
+ },
"builtIn": {
"type": "boolean",
"description": "[internal only] Indicates whether the plugin is developed and shipped as part of Grafana. Also known as a 'core plugin'."
@@ -262,7 +283,7 @@
},
"type": {
"type": "string",
- "enum": ["dashboard", "page", "panel", "datasource", "secretsmanager"]
+ "enum": ["dashboard", "page", "panel", "datasource"]
},
"name": {
"type": "string"
@@ -278,7 +299,7 @@
},
"action": {
"type": "string",
- "description": "The RBAC action a user must have to see this page in the navigation menu."
+ "description": "The RBAC action a user must have to see this page in the navigation menu. **Warning**: unless the action targets the plugin, only the action is verified, not what it applies to."
},
"path": {
"type": "string",
@@ -307,6 +328,10 @@
"type": "boolean",
"description": "For data source plugins, if the plugin supports metric queries. Used to enable the plugin in the panel editor."
},
+ "multiValueFilterOperators": {
+ "type": "boolean",
+ "description": "For data source plugins, if the plugin supports multi value operators in adhoc filters."
+ },
"pascalName": {
"type": "string",
"description": "[internal only] The PascalCase name for the plugin. Used for creating machine-friendly identifiers, typically in code generation. If not provided, defaults to name, but title-cased and sanitized (only alphabetical characters allowed).",
@@ -314,7 +339,11 @@
},
"preload": {
"type": "boolean",
- "description": "Initialize plugin on startup. By default, the plugin initializes on first use. Useful for app plugins that should load without user interaction."
+ "description": "Initialize plugin on startup. By default, the plugin initializes on first use, but when preload is set to true the plugin loads when the Grafana web app loads the first time. Only applicable to app plugins. When setting to `true`, implement [frontend code splitting](https://grafana.com/developers/plugin-tools/get-started/best-practices#app-plugins) to minimise performance implications."
+ },
+ "suggestions": {
+ "type": "boolean",
+ "description": "For panel plugins. If set to true, the plugin's suggestions supplier will be invoked and any suggestions returned will be included in the Suggestions pane in the Panel Editor."
},
"queryOptions": {
"type": "object",
@@ -337,10 +366,10 @@
},
"routes": {
"type": "array",
- "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](/developers/plugin-tools/create-a-plugin/extend-a-plugin/add-authentication-for-data-source-plugins).",
+ "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-authentication-for-data-source-plugins).",
"items": {
"type": "object",
- "description": "For data source plugins. Proxy routes used for plugin authentication and adding headers to HTTP requests made by the plugin. For more information, refer to [Authentication for data source plugins](/developers/plugin-tools/create-a-plugin/extend-a-plugin/add-authentication-for-data-source-plugins).",
+ "description": "",
"additionalProperties": false,
"properties": {
"path": {
@@ -349,7 +378,7 @@
},
"method": {
"type": "string",
- "description": "For data source plugins. Route method matches the HTTP verb like GET or POST. Multiple methods can be provided as a comma-separated list."
+ "description": "For data source plugins. Route method matches the HTTP verb like `GET` or `POST`. Multiple methods can be provided as a comma-separated list."
},
"url": {
"type": "string",
@@ -363,7 +392,7 @@
},
"reqAction": {
"type": "string",
- "description": "The RBAC action a user must have to use this route."
+ "description": "The RBAC action a user must have to use this route. **Warning**: unless the action targets the plugin (or a nested datasource plugin), only the action is verified, not what it applies to."
},
"headers": {
"type": "array",
@@ -479,8 +508,8 @@
},
"state": {
"type": "string",
- "description": "Marks a plugin as a pre-release.",
- "enum": ["alpha", "beta"]
+ "description": "Describes plugins life cycle status",
+ "enum": ["alpha", "beta", "stable", "deprecated"]
},
"streaming": {
"type": "boolean",
@@ -492,20 +521,22 @@
},
"iam": {
"type": "object",
- "description": "Identity and Access Management.",
+ "description": "Grafana reads the Identity and Access Management section and initializes a service account for the plugin, with a tailored set of [Grafana RBAC permissions](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/#rbac-permissions-actions-and-scopes). Grafana will share the service account's bearer token with the plugin backend using the `GF_PLUGIN_APP_CLIENT_SECRET` environment variable. Requires Grafana version 10.3.0 or later. Currently, this is behind the `externalServiceAccounts` feature toggle. To try this feature out, follow this [guide](https://grafana.com/developers/plugin-tools/how-to-guides/app-plugins/implement-rbac-in-app-plugins).",
"properties": {
"permissions": {
"type": "array",
- "description": "Permissions are the permissions that the plugin needs its associated service account to have",
+ "description": "Permissions are the permissions that the plugin needs its associated service account to have to query Grafana.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
- "type": "string"
+ "type": "string",
+ "description": "Action, for example: `teams:read`."
},
"scope": {
- "type": "string"
+ "type": "string",
+ "description": "The scope that the plugin needs to access e.g: `teams:*`."
}
}
}
@@ -514,14 +545,14 @@
},
"roles": {
"type": "array",
- "description": "List of RBAC roles and their default assignments.",
+ "description": "List of RBAC roles defined by the plugin and their default assignments to basic roles (`Viewer`, `Editor`, `Admin`, `Grafana Admin`). Requires Grafana version 9.4.0 or later.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"role": {
"type": "object",
- "description": "RBAC role definition to bundle related RBAC permissions on the plugin.",
+ "description": "A role groups your plugin's related RBAC permissions (ex: `Projects Admin` would group permissions to create, read, write and delete projects). The RBAC actions defined in your role must start with your plugin `id` (ex: `grafana-test-app.projects:read`).",
"additionalProperties": false,
"properties": {
"name": {
@@ -530,7 +561,7 @@
},
"description": {
"type": "string",
- "description": "Describe the aim of the role."
+ "description": "Describes the aim of the role."
},
"permissions": {
"type": "array",
@@ -552,7 +583,7 @@
},
"grants": {
"type": "array",
- "description": "Default assignments of the role to Grafana basic roles (Viewer, Editor, Admin, Grafana Admin)",
+ "description": "Default assignments of the role to Grafana basic roles (`Viewer`, `Editor`, `Admin`, `Grafana Admin`).",
"items": {
"type": "string"
}
@@ -561,32 +592,141 @@
}
},
"extensions": {
- "type": "array",
- "description": "The list of extensions that the plugin registers under other extension points.",
- "items": {
- "type": "object",
- "properties": {
- "extensionPointId": {
- "type": "string"
- },
- "title": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "type": {
- "type": "string",
- "enum": ["link", "component"]
+ "type": "object",
+ "description": "Plugin extensions are a way to extend either the UI of core Grafana or other plugins.",
+ "properties": {
+ "addedComponents": {
+ "type": "array",
+ "description": "This list must contain all component extensions that your plugin registers to other extension points using [`.addComponent()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addcomponent). **Components that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/user/profile/tab\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": " The title of your component extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your component extension."
+ }
+ },
+ "required": ["targets", "title"]
}
},
- "required": ["extensionPointId", "title", "type"]
+ "addedLinks": {
+ "type": "array",
+ "description": "This list must contain all link extensions that your plugin registers to other extension points using [`.addLink()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addlink). **Links that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/dashboard/panel/menu\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your link extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your link extension."
+ }
+ },
+ "required": ["targets", "title"]
+ }
+ },
+ "addedFunctions": {
+ "type": "array",
+ "description": "This list must contain all function extensions that your plugin registers to other extension points using [`.addedFunctions()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#addfunction). **Functions that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "targets": {
+ "type": "array",
+ "description": "The extension point ids your plugin registers the extension to, e.g. `[\"grafana/dashboard/panel/menu\"]`",
+ "items": {
+ "type": "string"
+ }
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your function extension.",
+ "minLength": 10
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your function extension."
+ }
+ },
+ "required": ["targets", "title"]
+ }
+ },
+ "exposedComponents": {
+ "type": "array",
+ "description": "This list must contain all components that your plugin exposes using [`.exposeComponent()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#exposecomponent). **Components that are not listed here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "A unique identifier for your exposed component. This is used to reference the component in other plugins. It must be in the following format: `{PLUGIN_ID}/name-of-component/v1`. It is recommended to add a version suffix to prevent future breaking changes. E.g.: `myorg-extensions-app/exposed-component/v1`.",
+ "pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your exposed component."
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your exposed component."
+ }
+ },
+ "required": ["id"]
+ }
+ },
+ "extensionPoints": {
+ "type": "array",
+ "description": "This list must contain all extension points that your plugin defines using [`usePluginLinks()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#usepluginlinks) or [`usePluginComponents()`](https://grafana.com/developers/plugin-tools/reference/ui-extensions-reference/ui-extensions#useplugincomponents). **Extension points that are not listed in here won't work.**",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "A unique identifier for your extension point. This is used to reference the extension point in other plugins. It must be in the following format: `{PLUGIN_ID}/name-of-my-extension-point/v1`. It is recommended to add a version suffix to prevent future breaking changes. E.g.: `myorg-extensions-app/extension-point/v1`.",
+ "pattern": "^[0-9a-z]+-([0-9a-z]+-)?(app|panel|datasource)\\/[a-zA-Z0-9_-]+\\/v[0-9_.-]+$"
+ },
+ "title": {
+ "type": "string",
+ "description": "The title of your extension point."
+ },
+ "description": {
+ "type": "string",
+ "description": "Additional information about your extension point."
+ }
+ },
+ "required": ["id"]
+ }
+ }
}
},
- "apiVersion": {
- "type": "string",
- "description": "[internal only] The API version for the plugin. Used for Datasource API servers. This metadata is temporary and will be removed in the future.",
- "pattern": "^v([\\d]+)(?:(alpha|beta)([\\d]+))?$"
+ "languages": {
+ "type": "array",
+ "description": "The list of languages supported by the plugin. Each entry should be a locale identifier in the format `language-COUNTRY` (for example `en-US`, `es-ES`, `de-DE`).",
+ "items": {
+ "type": "string"
+ }
}
}
-}
\ No newline at end of file
+}