@marble-sh/backstage-plugin-grafana-common 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/annotations.cjs.js +32 -0
- package/dist/annotations.cjs.js.map +1 -0
- package/dist/annotations.esm.js +20 -0
- package/dist/annotations.esm.js.map +1 -0
- package/dist/index.cjs.js +21 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.esm.js +3 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/selectors.cjs.js +30 -0
- package/dist/selectors.cjs.js.map +1 -0
- package/dist/selectors.esm.js +27 -0
- package/dist/selectors.esm.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @marble-sh/backstage-plugin-grafana-common
|
|
2
|
+
|
|
3
|
+
Common functionality shared by the Grafana Backstage plugins. This package is
|
|
4
|
+
isomorphic (usable from both frontend and backend) and has no heavy
|
|
5
|
+
dependencies, so it can be safely imported anywhere.
|
|
6
|
+
|
|
7
|
+
It provides:
|
|
8
|
+
|
|
9
|
+
- **Entity annotations** and helpers for selecting Grafana content per entity.
|
|
10
|
+
- **Data-transfer types** describing the shapes returned by the Grafana backend
|
|
11
|
+
REST API.
|
|
12
|
+
|
|
13
|
+
## Entity annotations
|
|
14
|
+
|
|
15
|
+
| Annotation | Helper | Meaning |
|
|
16
|
+
| ------------------------------ | ------------------------ | ------------------------------------------------------------------ |
|
|
17
|
+
| `grafana/instance` | `getGrafanaInstanceName` | Which configured Grafana instance the entity belongs to. |
|
|
18
|
+
| `grafana/dashboard-selector` | `getDashboardSelector` | Comma-separated title substrings; any match selects the dashboard. |
|
|
19
|
+
| `grafana/tag-selector` | `getTagSelector` | A comma-separated list of dashboard tags. |
|
|
20
|
+
| `grafana/alert-label-selector` | `getAlertLabelSelector` | A `key=value,...` list of alert label matchers. |
|
|
21
|
+
|
|
22
|
+
`isGrafanaAvailable(entity)` returns `true` when an entity carries any of the
|
|
23
|
+
above annotations, and is used to gate the Grafana entity tabs and cards.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
isGrafanaAvailable,
|
|
28
|
+
getGrafanaInstanceName,
|
|
29
|
+
} from '@marble-sh/backstage-plugin-grafana-common';
|
|
30
|
+
|
|
31
|
+
if (isGrafanaAvailable(entity)) {
|
|
32
|
+
const instance = getGrafanaInstanceName(entity); // e.g. "production"
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Types
|
|
37
|
+
|
|
38
|
+
The package exports the response and entity types used by the plugins, including
|
|
39
|
+
`GrafanaInstanceInfo`, `GrafanaDashboard`, `GrafanaAlert`, and the
|
|
40
|
+
`List*Response` bodies returned by the backend.
|
|
41
|
+
|
|
42
|
+
## Testing
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
yarn workspace @marble-sh/backstage-plugin-grafana-common test
|
|
46
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const GRAFANA_ANNOTATION_INSTANCE = "grafana/instance";
|
|
4
|
+
const GRAFANA_ANNOTATION_DASHBOARD_SELECTOR = "grafana/dashboard-selector";
|
|
5
|
+
const GRAFANA_ANNOTATION_TAG_SELECTOR = "grafana/tag-selector";
|
|
6
|
+
const GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR = "grafana/alert-label-selector";
|
|
7
|
+
const read = (entity, key) => {
|
|
8
|
+
const value = entity.metadata.annotations?.[key];
|
|
9
|
+
return value ? value : void 0;
|
|
10
|
+
};
|
|
11
|
+
const getGrafanaInstanceName = (entity) => read(entity, GRAFANA_ANNOTATION_INSTANCE);
|
|
12
|
+
const getDashboardSelector = (entity) => read(entity, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR);
|
|
13
|
+
const getTagSelector = (entity) => read(entity, GRAFANA_ANNOTATION_TAG_SELECTOR);
|
|
14
|
+
const getAlertLabelSelector = (entity) => read(entity, GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR);
|
|
15
|
+
const isDashboardsAvailable = (entity) => Boolean(
|
|
16
|
+
getGrafanaInstanceName(entity) || getDashboardSelector(entity) || getTagSelector(entity)
|
|
17
|
+
);
|
|
18
|
+
const isAlertsAvailable = (entity) => Boolean(getGrafanaInstanceName(entity) || getAlertLabelSelector(entity));
|
|
19
|
+
const isGrafanaAvailable = (entity) => isDashboardsAvailable(entity) || isAlertsAvailable(entity);
|
|
20
|
+
|
|
21
|
+
exports.GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR = GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR;
|
|
22
|
+
exports.GRAFANA_ANNOTATION_DASHBOARD_SELECTOR = GRAFANA_ANNOTATION_DASHBOARD_SELECTOR;
|
|
23
|
+
exports.GRAFANA_ANNOTATION_INSTANCE = GRAFANA_ANNOTATION_INSTANCE;
|
|
24
|
+
exports.GRAFANA_ANNOTATION_TAG_SELECTOR = GRAFANA_ANNOTATION_TAG_SELECTOR;
|
|
25
|
+
exports.getAlertLabelSelector = getAlertLabelSelector;
|
|
26
|
+
exports.getDashboardSelector = getDashboardSelector;
|
|
27
|
+
exports.getGrafanaInstanceName = getGrafanaInstanceName;
|
|
28
|
+
exports.getTagSelector = getTagSelector;
|
|
29
|
+
exports.isAlertsAvailable = isAlertsAvailable;
|
|
30
|
+
exports.isDashboardsAvailable = isDashboardsAvailable;
|
|
31
|
+
exports.isGrafanaAvailable = isGrafanaAvailable;
|
|
32
|
+
//# sourceMappingURL=annotations.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotations.cjs.js","sources":["../src/annotations.ts"],"sourcesContent":["/*\n * Copyright 2026 Cassidy Marble\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\n\n/**\n * Selects which configured Grafana instance an entity belongs to. The value\n * must match an instance `name` in the backend configuration.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_INSTANCE = 'grafana/instance';\n\n/**\n * Selects the dashboards shown for an entity: a comma-separated list of\n * case-insensitive title substrings, matching any dashboard whose title\n * contains at least one of the values.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_DASHBOARD_SELECTOR =\n 'grafana/dashboard-selector';\n\n/**\n * A comma-separated list of dashboard tags used to select the dashboards shown\n * for an entity.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_TAG_SELECTOR = 'grafana/tag-selector';\n\n/**\n * A comma-separated list of `key=value` label matchers used to select the\n * alerts shown for an entity.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR =\n 'grafana/alert-label-selector';\n\nconst read = (entity: Entity, key: string): string | undefined => {\n const value = entity.metadata.annotations?.[key];\n return value ? value : undefined;\n};\n\n/**\n * Returns the configured Grafana instance name for an entity, if any.\n *\n * @public\n */\nexport const getGrafanaInstanceName = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_INSTANCE);\n\n/**\n * Returns the dashboard selector expression for an entity, if any.\n *\n * @public\n */\nexport const getDashboardSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR);\n\n/**\n * Returns the dashboard tag selector for an entity, if any.\n *\n * @public\n */\nexport const getTagSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_TAG_SELECTOR);\n\n/**\n * Returns the alert label selector for an entity, if any.\n *\n * @public\n */\nexport const getAlertLabelSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR);\n\n/**\n * Returns `true` when an entity carries an annotation that selects Grafana\n * dashboards, and therefore has dashboard content to display.\n *\n * @public\n */\nexport const isDashboardsAvailable = (entity: Entity): boolean =>\n Boolean(\n getGrafanaInstanceName(entity) ||\n getDashboardSelector(entity) ||\n getTagSelector(entity),\n );\n\n/**\n * Returns `true` when an entity carries an annotation that selects Grafana\n * alerts, and therefore has alert content to display.\n *\n * @public\n */\nexport const isAlertsAvailable = (entity: Entity): boolean =>\n Boolean(getGrafanaInstanceName(entity) || getAlertLabelSelector(entity));\n\n/**\n * Returns `true` when an entity carries any Grafana annotation, and therefore\n * has Grafana content to display.\n *\n * @public\n */\nexport const isGrafanaAvailable = (entity: Entity): boolean =>\n isDashboardsAvailable(entity) || isAlertsAvailable(entity);\n"],"names":[],"mappings":";;AAwBO,MAAM,2BAAA,GAA8B;AASpC,MAAM,qCAAA,GACX;AAQK,MAAM,+BAAA,GAAkC;AAQxC,MAAM,uCAAA,GACX;AAEF,MAAM,IAAA,GAAO,CAAC,MAAA,EAAgB,GAAA,KAAoC;AAChE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,GAAG,CAAA;AAC/C,EAAA,OAAO,QAAQ,KAAA,GAAQ,MAAA;AACzB,CAAA;AAOO,MAAM,sBAAA,GAAyB,CAAC,MAAA,KACrC,IAAA,CAAK,QAAQ,2BAA2B;AAOnC,MAAM,oBAAA,GAAuB,CAAC,MAAA,KACnC,IAAA,CAAK,QAAQ,qCAAqC;AAO7C,MAAM,cAAA,GAAiB,CAAC,MAAA,KAC7B,IAAA,CAAK,QAAQ,+BAA+B;AAOvC,MAAM,qBAAA,GAAwB,CAAC,MAAA,KACpC,IAAA,CAAK,QAAQ,uCAAuC;AAQ/C,MAAM,qBAAA,GAAwB,CAAC,MAAA,KACpC,OAAA;AAAA,EACE,uBAAuB,MAAM,CAAA,IAC3B,qBAAqB,MAAM,CAAA,IAC3B,eAAe,MAAM;AACzB;AAQK,MAAM,iBAAA,GAAoB,CAAC,MAAA,KAChC,OAAA,CAAQ,uBAAuB,MAAM,CAAA,IAAK,qBAAA,CAAsB,MAAM,CAAC;AAQlE,MAAM,qBAAqB,CAAC,MAAA,KACjC,sBAAsB,MAAM,CAAA,IAAK,kBAAkB,MAAM;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const GRAFANA_ANNOTATION_INSTANCE = "grafana/instance";
|
|
2
|
+
const GRAFANA_ANNOTATION_DASHBOARD_SELECTOR = "grafana/dashboard-selector";
|
|
3
|
+
const GRAFANA_ANNOTATION_TAG_SELECTOR = "grafana/tag-selector";
|
|
4
|
+
const GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR = "grafana/alert-label-selector";
|
|
5
|
+
const read = (entity, key) => {
|
|
6
|
+
const value = entity.metadata.annotations?.[key];
|
|
7
|
+
return value ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
const getGrafanaInstanceName = (entity) => read(entity, GRAFANA_ANNOTATION_INSTANCE);
|
|
10
|
+
const getDashboardSelector = (entity) => read(entity, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR);
|
|
11
|
+
const getTagSelector = (entity) => read(entity, GRAFANA_ANNOTATION_TAG_SELECTOR);
|
|
12
|
+
const getAlertLabelSelector = (entity) => read(entity, GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR);
|
|
13
|
+
const isDashboardsAvailable = (entity) => Boolean(
|
|
14
|
+
getGrafanaInstanceName(entity) || getDashboardSelector(entity) || getTagSelector(entity)
|
|
15
|
+
);
|
|
16
|
+
const isAlertsAvailable = (entity) => Boolean(getGrafanaInstanceName(entity) || getAlertLabelSelector(entity));
|
|
17
|
+
const isGrafanaAvailable = (entity) => isDashboardsAvailable(entity) || isAlertsAvailable(entity);
|
|
18
|
+
|
|
19
|
+
export { GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR, GRAFANA_ANNOTATION_INSTANCE, GRAFANA_ANNOTATION_TAG_SELECTOR, getAlertLabelSelector, getDashboardSelector, getGrafanaInstanceName, getTagSelector, isAlertsAvailable, isDashboardsAvailable, isGrafanaAvailable };
|
|
20
|
+
//# sourceMappingURL=annotations.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotations.esm.js","sources":["../src/annotations.ts"],"sourcesContent":["/*\n * Copyright 2026 Cassidy Marble\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\n\n/**\n * Selects which configured Grafana instance an entity belongs to. The value\n * must match an instance `name` in the backend configuration.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_INSTANCE = 'grafana/instance';\n\n/**\n * Selects the dashboards shown for an entity: a comma-separated list of\n * case-insensitive title substrings, matching any dashboard whose title\n * contains at least one of the values.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_DASHBOARD_SELECTOR =\n 'grafana/dashboard-selector';\n\n/**\n * A comma-separated list of dashboard tags used to select the dashboards shown\n * for an entity.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_TAG_SELECTOR = 'grafana/tag-selector';\n\n/**\n * A comma-separated list of `key=value` label matchers used to select the\n * alerts shown for an entity.\n *\n * @public\n */\nexport const GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR =\n 'grafana/alert-label-selector';\n\nconst read = (entity: Entity, key: string): string | undefined => {\n const value = entity.metadata.annotations?.[key];\n return value ? value : undefined;\n};\n\n/**\n * Returns the configured Grafana instance name for an entity, if any.\n *\n * @public\n */\nexport const getGrafanaInstanceName = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_INSTANCE);\n\n/**\n * Returns the dashboard selector expression for an entity, if any.\n *\n * @public\n */\nexport const getDashboardSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR);\n\n/**\n * Returns the dashboard tag selector for an entity, if any.\n *\n * @public\n */\nexport const getTagSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_TAG_SELECTOR);\n\n/**\n * Returns the alert label selector for an entity, if any.\n *\n * @public\n */\nexport const getAlertLabelSelector = (entity: Entity): string | undefined =>\n read(entity, GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR);\n\n/**\n * Returns `true` when an entity carries an annotation that selects Grafana\n * dashboards, and therefore has dashboard content to display.\n *\n * @public\n */\nexport const isDashboardsAvailable = (entity: Entity): boolean =>\n Boolean(\n getGrafanaInstanceName(entity) ||\n getDashboardSelector(entity) ||\n getTagSelector(entity),\n );\n\n/**\n * Returns `true` when an entity carries an annotation that selects Grafana\n * alerts, and therefore has alert content to display.\n *\n * @public\n */\nexport const isAlertsAvailable = (entity: Entity): boolean =>\n Boolean(getGrafanaInstanceName(entity) || getAlertLabelSelector(entity));\n\n/**\n * Returns `true` when an entity carries any Grafana annotation, and therefore\n * has Grafana content to display.\n *\n * @public\n */\nexport const isGrafanaAvailable = (entity: Entity): boolean =>\n isDashboardsAvailable(entity) || isAlertsAvailable(entity);\n"],"names":[],"mappings":"AAwBO,MAAM,2BAAA,GAA8B;AASpC,MAAM,qCAAA,GACX;AAQK,MAAM,+BAAA,GAAkC;AAQxC,MAAM,uCAAA,GACX;AAEF,MAAM,IAAA,GAAO,CAAC,MAAA,EAAgB,GAAA,KAAoC;AAChE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,GAAG,CAAA;AAC/C,EAAA,OAAO,QAAQ,KAAA,GAAQ,MAAA;AACzB,CAAA;AAOO,MAAM,sBAAA,GAAyB,CAAC,MAAA,KACrC,IAAA,CAAK,QAAQ,2BAA2B;AAOnC,MAAM,oBAAA,GAAuB,CAAC,MAAA,KACnC,IAAA,CAAK,QAAQ,qCAAqC;AAO7C,MAAM,cAAA,GAAiB,CAAC,MAAA,KAC7B,IAAA,CAAK,QAAQ,+BAA+B;AAOvC,MAAM,qBAAA,GAAwB,CAAC,MAAA,KACpC,IAAA,CAAK,QAAQ,uCAAuC;AAQ/C,MAAM,qBAAA,GAAwB,CAAC,MAAA,KACpC,OAAA;AAAA,EACE,uBAAuB,MAAM,CAAA,IAC3B,qBAAqB,MAAM,CAAA,IAC3B,eAAe,MAAM;AACzB;AAQK,MAAM,iBAAA,GAAoB,CAAC,MAAA,KAChC,OAAA,CAAQ,uBAAuB,MAAM,CAAA,IAAK,qBAAA,CAAsB,MAAM,CAAC;AAQlE,MAAM,qBAAqB,CAAC,MAAA,KACjC,sBAAsB,MAAM,CAAA,IAAK,kBAAkB,MAAM;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var annotations = require('./annotations.cjs.js');
|
|
4
|
+
var selectors = require('./selectors.cjs.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
exports.GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR = annotations.GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR;
|
|
9
|
+
exports.GRAFANA_ANNOTATION_DASHBOARD_SELECTOR = annotations.GRAFANA_ANNOTATION_DASHBOARD_SELECTOR;
|
|
10
|
+
exports.GRAFANA_ANNOTATION_INSTANCE = annotations.GRAFANA_ANNOTATION_INSTANCE;
|
|
11
|
+
exports.GRAFANA_ANNOTATION_TAG_SELECTOR = annotations.GRAFANA_ANNOTATION_TAG_SELECTOR;
|
|
12
|
+
exports.getAlertLabelSelector = annotations.getAlertLabelSelector;
|
|
13
|
+
exports.getDashboardSelector = annotations.getDashboardSelector;
|
|
14
|
+
exports.getGrafanaInstanceName = annotations.getGrafanaInstanceName;
|
|
15
|
+
exports.getTagSelector = annotations.getTagSelector;
|
|
16
|
+
exports.isAlertsAvailable = annotations.isAlertsAvailable;
|
|
17
|
+
exports.isDashboardsAvailable = annotations.isDashboardsAvailable;
|
|
18
|
+
exports.isGrafanaAvailable = annotations.isGrafanaAvailable;
|
|
19
|
+
exports.parseLabelSelector = selectors.parseLabelSelector;
|
|
20
|
+
exports.parseTagSelector = selectors.parseTagSelector;
|
|
21
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Entity } from '@backstage/catalog-model';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Selects which configured Grafana instance an entity belongs to. The value
|
|
5
|
+
* must match an instance `name` in the backend configuration.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
declare const GRAFANA_ANNOTATION_INSTANCE = "grafana/instance";
|
|
10
|
+
/**
|
|
11
|
+
* Selects the dashboards shown for an entity: a comma-separated list of
|
|
12
|
+
* case-insensitive title substrings, matching any dashboard whose title
|
|
13
|
+
* contains at least one of the values.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
declare const GRAFANA_ANNOTATION_DASHBOARD_SELECTOR = "grafana/dashboard-selector";
|
|
18
|
+
/**
|
|
19
|
+
* A comma-separated list of dashboard tags used to select the dashboards shown
|
|
20
|
+
* for an entity.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
declare const GRAFANA_ANNOTATION_TAG_SELECTOR = "grafana/tag-selector";
|
|
25
|
+
/**
|
|
26
|
+
* A comma-separated list of `key=value` label matchers used to select the
|
|
27
|
+
* alerts shown for an entity.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
declare const GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR = "grafana/alert-label-selector";
|
|
32
|
+
/**
|
|
33
|
+
* Returns the configured Grafana instance name for an entity, if any.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
declare const getGrafanaInstanceName: (entity: Entity) => string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Returns the dashboard selector expression for an entity, if any.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
declare const getDashboardSelector: (entity: Entity) => string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the dashboard tag selector for an entity, if any.
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
declare const getTagSelector: (entity: Entity) => string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Returns the alert label selector for an entity, if any.
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
declare const getAlertLabelSelector: (entity: Entity) => string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Returns `true` when an entity carries an annotation that selects Grafana
|
|
58
|
+
* dashboards, and therefore has dashboard content to display.
|
|
59
|
+
*
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
declare const isDashboardsAvailable: (entity: Entity) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Returns `true` when an entity carries an annotation that selects Grafana
|
|
65
|
+
* alerts, and therefore has alert content to display.
|
|
66
|
+
*
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
declare const isAlertsAvailable: (entity: Entity) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Returns `true` when an entity carries any Grafana annotation, and therefore
|
|
72
|
+
* has Grafana content to display.
|
|
73
|
+
*
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
declare const isGrafanaAvailable: (entity: Entity) => boolean;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parses a `key=value,key2=value2` label selector string (as used by the
|
|
80
|
+
* `grafana/alert-label-selector` annotation) into a record. Whitespace around
|
|
81
|
+
* keys and values is trimmed, and empty or malformed segments are ignored.
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
declare function parseLabelSelector(selector: string | undefined): Record<string, string>;
|
|
86
|
+
/**
|
|
87
|
+
* Parses a comma-separated tag list (as used by the `grafana/tag-selector`
|
|
88
|
+
* annotation) into an array of trimmed, non-empty tags.
|
|
89
|
+
*
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
declare function parseTagSelector(selector: string | undefined): string[];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A configured Grafana instance, as exposed to clients of the backend.
|
|
96
|
+
*
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
type GrafanaInstanceInfo = {
|
|
100
|
+
/** The unique, stable instance name (matches the `grafana/instance` annotation). */
|
|
101
|
+
name: string;
|
|
102
|
+
/** A human-readable title for display. */
|
|
103
|
+
title: string;
|
|
104
|
+
/** The base URL of the Grafana instance, without a trailing slash. */
|
|
105
|
+
url: string;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* A Grafana dashboard, normalized across the App Platform and legacy search
|
|
109
|
+
* APIs.
|
|
110
|
+
*
|
|
111
|
+
* @public
|
|
112
|
+
*/
|
|
113
|
+
type GrafanaDashboard = {
|
|
114
|
+
/** The dashboard uid, as used in the dashboard URL. */
|
|
115
|
+
uid: string;
|
|
116
|
+
/** The dashboard title. */
|
|
117
|
+
title: string;
|
|
118
|
+
/** A fully-qualified URL to the dashboard in Grafana. */
|
|
119
|
+
url: string;
|
|
120
|
+
/** The title of the folder containing the dashboard, if any. */
|
|
121
|
+
folderTitle?: string;
|
|
122
|
+
/** A fully-qualified URL to the folder containing the dashboard, if any. */
|
|
123
|
+
folderUrl?: string;
|
|
124
|
+
/** The dashboard tags. */
|
|
125
|
+
tags: string[];
|
|
126
|
+
/** The name of the instance this dashboard was read from. */
|
|
127
|
+
instanceName: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* The evaluation state of a Grafana alert rule.
|
|
131
|
+
*
|
|
132
|
+
* @public
|
|
133
|
+
*/
|
|
134
|
+
type GrafanaAlertState = 'firing' | 'pending' | 'inactive' | 'normal' | 'no_data' | 'error' | 'unknown';
|
|
135
|
+
/**
|
|
136
|
+
* A Grafana alert rule together with its current state.
|
|
137
|
+
*
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
140
|
+
type GrafanaAlert = {
|
|
141
|
+
/** The alert rule name. */
|
|
142
|
+
name: string;
|
|
143
|
+
/** The current evaluation state of the rule. */
|
|
144
|
+
state: GrafanaAlertState;
|
|
145
|
+
/** A fully-qualified URL to the alert (or its dashboard/panel) in Grafana. */
|
|
146
|
+
url: string;
|
|
147
|
+
/** The labels attached to the rule. */
|
|
148
|
+
labels: Record<string, string>;
|
|
149
|
+
/** The title of the folder (namespace) containing the rule, if any. */
|
|
150
|
+
folderTitle?: string;
|
|
151
|
+
/** The name of the instance this alert was read from. */
|
|
152
|
+
instanceName: string;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Response body for `GET /instances`.
|
|
156
|
+
*
|
|
157
|
+
* @public
|
|
158
|
+
*/
|
|
159
|
+
type ListInstancesResponse = {
|
|
160
|
+
items: GrafanaInstanceInfo[];
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Response body for the dashboard listing endpoints.
|
|
164
|
+
*
|
|
165
|
+
* @public
|
|
166
|
+
*/
|
|
167
|
+
type ListDashboardsResponse = {
|
|
168
|
+
items: GrafanaDashboard[];
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Response body for the alert listing endpoints.
|
|
172
|
+
*
|
|
173
|
+
* @public
|
|
174
|
+
*/
|
|
175
|
+
type ListAlertsResponse = {
|
|
176
|
+
items: GrafanaAlert[];
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export { GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR, GRAFANA_ANNOTATION_INSTANCE, GRAFANA_ANNOTATION_TAG_SELECTOR, getAlertLabelSelector, getDashboardSelector, getGrafanaInstanceName, getTagSelector, isAlertsAvailable, isDashboardsAvailable, isGrafanaAvailable, parseLabelSelector, parseTagSelector };
|
|
180
|
+
export type { GrafanaAlert, GrafanaAlertState, GrafanaDashboard, GrafanaInstanceInfo, ListAlertsResponse, ListDashboardsResponse, ListInstancesResponse };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { GRAFANA_ANNOTATION_ALERT_LABEL_SELECTOR, GRAFANA_ANNOTATION_DASHBOARD_SELECTOR, GRAFANA_ANNOTATION_INSTANCE, GRAFANA_ANNOTATION_TAG_SELECTOR, getAlertLabelSelector, getDashboardSelector, getGrafanaInstanceName, getTagSelector, isAlertsAvailable, isDashboardsAvailable, isGrafanaAvailable } from './annotations.esm.js';
|
|
2
|
+
export { parseLabelSelector, parseTagSelector } from './selectors.esm.js';
|
|
3
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function parseLabelSelector(selector) {
|
|
4
|
+
if (!selector) {
|
|
5
|
+
return {};
|
|
6
|
+
}
|
|
7
|
+
const result = {};
|
|
8
|
+
for (const segment of selector.split(",")) {
|
|
9
|
+
const index = segment.indexOf("=");
|
|
10
|
+
if (index <= 0) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const key = segment.slice(0, index).trim();
|
|
14
|
+
const value = segment.slice(index + 1).trim();
|
|
15
|
+
if (key) {
|
|
16
|
+
result[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function parseTagSelector(selector) {
|
|
22
|
+
if (!selector) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
return selector.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.parseLabelSelector = parseLabelSelector;
|
|
29
|
+
exports.parseTagSelector = parseTagSelector;
|
|
30
|
+
//# sourceMappingURL=selectors.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.cjs.js","sources":["../src/selectors.ts"],"sourcesContent":["/*\n * Copyright 2026 Cassidy Marble\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parses a `key=value,key2=value2` label selector string (as used by the\n * `grafana/alert-label-selector` annotation) into a record. Whitespace around\n * keys and values is trimmed, and empty or malformed segments are ignored.\n *\n * @public\n */\nexport function parseLabelSelector(\n selector: string | undefined,\n): Record<string, string> {\n if (!selector) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const segment of selector.split(',')) {\n const index = segment.indexOf('=');\n if (index <= 0) {\n continue;\n }\n const key = segment.slice(0, index).trim();\n const value = segment.slice(index + 1).trim();\n if (key) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Parses a comma-separated tag list (as used by the `grafana/tag-selector`\n * annotation) into an array of trimmed, non-empty tags.\n *\n * @public\n */\nexport function parseTagSelector(selector: string | undefined): string[] {\n if (!selector) {\n return [];\n }\n return selector\n .split(',')\n .map(tag => tag.trim())\n .filter(Boolean);\n}\n"],"names":[],"mappings":";;AAuBO,SAAS,mBACd,QAAA,EACwB;AACxB,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,KAAA,MAAW,OAAA,IAAW,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,EAAG;AACzC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACjC,IAAA,IAAI,SAAS,CAAA,EAAG;AACd,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,KAAK,EAAE,IAAA,EAAK;AACzC,IAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,GAAQ,CAAC,EAAE,IAAA,EAAK;AAC5C,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,iBAAiB,QAAA,EAAwC;AACvE,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,OAAO,QAAA,CACJ,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAA,EAAM,CAAA,CACrB,MAAA,CAAO,OAAO,CAAA;AACnB;;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function parseLabelSelector(selector) {
|
|
2
|
+
if (!selector) {
|
|
3
|
+
return {};
|
|
4
|
+
}
|
|
5
|
+
const result = {};
|
|
6
|
+
for (const segment of selector.split(",")) {
|
|
7
|
+
const index = segment.indexOf("=");
|
|
8
|
+
if (index <= 0) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const key = segment.slice(0, index).trim();
|
|
12
|
+
const value = segment.slice(index + 1).trim();
|
|
13
|
+
if (key) {
|
|
14
|
+
result[key] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
function parseTagSelector(selector) {
|
|
20
|
+
if (!selector) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return selector.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { parseLabelSelector, parseTagSelector };
|
|
27
|
+
//# sourceMappingURL=selectors.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.esm.js","sources":["../src/selectors.ts"],"sourcesContent":["/*\n * Copyright 2026 Cassidy Marble\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parses a `key=value,key2=value2` label selector string (as used by the\n * `grafana/alert-label-selector` annotation) into a record. Whitespace around\n * keys and values is trimmed, and empty or malformed segments are ignored.\n *\n * @public\n */\nexport function parseLabelSelector(\n selector: string | undefined,\n): Record<string, string> {\n if (!selector) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const segment of selector.split(',')) {\n const index = segment.indexOf('=');\n if (index <= 0) {\n continue;\n }\n const key = segment.slice(0, index).trim();\n const value = segment.slice(index + 1).trim();\n if (key) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Parses a comma-separated tag list (as used by the `grafana/tag-selector`\n * annotation) into an array of trimmed, non-empty tags.\n *\n * @public\n */\nexport function parseTagSelector(selector: string | undefined): string[] {\n if (!selector) {\n return [];\n }\n return selector\n .split(',')\n .map(tag => tag.trim())\n .filter(Boolean);\n}\n"],"names":[],"mappings":"AAuBO,SAAS,mBACd,QAAA,EACwB;AACxB,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,KAAA,MAAW,OAAA,IAAW,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,EAAG;AACzC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AACjC,IAAA,IAAI,SAAS,CAAA,EAAG;AACd,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAM,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,KAAK,EAAE,IAAA,EAAK;AACzC,IAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,GAAQ,CAAC,EAAE,IAAA,EAAK;AAC5C,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,iBAAiB,QAAA,EAAwC;AACvE,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,OAAO,QAAA,CACJ,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAA,EAAM,CAAA,CACrB,MAAA,CAAO,OAAO,CAAA;AACnB;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marble-sh/backstage-plugin-grafana-common",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Common functionality for the Grafana Backstage plugins (types, annotations, and API contracts)",
|
|
5
|
+
"main": "./dist/index.cjs.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"backstage": {
|
|
12
|
+
"role": "common-library",
|
|
13
|
+
"pluginId": "grafana",
|
|
14
|
+
"pluginPackages": [
|
|
15
|
+
"@marble-sh/backstage-plugin-grafana",
|
|
16
|
+
"@marble-sh/backstage-plugin-grafana-backend",
|
|
17
|
+
"@marble-sh/backstage-plugin-grafana-common",
|
|
18
|
+
"@marble-sh/backstage-plugin-grafana-node"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "backstage-cli package build",
|
|
24
|
+
"clean": "backstage-cli package clean",
|
|
25
|
+
"lint": "backstage-cli package lint",
|
|
26
|
+
"prepack": "backstage-cli package prepack",
|
|
27
|
+
"postpack": "backstage-cli package postpack",
|
|
28
|
+
"test": "backstage-cli package test"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@backstage/catalog-model": "backstage:^"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@backstage/cli": "backstage:^"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/marble-sh/backstage-plugins-grafana",
|
|
42
|
+
"directory": "plugins/grafana-common"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"backstage",
|
|
46
|
+
"plugin",
|
|
47
|
+
"grafana"
|
|
48
|
+
],
|
|
49
|
+
"author": "Cassidy Marble",
|
|
50
|
+
"homepage": "https://github.com/marble-sh/backstage-plugins-grafana/tree/main/plugins/grafana-common",
|
|
51
|
+
"bugs": "https://github.com/marble-sh/backstage-plugins-grafana/issues",
|
|
52
|
+
"exports": {
|
|
53
|
+
".": {
|
|
54
|
+
"import": "./dist/index.esm.js",
|
|
55
|
+
"require": "./dist/index.cjs.js",
|
|
56
|
+
"types": "./dist/index.d.ts",
|
|
57
|
+
"default": "./dist/index.cjs.js"
|
|
58
|
+
},
|
|
59
|
+
"./package.json": "./package.json"
|
|
60
|
+
},
|
|
61
|
+
"typesVersions": {
|
|
62
|
+
"*": {
|
|
63
|
+
"package.json": [
|
|
64
|
+
"package.json"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"module": "./dist/index.esm.js"
|
|
69
|
+
}
|