diff --git a/.betterer.results b/.betterer.results index a5ae922b47a..46ecf2ed512 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4144,9 +4144,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"] ], - "public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxCheatSheet.tsx:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"] - ], "public/app/plugins/datasource/influxdb/datasource.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], diff --git a/.drone.yml b/.drone.yml index 757e9bf62f0..35c6be52526 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4550,7 +4550,7 @@ steps: image: alpine/git name: clone-rgm - commands: - - apk update && apk add docker bash + - apk update && apk add docker - export GRAFANA_DIR=$$(pwd) - cd rgm && ./scripts/drone_publish_main.sh environment: @@ -4604,7 +4604,7 @@ steps: image: alpine/git name: clone-rgm - commands: - - apk update && apk add docker bash + - apk update && apk add docker - export GRAFANA_DIR=$$(pwd) - cd rgm && ./scripts/drone_publish_tag.sh environment: @@ -6925,7 +6925,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana:latest image: aquasec/trivy:0.21.0 - name: scan-unkown-low-medium-vulnerabilities + name: scan-unknown-low-medium-vulnerabilities - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana:latest image: aquasec/trivy:0.21.0 @@ -6955,7 +6955,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana:main image: aquasec/trivy:0.21.0 - name: scan-unkown-low-medium-vulnerabilities + name: scan-unknown-low-medium-vulnerabilities - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana:main image: aquasec/trivy:0.21.0 @@ -6985,7 +6985,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana:latest-ubuntu image: aquasec/trivy:0.21.0 - name: scan-unkown-low-medium-vulnerabilities + name: scan-unknown-low-medium-vulnerabilities - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana:latest-ubuntu image: aquasec/trivy:0.21.0 @@ -7016,7 +7016,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana:main-ubuntu image: aquasec/trivy:0.21.0 - name: scan-unkown-low-medium-vulnerabilities + name: scan-unknown-low-medium-vulnerabilities - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana:main-ubuntu image: aquasec/trivy:0.21.0 @@ -7039,6 +7039,80 @@ type: docker clone: retries: 3 kind: pipeline +name: scan-build-test-and-publish-docker-images +platform: + arch: amd64 + os: linux +steps: +- commands: + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/build-container:1.7.4 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.17.1 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mcr.microsoft.com/windows:1809 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/ci-wix:0.1.1 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.20.4 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-windows-test:0.1.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM postgres:12.3-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:5.7.39 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:8.0.32 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM redis:6.2.11-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM memcached:1.6.9-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:windowsservercore-1809 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us.gcr.io/kubernetes-dev/package-publish:latest + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM osixia/openldap:1.4.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/drone-downstream + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docker-puppeteer:1.1.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docs-base:latest + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM cypress/included:9.5.1-node16.14.0-slim-chrome99-ff97 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e:latest + image: aquasec/trivy:0.21.0 + name: scan-unknown-low-medium-vulnerabilities +- commands: + - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/build-container:1.7.4 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3 + - trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.17.1 + - trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8 + - trivy --exit-code 1 --severity HIGH,CRITICAL mcr.microsoft.com/windows:1809 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/ci-wix:0.1.1 + - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.20.4 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-windows-test:0.1.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack + - trivy --exit-code 1 --severity HIGH,CRITICAL postgres:12.3-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL mysql:5.7.39 + - trivy --exit-code 1 --severity HIGH,CRITICAL mysql:8.0.32 + - trivy --exit-code 1 --severity HIGH,CRITICAL redis:6.2.11-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL memcached:1.6.9-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL docker:windowsservercore-1809 + - trivy --exit-code 1 --severity HIGH,CRITICAL us.gcr.io/kubernetes-dev/package-publish:latest + - trivy --exit-code 1 --severity HIGH,CRITICAL osixia/openldap:1.4.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/drone-downstream + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docker-puppeteer:1.1.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docs-base:latest + - trivy --exit-code 1 --severity HIGH,CRITICAL cypress/included:9.5.1-node16.14.0-slim-chrome99-ff97 + - trivy --exit-code 1 --severity HIGH,CRITICAL us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e:latest + image: aquasec/trivy:0.21.0 + name: scan-high-critical-vulnerabilities +- image: plugins/slack + name: slack-notify-failure + settings: + channel: grafana-backend-ops + template: 'Nightly docker image scan job for build-images failed: {{build.link}}' + webhook: + from_secret: slack_webhook_backend + when: + status: failure +trigger: + cron: nightly + event: cron +type: docker +--- +clone: + retries: 3 +kind: pipeline name: grafana-com-nightly platform: arch: amd64 @@ -7242,6 +7316,6 @@ kind: secret name: delivery-bot-app-private-key --- kind: signature -hmac: 124ed302e0b2a80dd5e98057f9b03750af9bae084b3ef4f574e6f2888a1a741a +hmac: 44f95e0e3d9eb3fc8891e94f7205c18e85adacab25b69906d3c5212875baa383 ... diff --git a/CHANGELOG.md b/CHANGELOG.md index 379d4f1e7cd..b7a7c41f37d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ ### Features and enhancements +- **Alerting:** Migrate unknown NoData\Error settings to the default. [#69010](https://github.com/grafana/grafana/issues/69010), [@grafanabot](https://github.com/grafanabot) +- **Drawer:** Position under nav & minor redesign . [#68396](https://github.com/grafana/grafana/issues/68396), [@grafanabot](https://github.com/grafanabot) +- **Navigation:** Add keyboard shortcut to navigate directly to Dashboards. [#68374](https://github.com/grafana/grafana/issues/68374), [@grafanabot](https://github.com/grafanabot) +- **Explore:** Promote exploreMixedDatasource to Stable & enable by default. [#68353](https://github.com/grafana/grafana/issues/68353), [@Elfo404](https://github.com/Elfo404) +- **Tempo:** Escape regex-sensitive characters in span name before building promql query. [#68313](https://github.com/grafana/grafana/issues/68313), [@grafanabot](https://github.com/grafanabot) +- **Drawer:** Introduce a size property that set's width percentage and minWidth . [#68128](https://github.com/grafana/grafana/issues/68128), [@grafanabot](https://github.com/grafanabot) +- **AngularDeprecation:** Show warnings in panel edit for angular panels. [#68083](https://github.com/grafana/grafana/issues/68083), [@grafanabot](https://github.com/grafanabot) +- **Dashboard:** Change add panel button to fill to remove outline border. [#68017](https://github.com/grafana/grafana/issues/68017), [@grafanabot](https://github.com/grafanabot) - **Query History:** Remove migration. [#67470](https://github.com/grafana/grafana/issues/67470), [@Elfo404](https://github.com/Elfo404) - **Alerting:** Implement template testing endpoint. [#67450](https://github.com/grafana/grafana/issues/67450), [@JacobsonMT](https://github.com/JacobsonMT) - **Trace View:** Export trace button . [#67368](https://github.com/grafana/grafana/issues/67368), [@adrapereira](https://github.com/adrapereira) @@ -100,6 +108,37 @@ ### Bug fixes +- **ResourcePicker:** Fix missing border bug on cancel button. [#69113](https://github.com/grafana/grafana/issues/69113), [@nmarrs](https://github.com/nmarrs) +- **TimeSeries:** Fix centeredZero y axis ranging when all values are 0. [#69112](https://github.com/grafana/grafana/issues/69112), [@grafanabot](https://github.com/grafanabot) +- **StatusHistory:** Fix rendering of value-mapped null. [#69108](https://github.com/grafana/grafana/issues/69108), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Fix provenance guard checks for Alertmanager configuration to not cause panic when compared nested objects. [#69094](https://github.com/grafana/grafana/issues/69094), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Add support for Alert State History Loki primary. [#69077](https://github.com/grafana/grafana/issues/69077), [@grafanabot](https://github.com/grafanabot) +- **Dashboards:** Fix undefined aria labels in Annotation Checkboxes for Programmatic Access. [#68873](https://github.com/grafana/grafana/issues/68873), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Fix stale query preview error. [#68836](https://github.com/grafana/grafana/issues/68836), [@grafanabot](https://github.com/grafanabot) +- **AnonymousAuth:** Fix concurrent read-write crash. [#68803](https://github.com/grafana/grafana/issues/68803), [@grafanabot](https://github.com/grafanabot) +- **AzureMonitor:** Ensure legacy properties containing template variables are correctly migrated. [#68792](https://github.com/grafana/grafana/issues/68792), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Add additional contact points for external AM. [#68778](https://github.com/grafana/grafana/issues/68778), [@grafanabot](https://github.com/grafanabot) +- **RBAC:** Remove legacy AC editor and admin role on new dashboard route. [#68777](https://github.com/grafana/grafana/issues/68777), [@grafanabot](https://github.com/grafanabot) +- **Azure Monitor:** Fix bug with top value so more than 10 resources can be shown . [#68725](https://github.com/grafana/grafana/issues/68725), [@grafanabot](https://github.com/grafanabot) +- **NodeGraph:** Fix overlaps preventing opening an edge context menu when nodes were too close. [#68628](https://github.com/grafana/grafana/issues/68628), [@grafanabot](https://github.com/grafanabot) +- **Plugins:** Correct the usage of mutex for gRPC plugin implementation. [#68609](https://github.com/grafana/grafana/issues/68609), [@grafanabot](https://github.com/grafanabot) +- **Azure Monitor:** Fix bug that did not show alert rule preview. [#68581](https://github.com/grafana/grafana/issues/68581), [@grafanabot](https://github.com/grafanabot) +- **FlameGraph:** Fix table sort being reset when search changes. [#68454](https://github.com/grafana/grafana/issues/68454), [@grafanabot](https://github.com/grafanabot) +- **Command Palette:** Prevent stale search results from overwriting newer results. [#68392](https://github.com/grafana/grafana/issues/68392), [@grafanabot](https://github.com/grafanabot) +- **Search:** Fix Search returning results out of order. [#68387](https://github.com/grafana/grafana/issues/68387), [@joshhunt](https://github.com/joshhunt) +- **Explore:** Remove data source onboarding page. [#68381](https://github.com/grafana/grafana/issues/68381), [@grafanabot](https://github.com/grafanabot) +- **Flamegraph:** Fix tooltip positioning. [#68312](https://github.com/grafana/grafana/issues/68312), [@grafanabot](https://github.com/grafanabot) +- **Pyroscope:** Add authentication when calling backendType resource API. [#68311](https://github.com/grafana/grafana/issues/68311), [@grafanabot](https://github.com/grafanabot) +- **Histogram:** Respect min/max panel settings for x-axis. [#68245](https://github.com/grafana/grafana/issues/68245), [@grafanabot](https://github.com/grafanabot) +- **QueryRow:** Make toggle actions screen-readers accessible. [#68210](https://github.com/grafana/grafana/issues/68210), [@grafanabot](https://github.com/grafanabot) +- **Heatmap:** Fix color rendering for value ranges < 1. [#68164](https://github.com/grafana/grafana/issues/68164), [@grafanabot](https://github.com/grafanabot) +- **Heatmap:** Handle unsorted timestamps in calculate mode. [#68151](https://github.com/grafana/grafana/issues/68151), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Fixes Alert list panel "ungrouped" regression. [#68090](https://github.com/grafana/grafana/issues/68090), [@grafanabot](https://github.com/grafanabot) +- **Alerting:** Show export button for org admins. [#67995](https://github.com/grafana/grafana/issues/67995), [@grafanabot](https://github.com/grafanabot) +- **Navigation:** Fix 'Page not found' when sending or going back from 'Invitate user' page. [#67972](https://github.com/grafana/grafana/issues/67972), [@grafanabot](https://github.com/grafanabot) +- **InspectDrawer:** Fixes issue with double scrollbars. [#67888](https://github.com/grafana/grafana/issues/67888), [@grafanabot](https://github.com/grafanabot) +- **Connections:** Show core datasource plugins as well. [#67886](https://github.com/grafana/grafana/issues/67886), [@grafanabot](https://github.com/grafanabot) +- **Gauge:** Set min and max for percent unit. [#67719](https://github.com/grafana/grafana/issues/67719), [@grafanabot](https://github.com/grafanabot) - **TimeSeries:** Fix leading null-fill for missing intervals. [#67570](https://github.com/grafana/grafana/issues/67570), [@leeoniya](https://github.com/leeoniya) - **Pyroscope:** Fix autodetection in case of using Phlare backend. [#67536](https://github.com/grafana/grafana/issues/67536), [@aocenas](https://github.com/aocenas) - **Dashboard:** Revert fixed header shown on mobile devices in the new panel header. [#67510](https://github.com/grafana/grafana/issues/67510), [@axelavargas](https://github.com/axelavargas) @@ -162,8 +201,7 @@ ### Breaking changes -The deprecated `plugin:build` command in the Grafana Toolkit have been removed in this release. The replacement [`create-plugin`](https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin/) tool is recommended for plugin development. -Issue [#67485](https://github.com/grafana/grafana/issues/67485) +The deprecated `plugin:build` command in the Grafana Toolkit have been removed in this release. The replacement [`create-plugin`](https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin/) tool is recommended for plugin development. Issue [#67485](https://github.com/grafana/grafana/issues/67485) The deprecated `package:build`, `node-version-check` and `toolkit:build` commands in the Grafana Toolkit have been removed in this release. Issue [#67475](https://github.com/grafana/grafana/issues/67475) @@ -181,13 +219,15 @@ The deprecated `plugin:test` and `plugin:dev` commands in the Grafana Toolkit ha The type signature of the `testDatasource()` method on the `DataSourceWithBackend` class [has changed](https://github.com/grafana/grafana/pull/67014/files/a5608dc4f27ab4459e725b22ff60b8fc05390c08#diff-c58fc1a09e9b9b17e5f45efbfb646273e69145f7687facb134440da4edafc745R263), the returned Promise is now typed stricter, which is probably going to cause type-errors while building plugins against the latest Grafana versions. -````typescript +```typescript // Before abstract testDatasource(): Promise; // After abstract testDatasource(): Promise; -``` Issue [#67014](https://github.com/grafana/grafana/issues/67014) +``` + +Issue [#67014](https://github.com/grafana/grafana/issues/67014) Grafana requires an Elasticsearch version of 7.16 or newer. If you use an older Elasticsearch version, you will get warnings in the query editor and on the datasource configuration page. Issue [#66928](https://github.com/grafana/grafana/issues/66928) @@ -199,20 +239,19 @@ We've removed some now unused properties from the `NavModel` interface. Issue [# We removed previously deprecated components from `@grafana/data` : `getLogLevel`, `getLogLevelFromKey`, `addLogLevelToSeries`, `LogsParser`, `LogsParsers`, `calculateFieldStats`, `calculateLogsLabelStats`, `calculateStats`, `getParser`, `sortInAscendingOrder`, `sortInDescendingOrder`, `sortLogsResult`, `sortLogRows`, `checkLogsError`, `escapeUnescapedString`. Issue [#66271](https://github.com/grafana/grafana/issues/66271) -We removed previously deprecated components from `@grafana/ui` : `LogLabels`, `LogMessageAnsi`, `LogRows`, `getLogRowStyles`. - Issue [#66268](https://github.com/grafana/grafana/issues/66268) +We removed previously deprecated components from `@grafana/ui` : `LogLabels`, `LogMessageAnsi`, `LogRows`, `getLogRowStyles`. Issue [#66268](https://github.com/grafana/grafana/issues/66268) -We removed previously deprecated `DataSourceWithLogsVolumeSupport` that was replaced with `DataSourceWithSupplementaryQueriesSupport`. Both APIs are for internal use only. Issue [#66266](https://github.com/grafana/grafana/issues/66266) +We removed previously deprecated `DataSourceWithLogsVolumeSupport` that was replaced with `DataSourceWithSupplementaryQueriesSupport`. Both APIs are for internal use only. Issue [#66266](https://github.com/grafana/grafana/issues/66266) -Additional functions (map/filter/forEach/iterator) have been added to the root Vector interface. Any code using vectors will continue to work unchanged, but in the rare case that you have implemented Vector directly, it be missing these functions. The easiest fix is to extend [FunctionalVector](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/vector/FunctionalVector.ts). +Additional functions (map/filter/forEach/iterator) have been added to the root Vector interface. Any code using vectors will continue to work unchanged, but in the rare case that you have implemented Vector directly, it be missing these functions. The easiest fix is to extend [FunctionalVector](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/vector/FunctionalVector.ts). The `ArrayVector` class now extends the native JavaScript `Array` and gains all of its prototype/instance methods as a result. Issue [#66187](https://github.com/grafana/grafana/issues/66187) We've removed the ability for functions to be passed as children to the `Dropdown` component. Previously, this was used to access the `isOpen` state of the dropdown. This can be now be achieved with the `onVisibleChange` prop. Before: -```` +``` return ( {(isOpen) => @@ -220,12 +259,11 @@ return ( } ); - ``` After: -``` +``` const [isOpen, setIsOpen] = useState(false); ... @@ -235,13 +273,11 @@ return ( ); +``` -````Issue [#65467](https://github.com/grafana/grafana/issues/65467) +Issue [#65467](https://github.com/grafana/grafana/issues/65467) -(relevant for plugin developers) The deprecated internal `dashboardId` is now removed from the request context. For usage tracking use the `dashboardUid` - - - Issue [#64786](https://github.com/grafana/grafana/issues/64786) +(relevant for plugin developers) The deprecated internal `dashboardId` is now removed from the request context. For usage tracking use the `dashboardUid` Issue [#64786](https://github.com/grafana/grafana/issues/64786) Grafana has been upgraded to React 18 and now leverages the new React client rendering API. Plugin authors in particular should be aware, as there could be unintended side effects due to the changes around automatic batching of state updates and consistent `useEffect` timings. Be sure to test your plugins and reference the React 18 upgrade docs here: https://react.dev/blog/2022/03/08/react-18-upgrade-guide Issue [#64428](https://github.com/grafana/grafana/issues/64428) @@ -2650,7 +2686,7 @@ await selectOptionInTest(selectEl, 'Option 2'); import { select } from 'react-select-event'; // ...test usage await select(selectEl, 'Option 2', { container: document.body }); -```` +``` Issue [#50442](https://github.com/grafana/grafana/issues/50442) @@ -3317,8 +3353,8 @@ The change in behavior is that negative-valued series are now stacked downwards The meaning of the default data source has now changed from being a persisted property in a panel. Before when you selected the default data source for a panel and later changed the default data source to another data source it would change all panels who were configured to use the default data source. From now on the default data source is just the default for new panels and changing the default will not impact any currently saved dashboards. Issue [#45132](https://github.com/grafana/grafana/issues/45132) -The Tooltip component provided by `@grafana/ui` is no longer automatically interactive (that is you can hover onto it and click a link or select text). It will from now on by default close automatically when you mouse out from the trigger element. To make tooltips behave like before set the new `interactive` property to true. - Issue [#45053](https://github.com/grafana/grafana/issues/45053) +The Tooltip component provided by `@grafana/ui` is no longer automatically interactive (that is you can hover onto it and click a link or select text). It will from now on by default close automatically when you mouse out from the trigger element. To make tooltips behave like before set the new `interactive` property to true. +Issue [#45053](https://github.com/grafana/grafana/issues/45053) ### Deprecations diff --git a/contribute/style-guides/testing.md b/contribute/style-guides/testing.md new file mode 100644 index 00000000000..7ca50d926b9 --- /dev/null +++ b/contribute/style-guides/testing.md @@ -0,0 +1,225 @@ +# Testing Guidelines + +The goal of this document is to address the most frequently asked "How to" questions related to unit testing. + +## Best practices + +- Default to the `*ByRole` queries when testing components as it encourages testing with accessibility concerns in mind. It's also possible to use `*ByLabelText` queries. However, the `*ByRole` queries are [more robust](https://testing-library.com/docs/queries/bylabeltext/#name) and are generally recommended over the former. + +## Testing User Interactions + +We use the [user-event](https://testing-library.com/docs/user-event/intro) library for simulating user interactions during testing. This library is preferred over the built-in `fireEvent` method, as it more accurately mirrors real user interactions with elements. + +There are two important considerations when working with `userEvent`: + +1. All methods in `userEvent` are asynchronous, and thus require the use of `await` when called. +2. Directly calling methods from `userEvent` may not be supported in future versions. As such, it's necessary to first call `userEvent.setup()` prior to the tests. This method returns a `userEvent` instance, complete with all its methods. This setup process can be simplified using a utility function: + +```tsx +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +function setup(jsx: JSX.Element) { + return { + user: userEvent.setup(), + ...render(jsx), + }; +} + +it('should render', async () => { + const { user } = setup( + + + + )} + + ); +} diff --git a/public/app/features/browse-dashboards/fixtures/folder.fixture.ts b/public/app/features/browse-dashboards/fixtures/folder.fixture.ts new file mode 100644 index 00000000000..14cb3a83652 --- /dev/null +++ b/public/app/features/browse-dashboards/fixtures/folder.fixture.ts @@ -0,0 +1,25 @@ +import { Chance } from 'chance'; + +import { FolderDTO } from 'app/types'; + +export function mockFolderDTO(seed = 1, partial?: Partial): FolderDTO { + const random = Chance(seed); + const uid = random.guid(); + return { + canAdmin: true, + canDelete: true, + canEdit: true, + canSave: true, + created: new Date(random.timestamp()).toISOString(), + createdBy: '', + hasAcl: true, + id: 1, + title: random.sentence({ words: 3 }), + uid, + updated: new Date(random.timestamp()).toISOString(), + updatedBy: '', + url: `/dashboards/f/${uid}`, + version: 1, + ...partial, + }; +} diff --git a/public/app/features/commandPalette/actions/dashboardActions.ts b/public/app/features/commandPalette/actions/dashboardActions.ts index 44d6ff4ebf6..be10a89d11c 100644 --- a/public/app/features/commandPalette/actions/dashboardActions.ts +++ b/public/app/features/commandPalette/actions/dashboardActions.ts @@ -1,7 +1,6 @@ import debounce from 'debounce-promise'; import { useEffect, useRef, useState } from 'react'; -import { locationUtil } from '@grafana/data'; import { config } from '@grafana/runtime'; import { t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; @@ -43,7 +42,7 @@ export async function getRecentDashboardActions(): Promise 0 && !isCreateAction ? idForNavItem(parents[parents.length - 1]) : undefined, priority: priority, diff --git a/public/app/features/dashboard/components/SubMenu/SubMenu.tsx b/public/app/features/dashboard/components/SubMenu/SubMenu.tsx index 125c8c731aa..6f0488ec342 100644 --- a/public/app/features/dashboard/components/SubMenu/SubMenu.tsx +++ b/public/app/features/dashboard/components/SubMenu/SubMenu.tsx @@ -2,7 +2,8 @@ import { css } from '@emotion/css'; import React, { PureComponent } from 'react'; import { connect, MapStateToProps } from 'react-redux'; -import { AnnotationQuery, DataQuery } from '@grafana/data'; +import { AnnotationQuery, DataQuery, GrafanaTheme2 } from '@grafana/data'; +import { stylesFactory, Themeable2, withTheme2 } from '@grafana/ui'; import { StoreState } from '../../../../types'; import { getSubMenuVariables, getVariablesState } from '../../../variables/state/selectors'; @@ -14,7 +15,7 @@ import { Annotations } from './Annotations'; import { DashboardLinks } from './DashboardLinks'; import { SubMenuItems } from './SubMenuItems'; -interface OwnProps { +interface OwnProps extends Themeable2 { dashboard: DashboardModel; links: DashboardLink[]; annotations: AnnotationQuery[]; @@ -43,7 +44,9 @@ class SubMenuUnConnected extends PureComponent { }; render() { - const { dashboard, variables, links, annotations } = this.props; + const { dashboard, variables, links, annotations, theme } = this.props; + + const styles = getStyles(theme); if (!dashboard.isSubMenuVisible()) { return null; @@ -52,8 +55,8 @@ class SubMenuUnConnected extends PureComponent { const readOnlyVariables = dashboard.meta.isSnapshot ?? false; return ( -
-
+
+ { onAnnotationChanged={this.onAnnotationStateChanged} events={dashboard.events} /> -
+
{dashboard && }
); @@ -76,12 +79,28 @@ const mapStateToProps: MapStateToProps = ( }; }; -const styles = css` - display: flex; - flex-wrap: wrap; - display: contents; -`; +const getStyles = stylesFactory((theme: GrafanaTheme2) => { + return { + formStyles: css` + display: flex; + flex-wrap: wrap; + display: contents; + `, + submenu: css` + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-content: flex-start; + align-items: flex-start; + gap: ${theme.spacing(1)} ${theme.spacing(2)}; + padding: 0 0 ${theme.spacing(1)} 0; + `, + spacer: css({ + flexGrow: 1, + }), + }; +}); -export const SubMenu = connect(mapStateToProps)(SubMenuUnConnected); +export const SubMenu = withTheme2(connect(mapStateToProps)(SubMenuUnConnected)); SubMenu.displayName = 'SubMenu'; diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index e9c5a1fa95a..24514467686 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -437,23 +437,25 @@ function updateStatePageNavFromProps(props: Props, state: State): State { }; } - // Check if folder changed const { folderTitle, folderUid } = dashboard.meta; - if (folderTitle && folderUid && pageNav && pageNav.parentItem?.text !== folderTitle) { + if (folderUid && pageNav) { if (config.featureToggles.nestedFolders) { - const folderNavModel = folderUid ? getNavModel(navIndex, `folder-dashboards-${folderUid}`).main : undefined; + const folderNavModel = getNavModel(navIndex, `folder-dashboards-${folderUid}`).main; pageNav = { ...pageNav, parentItem: folderNavModel, }; } else { - pageNav = { - ...pageNav, - parentItem: { - text: folderTitle, - url: `/dashboards/f/${dashboard.meta.folderUid}`, - }, - }; + // Check if folder changed + if (folderTitle && pageNav.parentItem?.text !== folderTitle) { + pageNav = { + ...pageNav, + parentItem: { + text: folderTitle, + url: `/dashboards/f/${dashboard.meta.folderUid}`, + }, + }; + } } } diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts index b562b70b853..c1168e31099 100644 --- a/public/app/features/dashboard/services/DashboardSrv.ts +++ b/public/app/features/dashboard/services/DashboardSrv.ts @@ -50,14 +50,11 @@ export class DashboardSrv { return new DashboardModel(dashboard, meta); } - setCurrent(dashboard: DashboardModel) { + setCurrent(dashboard: DashboardModel | undefined) { this.dashboard = dashboard; } getCurrent(): DashboardModel | undefined { - if (!this.dashboard) { - console.warn('Calling getDashboardSrv().getCurrent() without calling getDashboardSrv().setCurrent() first.'); - } return this.dashboard; } diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index a8e9af013a4..4af55f70582 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -768,6 +768,7 @@ export class DashboardModel implements TimeModel { for (let optionIndex = 0; optionIndex < selectedOptions.length; optionIndex++) { const option = selectedOptions[optionIndex]; const rowCopy = this.getRowRepeatClone(panel, optionIndex, panelIndex); + setScopedVars(rowCopy, option); const rowHeight = this.getRowHeight(rowCopy); @@ -955,6 +956,10 @@ export class DashboardModel implements TimeModel { row.collapsed = false; const rowPanels = row.panels ?? []; const hasRepeat = rowPanels.some((p: PanelModel) => p.repeat); + + // This is set only for the row being repeated. + const rowRepeatVariable = row.repeat; + if (rowPanels.length > 0) { // Use first panel to figure out if it was moved or pushed // If the panel doesn't have gridPos.y, use the row gridPos.y instead. @@ -969,6 +974,18 @@ export class DashboardModel implements TimeModel { let yMax = row.gridPos.y; for (const panel of rowPanels) { + // When expanding original row that's repeated, set scopedVars for repeated row panels. + if (rowRepeatVariable) { + const variable = this.getPanelRepeatVariable(row); + panel.scopedVars ??= {}; + if (variable) { + const selectedOptions = this.getSelectedVariableOptions(variable); + panel.scopedVars = { + ...panel.scopedVars, + [variable.name]: selectedOptions[0], + }; + } + } // set the y gridPos if it wasn't already set panel.gridPos.y ?? (panel.gridPos.y = row.gridPos.y); // (Safari 13.1 lacks ??= support) // make sure y is adjusted (in case row moved while collapsed) diff --git a/public/app/features/dashboard/state/actions.ts b/public/app/features/dashboard/state/actions.ts index 1b089434123..7d449b7616f 100644 --- a/public/app/features/dashboard/state/actions.ts +++ b/public/app/features/dashboard/state/actions.ts @@ -9,6 +9,7 @@ import { DashboardAcl, DashboardAclUpdateDTO, NewDashboardAclItem, PermissionLev import { loadPluginDashboards } from '../../plugins/admin/state/actions'; import { cancelVariables } from '../../variables/state/actions'; +import { getDashboardSrv } from '../services/DashboardSrv'; import { getTimeSrv } from '../services/TimeSrv'; import { cleanUpDashboard, loadDashboardPermissions } from './reducers'; @@ -124,10 +125,12 @@ export const cleanUpDashboardAndVariables = (): ThunkResult => (dispatch, } getTimeSrv().stopAutoRefresh(); - dispatch(cleanUpDashboard()); dispatch(removeAllPanels()); + dashboardWatcher.leave(); + + getDashboardSrv().setCurrent(undefined); }; export const updateTimeZoneDashboard = diff --git a/public/app/features/dashboard/state/initDashboard.ts b/public/app/features/dashboard/state/initDashboard.ts index c4786a2529d..9964b32a700 100644 --- a/public/app/features/dashboard/state/initDashboard.ts +++ b/public/app/features/dashboard/state/initDashboard.ts @@ -104,6 +104,12 @@ async function fetchDashboard( return dashDTO; } case DashboardRoutes.New: { + // only the folder API has information about ancestors + // get parent folder (if it exists) and put it in the store + // this will be used to populate the full breadcrumb trail + if (config.featureToggles.nestedFolders && args.urlFolderUid) { + await dispatch(getFolderByUid(args.urlFolderUid)); + } return getNewDashboardModelData(args.urlFolderUid, args.panelType); } case DashboardRoutes.Path: { diff --git a/public/app/features/logs/components/getLogRowStyles.ts b/public/app/features/logs/components/getLogRowStyles.ts index 4c737403bbc..2062fdec656 100644 --- a/public/app/features/logs/components/getLogRowStyles.ts +++ b/public/app/features/logs/components/getLogRowStyles.ts @@ -70,7 +70,6 @@ export const getLogRowStyles = memoizeOne((theme: GrafanaTheme2) => { font-family: ${theme.typography.fontFamilyMonospace}; font-size: ${theme.typography.bodySmall.fontSize}; width: 100%; - margin-bottom: ${theme.spacing(2.25)}; /* This is to make sure the last row's LogRowMenu is not cut off. */ `, contextBackground: css` background: ${hoverBgColor}; diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index c31c13c1900..0ebe1891ce2 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -254,7 +254,8 @@ export class DatasourceSrv implements DataSourceService { // Support for multi-value variables with only one selected datasource dsValue = dsValue[0]; } - const dsSettings = !Array.isArray(dsValue) && this.settingsMapByName[dsValue]; + const dsSettings = + !Array.isArray(dsValue) && (this.settingsMapByName[dsValue] || this.settingsMapByUid[dsValue]); if (dsSettings) { const key = `$\{${variable.name}\}`; diff --git a/public/app/features/plugins/tests/datasource_srv.test.ts b/public/app/features/plugins/tests/datasource_srv.test.ts index 319cf5ed141..a6ff1cc3b2c 100644 --- a/public/app/features/plugins/tests/datasource_srv.test.ts +++ b/public/app/features/plugins/tests/datasource_srv.test.ts @@ -18,6 +18,13 @@ const templateSrv: any = { value: 'BBB', }, }, + { + type: 'datasource', + name: 'datasourceByUid', + current: { + value: 'uid-code-DDDD', + }, + }, { type: 'datasource', name: 'datasourceDefault', @@ -32,6 +39,7 @@ const templateSrv: any = { } let result = v.replace('${datasource}', 'BBB'); + result = result.replace('${datasourceByUid}', 'DDDD'); result = result.replace('${datasourceDefault}', 'default'); return result; }, @@ -103,6 +111,12 @@ describe('datasource_srv', () => { meta: { metrics: true }, isDefault: true, }, + DDDD: { + type: 'test-db', + name: 'DDDD', + uid: 'uid-code-DDDD', + meta: { metrics: true }, + }, Jaeger: { type: 'jaeger-db', name: 'Jaeger', @@ -165,7 +179,7 @@ describe('datasource_srv', () => { expect(dataSourceSrv.getInstanceSettings({ uid: 'uid-code-mmm' })).toBe(ds); }); - it('should work with variable', () => { + it('should work with variable by ds name', () => { const ds = dataSourceSrv.getInstanceSettings('${datasource}'); expect(ds?.name).toBe('${datasource}'); expect(ds?.uid).toBe('${datasource}'); @@ -177,6 +191,18 @@ describe('datasource_srv', () => { `); }); + it('should work with variable by ds value (uid)', () => { + const ds = dataSourceSrv.getInstanceSettings('${datasourceByUid}'); + expect(ds?.name).toBe('${datasourceByUid}'); + expect(ds?.uid).toBe('${datasourceByUid}'); + expect(ds?.rawRef).toMatchInlineSnapshot(` + { + "type": "test-db", + "uid": "uid-code-DDDD", + } + `); + }); + it('should work with variable via scopedVars', () => { const ds = dataSourceSrv.getInstanceSettings('${datasource}', { datasource: { text: 'Prom', value: 'uid-code-aaa' }, @@ -247,7 +273,7 @@ describe('datasource_srv', () => { describe('when getting external metric sources', () => { it('should return list of explore sources', () => { const externalSources = dataSourceSrv.getExternal(); - expect(externalSources.length).toBe(6); + expect(externalSources.length).toBe(7); }); }); @@ -260,8 +286,9 @@ describe('datasource_srv', () => { it('Can get list of data sources with variables: true', () => { const list = dataSourceSrv.getList({ metrics: true, variables: true }); - expect(list[0].name).toBe('${datasourceDefault}'); - expect(list[1].name).toBe('${datasource}'); + expect(list[0].name).toBe('${datasourceByUid}'); + expect(list[1].name).toBe('${datasourceDefault}'); + expect(list[2].name).toBe('${datasource}'); }); it('Can get list of data sources with tracing: true', () => { @@ -300,6 +327,14 @@ describe('datasource_srv', () => { "type": "test-db", "uid": "uid-code-BBB", }, + { + "meta": { + "metrics": true, + }, + "name": "DDDD", + "type": "test-db", + "uid": "uid-code-DDDD", + }, { "meta": { "annotations": true, diff --git a/public/app/features/search/tempI18nPhrases.ts b/public/app/features/search/tempI18nPhrases.ts index 83b99bf2570..40c276270ae 100644 --- a/public/app/features/search/tempI18nPhrases.ts +++ b/public/app/features/search/tempI18nPhrases.ts @@ -10,11 +10,11 @@ export function getSearchPlaceholder(includePanels = false) { } export function getNewDashboardPhrase() { - return t('search.dashboard-actions.new-dashboard', 'New Dashboard'); + return t('search.dashboard-actions.new-dashboard', 'New dashboard'); } export function getNewFolderPhrase() { - return t('search.dashboard-actions.new-folder', 'New Folder'); + return t('search.dashboard-actions.new-folder', 'New folder'); } export function getImportPhrase() { diff --git a/public/app/features/transformers/configFromQuery/configFromQuery.ts b/public/app/features/transformers/configFromQuery/configFromQuery.ts index 5b3bc7cb2c6..0ac2fc25788 100644 --- a/public/app/features/transformers/configFromQuery/configFromQuery.ts +++ b/public/app/features/transformers/configFromQuery/configFromQuery.ts @@ -66,6 +66,7 @@ export function extractConfigFromQuery(options: ConfigFromQueryTransformOptions, const outputFrame: DataFrame = { fields: [], length: frame.length, + refId: frame.refId, }; for (const field of frame.fields) { @@ -85,7 +86,6 @@ export function extractConfigFromQuery(options: ConfigFromQueryTransformOptions, output.push(outputFrame); } - return output; } diff --git a/public/app/features/variables/datasource/reducer.ts b/public/app/features/variables/datasource/reducer.ts index 9e422dae7b5..d28abaf014f 100644 --- a/public/app/features/variables/datasource/reducer.ts +++ b/public/app/features/variables/datasource/reducer.ts @@ -42,7 +42,7 @@ export const dataSourceVariableSlice = createSlice({ } if (isValid(source, regex)) { - options.push({ text: source.name, value: source.name, selected: false }); + options.push({ text: source.name, value: source.uid, selected: false }); } if (isDefault(source, regex)) { diff --git a/public/app/features/variables/editor/VariableEditorList.tsx b/public/app/features/variables/editor/VariableEditorList.tsx index 26b0723e688..5a5fe5a28cd 100644 --- a/public/app/features/variables/editor/VariableEditorList.tsx +++ b/public/app/features/variables/editor/VariableEditorList.tsx @@ -59,7 +59,7 @@ export function VariableEditorList({ Variable - Definition + Description diff --git a/public/app/features/variables/editor/VariableEditorListRow.tsx b/public/app/features/variables/editor/VariableEditorListRow.tsx index 5b378bb0afb..95a1bdb16e4 100644 --- a/public/app/features/variables/editor/VariableEditorListRow.tsx +++ b/public/app/features/variables/editor/VariableEditorListRow.tsx @@ -7,7 +7,7 @@ import { selectors } from '@grafana/e2e-selectors'; import { reportInteraction } from '@grafana/runtime'; import { Button, Icon, IconButton, useStyles2, useTheme2 } from '@grafana/ui'; -import { hasOptions, isAdHoc, isQuery } from '../guard'; +import { isAdHoc } from '../guard'; import { VariableUsagesButton } from '../inspect/VariableUsagesButton'; import { getVariableUsages, UsagesToNetwork, VariableUsageTree } from '../inspect/utils'; import { KeyedVariableIdentifier } from '../state/types'; @@ -35,7 +35,6 @@ export function VariableEditorListRow({ }: VariableEditorListRowProps): ReactElement { const theme = useTheme2(); const styles = useStyles2(getStyles); - const definition = getDefinition(variable); const usages = getVariableUsages(variable.id, usageTree); const passed = usages > 0 || isAdHoc(variable); const identifier = toKeyedVariableIdentifier(variable); @@ -68,14 +67,14 @@ export function VariableEditorListRow({ { event.preventDefault(); propsOnEdit(identifier); }} - aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDefinitionFields(variable.name)} + aria-label={selectors.pages.Dashboard.Settings.Variables.List.tableRowDescriptionFields(variable.name)} > - {definition} + {variable.description} @@ -122,21 +121,6 @@ export function VariableEditorListRow({ ); } -function getDefinition(model: VariableModel): string { - let definition = ''; - if (isQuery(model)) { - if (model.definition) { - definition = model.definition; - } else if (typeof model.query === 'string') { - definition = model.query; - } - } else if (hasOptions(model)) { - definition = model.query; - } - - return definition; -} - interface VariableCheckIndicatorProps { passed: boolean; } @@ -174,7 +158,7 @@ function getStyles(theme: GrafanaTheme2) { cursor: pointer; color: ${theme.colors.primary.text}; `, - definitionColumn: css` + descriptionColumn: css` width: 100%; max-width: 200px; cursor: pointer; diff --git a/public/app/features/variables/shared/testing/helpers.ts b/public/app/features/variables/shared/testing/helpers.ts index ea9abfb7f48..83e02bf29e2 100644 --- a/public/app/features/variables/shared/testing/helpers.ts +++ b/public/app/features/variables/shared/testing/helpers.ts @@ -3,7 +3,7 @@ import { DataSourceInstanceSettings, DataSourceJsonData, DataSourcePluginMeta } export function getDataSourceInstanceSetting(name: string, meta: DataSourcePluginMeta): DataSourceInstanceSettings { return { id: 1, - uid: '', + uid: name, type: '', name, meta, diff --git a/public/app/features/variables/shared/testing/optionsVariableBuilder.ts b/public/app/features/variables/shared/testing/optionsVariableBuilder.ts index 18f2100e0b1..97bed58572a 100644 --- a/public/app/features/variables/shared/testing/optionsVariableBuilder.ts +++ b/public/app/features/variables/shared/testing/optionsVariableBuilder.ts @@ -3,14 +3,20 @@ import { VariableOption, VariableWithOptions } from 'app/features/variables/type import { VariableBuilder } from './variableBuilder'; export class OptionsVariableBuilder extends VariableBuilder { - withOptions(...texts: string[]) { + withOptions(...options: Array) { this.variable.options = []; - for (let index = 0; index < texts.length; index++) { - this.variable.options.push({ - text: texts[index], - value: texts[index], - selected: false, - }); + for (let index = 0; index < options.length; index++) { + const option = options[index]; + + if (typeof option === 'string') { + this.variable.options.push({ + text: option, + value: option, + selected: false, + }); + } else { + this.variable.options.push({ ...option, selected: false }); + } } return this; } diff --git a/public/app/features/variables/state/actions.test.ts b/public/app/features/variables/state/actions.test.ts index 3ec73fe3137..675c39c3cb4 100644 --- a/public/app/features/variables/state/actions.test.ts +++ b/public/app/features/variables/state/actions.test.ts @@ -381,6 +381,74 @@ describe('shared actions', () => { }); }); + describe('and not multivalue, but with currentValue specified', () => { + const A = { text: 'A', value: 'a-uid' }; + const B = { text: 'B', value: 'b-uid' }; + const C = { text: 'C', value: 'c-uid' }; + + it.each` + withOptions | currentText | currentValue | defaultValue | expected + ${[A, B, C]} | ${undefined} | ${undefined} | ${undefined} | ${A} + ${[A, B, C]} | ${'B'} | ${'b-uid'} | ${undefined} | ${B} + ${[A, B, C]} | ${'B'} | ${undefined} | ${undefined} | ${B} + ${[A, B, C]} | ${undefined} | ${'b-uid'} | ${undefined} | ${B} + ${[A, B, C]} | ${'Old B'} | ${'b-uid'} | ${undefined} | ${B} + ${[A, B, C]} | ${undefined} | ${'x-uid'} | ${'b-uid'} | ${B} + ${[A, B, C]} | ${undefined} | ${'b-uid'} | ${'c-uid'} | ${B} + ${[A, B, C]} | ${undefined} | ${'x-uid'} | ${undefined} | ${A} + ${undefined} | ${undefined} | ${'b-uid'} | ${undefined} | ${'should not dispatch setCurrentVariableValue'} + `( + 'then correct actions are dispatched', + async ({ withOptions, currentText, currentValue, defaultValue, expected }) => { + let custom; + const key = 'key'; + if (!withOptions) { + custom = customBuilder() + .withId('0') + .withRootStateKey(key) + .withCurrent(currentText, currentValue) + .withoutOptions() + .build(); + } else { + custom = customBuilder() + .withId('0') + .withRootStateKey(key) + .withOptions(...withOptions) + .withCurrent(currentText, currentValue) + .build(); + } + + const tester = await reduxTester() + .givenRootReducer(getTemplatingRootReducer()) + .whenActionIsDispatched( + toKeyedAction(key, addVariable(toVariablePayload(custom, { global: false, index: 0, model: custom }))) + ) + .whenAsyncActionIsDispatched( + validateVariableSelectionState(toKeyedVariableIdentifier(custom), defaultValue), + true + ); + + await tester.thenDispatchedActionsPredicateShouldEqual((dispatchedActions) => { + const expectedActions: AnyAction[] = withOptions + ? [ + toKeyedAction( + key, + setCurrentVariableValue( + toVariablePayload( + { type: 'custom', id: '0' }, + { option: { text: expected.text, value: expected.value, selected: false } } + ) + ) + ), + ] + : []; + expect(dispatchedActions).toEqual(expectedActions); + return true; + }); + } + ); + }); + describe('and multivalue', () => { it.each` withOptions | withCurrent | defaultValue | expectedText | expectedSelected diff --git a/public/app/features/variables/state/actions.ts b/public/app/features/variables/state/actions.ts index 4cd626cba7d..612f1217683 100644 --- a/public/app/features/variables/state/actions.ts +++ b/public/app/features/variables/state/actions.ts @@ -60,6 +60,7 @@ import { ensureStringValues, ExtendedUrlQueryMap, getCurrentText, + getCurrentValue, getVariableRefresh, hasOngoingTransaction, toKeyedVariableIdentifier, @@ -522,14 +523,16 @@ export const validateVariableSelectionState = ( // 1. find the current value const text = getCurrentText(variableInState); - option = variableInState.options?.find((v) => v.text === text); + const value = getCurrentValue(variableInState); + + option = variableInState.options?.find((v: VariableOption) => v.text === text || v.value === value); if (option) { return setValue(variableInState, option); } // 2. find the default value if (defaultValue) { - option = variableInState.options?.find((v) => v.text === defaultValue); + option = variableInState.options?.find((v) => v.text === defaultValue || v.value === defaultValue); if (option) { return setValue(variableInState, option); } diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/completion/types.ts b/public/app/plugins/datasource/cloudwatch/language/logs/completion/types.ts new file mode 100644 index 00000000000..58534242044 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/language/logs/completion/types.ts @@ -0,0 +1,15 @@ +import { TokenTypes } from '../../monarch/types'; + +export const LogsTokenTypes: TokenTypes = { + Parenthesis: 'delimiter.parenthesis.cloudwatch-logs', + Whitespace: 'white.cloudwatch-logs', + Keyword: 'keyword.cloudwatch-logs', + Delimiter: 'delimiter.cloudwatch-logs', + Operator: 'operator.cloudwatch-logs', + Identifier: 'identifier.cloudwatch-logs', + Type: 'type.cloudwatch-logs', + Function: 'predefined.cloudwatch-logs', + Number: 'number.cloudwatch-logs', + String: 'string.cloudwatch-logs', + Variable: 'variable.cloudwatch-logs', +}; diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/definition.ts b/public/app/plugins/datasource/cloudwatch/language/logs/definition.ts new file mode 100644 index 00000000000..7170f4be9a0 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/language/logs/definition.ts @@ -0,0 +1,10 @@ +import { LanguageDefinition } from '../monarch/register'; + +const cloudWatchLogsLanguageDefinition: LanguageDefinition = { + id: 'logs', + extensions: [], + aliases: [], + mimetypes: [], + loader: () => import('./language'), +}; +export default cloudWatchLogsLanguageDefinition; diff --git a/public/app/plugins/datasource/cloudwatch/language/logs/language.ts b/public/app/plugins/datasource/cloudwatch/language/logs/language.ts new file mode 100644 index 00000000000..359b9d391e2 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/language/logs/language.ts @@ -0,0 +1,181 @@ +import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api'; + +// CloudWatch Logs: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html +interface CloudWatchLogsLanguage extends monacoType.languages.IMonarchLanguage { + commands: string[]; + operators: string[]; + builtinFunctions: string[]; +} + +export const DISPLAY = 'display'; +export const FIELDS = 'fields'; +export const FILTER = 'filter'; +export const STATS = 'stats'; +export const SORT = 'sort'; +export const LIMIT = 'limit'; +export const PARSE = 'parse'; +export const UNMASK = 'unmask'; //make sure we support this one +export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, STATS, SORT, LIMIT, PARSE, UNMASK]; + +export const LOGS_LOGIC_OPERATORS = ['and', 'or', 'not']; + +export const LOGS_FUNCTION_OPERATORS = [ + // math + 'abs', + 'ceil', + 'floor', + 'greatest', + 'least', + 'log', + 'sqrt', + // datetime + 'bin', + 'datefloor', + 'dateceil', + 'fromMillis', + 'toMillis', + // general + 'ispresent', + 'coalesce', + // ip + 'isValidIp', + 'isValidIpV4', + 'isValidIpV6', + 'isIpInSubnet', + 'isIpv4InSubnet', + 'isIpv6InSubnet', + // stats aggregation + 'avg', + 'count', + 'count_distinct', + 'max', + 'min', + 'pct', + 'stddev', + 'sum', + // stats non-aggregation + 'earliest', + 'latest', + 'sortsFirst', + 'sortsLast', + // strings + 'isempty', + 'isblank', + 'concat', + 'ltrim', + 'rtrim', + 'trim', + 'strlen', + 'toupper', + 'tolower', + 'substr', + 'replace', + 'strcontains', +]; + +export const LOGS_KEYWORDS = ['like', 'by', 'in', 'desc', 'asc', 'as']; + +export const language: CloudWatchLogsLanguage = { + defaultToken: 'invalid', + id: 'logs', + ignoreCase: true, + brackets: [ + { open: '[', close: ']', token: 'delimiter.square' }, + { open: '(', close: ')', token: 'delimiter.parenthesis' }, + ], + commands: [...LOGS_COMMANDS, ...LOGS_KEYWORDS], + operators: LOGS_LOGIC_OPERATORS, + builtinFunctions: LOGS_FUNCTION_OPERATORS, + tokenizer: { + root: [ + { include: '@comments' }, + { include: '@regexes' }, + { include: '@whitespace' }, + { include: '@fieldNames' }, + { include: '@variables' }, + { include: '@strings' }, + { include: '@numbers' }, + + [/\|\|/, 'operator'], + [/[,.:\|]/, 'delimiter'], + [/[()\[\]]/, 'delimiter.parenthesis'], + [ + /[\w@#$]+/, + { + cases: { + '@commands': 'keyword', + '@builtinFunctions': 'predefined', + '@operators': 'operator', + '@default': 'identifier', + }, + }, + ], + [/[+\-*/^%=!<>]/, 'operator'], // handles the math operators + ], + variables: [ + [/\${/, { token: 'variable', next: '@variable_bracket' }], + [/\$[a-zA-Z0-9-_]+/, 'variable'], + ], + variable_bracket: [ + [/[a-zA-Z0-9-_:]+/, 'variable'], + [/}/, { token: 'variable', next: '@pop' }], + ], + fieldNames: [[/(@[_a-zA-Z]+[_.0-9a-zA-Z]*)|(`((\\`)|([^`]))*?`)/, 'identifier']], + whitespace: [[/\s+/, 'white']], + comments: [ + [/^#.*/, 'comment'], + [/\s+#.*/, 'comment'], + ], + numbers: [ + [/0[xX][0-9a-fA-F]*/, 'number'], + [/[$][+-]*\d*(\.\d*)?/, 'number'], + [/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, 'number'], + ], + strings: [ + [/'/, { token: 'string', next: '@string' }], + [/"/, { token: 'string', next: '@string_double' }], + [/`/, { token: 'identifier', next: '@string_backtick' }], + ], + string: [ + [/[^']+/, 'string'], + [/''/, 'string'], + [/'/, { token: 'string', next: '@pop' }], + ], + string_double: [ + [/[^\\"]+/, 'string'], + [/"/, 'string', '@pop'], + ], + string_backtick: [ + [/[^\\`]+/, 'identifier'], + [/`/, 'identifier', '@pop'], + ], + regexes: [[/\/.*?\/(?=\s*\||\s*$|,)/, 'regexp']], + }, +}; + +export const conf: monacoType.languages.LanguageConfiguration = { + comments: { + lineComment: '#', + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'], + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + { open: '`', close: '`' }, + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + { open: '`', close: '`' }, + ], +}; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxCheatSheet.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxCheatSheet.tsx index 7bf562c2cff..04b573159c6 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxCheatSheet.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxCheatSheet.tsx @@ -8,7 +8,7 @@ const CHEAT_SHEET_ITEMS = [ }, ]; -const InfluxCheatSheet = (props: any) => ( +export const InfluxCheatSheet = () => (

InfluxDB Cheat Sheet

{CHEAT_SHEET_ITEMS.map((item) => ( @@ -19,5 +19,3 @@ const InfluxCheatSheet = (props: any) => ( ))}
); - -export default InfluxCheatSheet; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxStartPage.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxStartPage.tsx index f5ac86a24d2..10f35190d08 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxStartPage.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/InfluxStartPage.tsx @@ -1,11 +1,7 @@ -import React, { PureComponent } from 'react'; +import React from 'react'; -import { QueryEditorHelpProps } from '@grafana/data'; +import { InfluxCheatSheet } from './InfluxCheatSheet'; -import InfluxCheatSheet from './InfluxCheatSheet'; - -export default class InfluxStartPage extends PureComponent { - render() { - return ; - } +export function InfluxStartPage() { + return ; } diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/code/RawInfluxQLEditor.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/code/RawInfluxQLEditor.tsx index 887bec9ee93..0df57b7b26d 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/code/RawInfluxQLEditor.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/code/RawInfluxQLEditor.tsx @@ -4,7 +4,7 @@ import { HorizontalGroup, InlineFormLabel, Input, Select, TextArea } from '@graf import { InfluxQuery } from '../../../../../types'; import { DEFAULT_RESULT_FORMAT, RESULT_FORMATS } from '../../../constants'; -import { useShadowedState } from '../../hooks/useShadowedState'; +import { useShadowedState } from '../hooks/useShadowedState'; type Props = { query: InfluxQuery; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.test.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.test.ts new file mode 100644 index 00000000000..e4648e2f1e7 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.test.ts @@ -0,0 +1,44 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import config from 'app/core/config'; + +import { getMockDS, getMockDSInstanceSettings, mockBackendService } from '../../../../../specs/mocks'; + +import { useRetentionPolicies } from './useRetentionPolicies'; + +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), +})); + +describe('useRetentionPolicies', () => { + it('should return all policies when influxdbBackendMigration feature toggle enabled', async () => { + const instanceSettings = getMockDSInstanceSettings(); + const datasource = getMockDS(instanceSettings); + mockBackendService(response); + + config.featureToggles.influxdbBackendMigration = true; + const { result, waitForNextUpdate } = renderHook(() => useRetentionPolicies(datasource)); + await waitForNextUpdate(); + expect(result.current.retentionPolicies.length).toEqual(4); + expect(result.current.retentionPolicies[0]).toEqual('autogen'); + }); +}); + +const response = { + data: { + results: { + metadataQuery: { + status: 200, + frames: [ + { + schema: { + refId: 'metadataQuery', + fields: [{ name: 'value', type: 'string', typeInfo: { frame: 'string' } }], + }, + data: { values: [['autogen', 'bar', '5m_avg', '1m_avg']] }, + }, + ], + }, + }, + }, +}; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.ts new file mode 100644 index 00000000000..c25b795294c --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useRetentionPolicies.ts @@ -0,0 +1,15 @@ +import { useEffect, useState } from 'react'; + +import InfluxDatasource from '../../../../../datasource'; +import { getAllPolicies } from '../../../../../influxql_metadata_query'; + +export const useRetentionPolicies = (datasource: InfluxDatasource) => { + const [retentionPolicies, setRetentionPolicies] = useState([]); + useEffect(() => { + getAllPolicies(datasource).then((data) => { + setRetentionPolicies(data); + }); + }, [datasource]); + + return { retentionPolicies }; +}; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/hooks/useShadowedState.test.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useShadowedState.test.ts similarity index 100% rename from public/app/plugins/datasource/influxdb/components/editor/query/hooks/useShadowedState.test.ts rename to public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useShadowedState.test.ts diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/hooks/useShadowedState.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useShadowedState.ts similarity index 100% rename from public/app/plugins/datasource/influxdb/components/editor/query/hooks/useShadowedState.ts rename to public/app/plugins/datasource/influxdb/components/editor/query/influxql/hooks/useShadowedState.ts diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/filterTags.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/filterTags.ts new file mode 100644 index 00000000000..904de835063 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/filterTags.ts @@ -0,0 +1,7 @@ +// it is possible to add fields into the `InfluxQueryTag` structures, and they do work, +// but in some cases, when we do metadata queries, we have to remove them from the queries. +import { InfluxQueryTag } from '../../../../../types'; + +export function filterTags(parts: InfluxQueryTag[], allTagKeys: Set): InfluxQueryTag[] { + return parts.filter((t) => t.key.endsWith('::tag') || allTagKeys.has(t.key + '::tag')); +} diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/getTemplateVariableOptions.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/getTemplateVariableOptions.ts new file mode 100644 index 00000000000..51db862e4d9 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/getTemplateVariableOptions.ts @@ -0,0 +1,12 @@ +import { TypedVariableModel } from '@grafana/data/src'; +import { getTemplateSrv } from '@grafana/runtime/src'; + +export function getTemplateVariableOptions(wrapper: (v: TypedVariableModel) => string) { + return ( + getTemplateSrv() + .getVariables() + // we make them regex-params, i'm not 100% sure why. + // probably because this way multi-value variables work ok too. + .map(wrapper) + ); +} diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/withTemplateVariableOptions.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/withTemplateVariableOptions.ts new file mode 100644 index 00000000000..89bd1372bcd --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/withTemplateVariableOptions.ts @@ -0,0 +1,16 @@ +// helper function to make it easy to call this from the widget-render-code +import { TypedVariableModel } from '@grafana/data/src'; + +import { getTemplateVariableOptions } from './getTemplateVariableOptions'; + +export function withTemplateVariableOptions( + optionsPromise: Promise, + wrapper: (v: TypedVariableModel) => string, + filter?: string +): Promise { + let templateVariableOptions = getTemplateVariableOptions(wrapper); + if (filter) { + templateVariableOptions = templateVariableOptions.filter((tvo) => tvo.indexOf(filter) > -1); + } + return optionsPromise.then((options) => [...templateVariableOptions, ...options]); +} diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/wrapper.ts b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/wrapper.ts new file mode 100644 index 00000000000..769a9a435a4 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/utils/wrapper.ts @@ -0,0 +1,9 @@ +import { TypedVariableModel } from '@grafana/data/src'; + +export function wrapRegex(v: TypedVariableModel): string { + return `/^$${v.name}$/`; +} + +export function wrapPure(v: TypedVariableModel): string { + return `$${v.name}`; +} diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/InputSection.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/InputSection.tsx index 6bf9e24e9b5..71b9fad90d3 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/InputSection.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/InputSection.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Input } from '@grafana/ui'; -import { useShadowedState } from '../../hooks/useShadowedState'; +import { useShadowedState } from '../hooks/useShadowedState'; import { paddingRightClass } from './styles'; diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/Seg.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/Seg.tsx index 8a2077b0e66..28b679fd5e3 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/Seg.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/Seg.tsx @@ -6,7 +6,7 @@ import { useAsyncFn } from 'react-use'; import { SelectableValue } from '@grafana/data'; import { AsyncSelect, InlineLabel, Input, Select } from '@grafana/ui'; -import { useShadowedState } from '../../hooks/useShadowedState'; +import { useShadowedState } from '../hooks/useShadowedState'; // this file is a simpler version of `grafana-ui / SegmentAsync.tsx` // with some changes: diff --git a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tsx b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tsx index 0a00132e9e1..21951edc28a 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/query/influxql/visual/VisualInfluxQLEditor.tsx @@ -1,9 +1,7 @@ import { css } from '@emotion/css'; import React, { useId, useMemo } from 'react'; -import { useAsync } from 'react-use'; -import { GrafanaTheme2, TypedVariableModel } from '@grafana/data'; -import { getTemplateSrv } from '@grafana/runtime'; +import { GrafanaTheme2 } from '@grafana/data'; import { InlineLabel, SegmentSection, useStyles2 } from '@grafana/ui'; import InfluxDatasource from '../../../../../datasource'; @@ -25,7 +23,11 @@ import { } from '../../../../../queryUtils'; import { InfluxQuery, InfluxQueryTag } from '../../../../../types'; import { DEFAULT_RESULT_FORMAT } from '../../../constants'; +import { useRetentionPolicies } from '../hooks/useRetentionPolicies'; +import { filterTags } from '../utils/filterTags'; import { getNewGroupByPartOptions, getNewSelectPartOptions, makePartList } from '../utils/partListUtils'; +import { withTemplateVariableOptions } from '../utils/withTemplateVariableOptions'; +import { wrapPure, wrapRegex } from '../utils/wrapper'; import { FormatAsSection } from './FormatAsSection'; import { FromSection } from './FromSection'; @@ -41,43 +43,6 @@ type Props = { datasource: InfluxDatasource; }; -function wrapRegex(v: TypedVariableModel): string { - return `/^$${v.name}$/`; -} - -function wrapPure(v: TypedVariableModel): string { - return `$${v.name}`; -} - -function getTemplateVariableOptions(wrapper: (v: TypedVariableModel) => string) { - return ( - getTemplateSrv() - .getVariables() - // we make them regex-params, i'm not 100% sure why. - // probably because this way multi-value variables work ok too. - .map(wrapper) - ); -} - -// helper function to make it easy to call this from the widget-render-code -function withTemplateVariableOptions( - optionsPromise: Promise, - wrapper: (v: TypedVariableModel) => string, - filter?: string -): Promise { - let templateVariableOptions = getTemplateVariableOptions(wrapper); - if (filter) { - templateVariableOptions = templateVariableOptions.filter((tvo) => tvo.indexOf(filter) > -1); - } - return optionsPromise.then((options) => [...templateVariableOptions, ...options]); -} - -// it is possible to add fields into the `InfluxQueryTag` structures, and they do work, -// but in some cases, when we do metadata queries, we have to remove them from the queries. -function filterTags(parts: InfluxQueryTag[], allTagKeys: Set): InfluxQueryTag[] { - return parts.filter((t) => t.key.endsWith('::tag') || allTagKeys.has(t.key + '::tag')); -} - export const VisualInfluxQLEditor = (props: Props): JSX.Element => { const uniqueId = useId(); const formatAsId = `influxdb-qe-format-as-${uniqueId}`; @@ -87,9 +52,7 @@ export const VisualInfluxQLEditor = (props: Props): JSX.Element => { const query = normalizeQuery(props.query); const { datasource } = props; const { measurement, policy } = query; - - const policyData = useAsync(() => getAllPolicies(datasource), [datasource]); - const retentionPolicies = !!policyData.error ? [] : policyData.value ?? []; + const { retentionPolicies } = useRetentionPolicies(datasource); const allTagKeys = useMemo(async () => { const tagKeys = (await getTagKeysForMeasurementAndTags(datasource, [], measurement, policy)).map( diff --git a/public/app/plugins/datasource/influxdb/module.ts b/public/app/plugins/datasource/influxdb/module.ts index da229451278..c293c017d6f 100644 --- a/public/app/plugins/datasource/influxdb/module.ts +++ b/public/app/plugins/datasource/influxdb/module.ts @@ -2,7 +2,7 @@ import { DataSourcePlugin } from '@grafana/data'; import ConfigEditor from './components/editor/config/ConfigEditor'; import { QueryEditor } from './components/editor/query/QueryEditor'; -import InfluxStartPage from './components/editor/query/influxql/InfluxStartPage'; +import { InfluxStartPage } from './components/editor/query/influxql/InfluxStartPage'; import VariableQueryEditor from './components/editor/variable/VariableQueryEditor'; import InfluxDatasource from './datasource'; diff --git a/public/app/plugins/datasource/influxdb/specs/mocks.ts b/public/app/plugins/datasource/influxdb/specs/mocks.ts index 9402f08aa44..72c01d4453a 100644 --- a/public/app/plugins/datasource/influxdb/specs/mocks.ts +++ b/public/app/plugins/datasource/influxdb/specs/mocks.ts @@ -58,6 +58,6 @@ export function getMockDSInstanceSettings(): DataSourceInstanceSettings { const { options, onOptionsChange } = props; + const updatePredefinedOperations = useCallback( + (value: string) => { + reportInteraction('grafana_loki_predefined_operations_changed', { value }); + onOptionsChange(setPredefinedOperations(options, value)); + }, + [options, onOptionsChange] + ); + return ( <> { maxLines={options.jsonData.maxLines || ''} onMaxLinedChange={(value) => onOptionsChange(setMaxLines(options, value))} predefinedOperations={options.jsonData.predefinedOperations || ''} - onPredefinedOperationsChange={(value) => onOptionsChange(setPredefinedOperations(options, value))} + onPredefinedOperationsChange={updatePredefinedOperations} /> trackQuery(response, fixedRequest, startTime))); + return this.runQuery(fixedRequest).pipe( + tap((response) => + trackQuery(response, fixedRequest, startTime, { predefinedOperations: this.predefinedOperations }) + ) + ); } runQuery(fixedRequest: DataQueryRequest) { diff --git a/public/app/plugins/datasource/loki/querySplitting.test.ts b/public/app/plugins/datasource/loki/querySplitting.test.ts index 107a2bf548f..06e5c413e33 100644 --- a/public/app/plugins/datasource/loki/querySplitting.test.ts +++ b/public/app/plugins/datasource/loki/querySplitting.test.ts @@ -91,7 +91,8 @@ describe('runSplitQuery()', () => { }, ], request, - new Date() + new Date(), + { predefinedOperations: '' } ); }); }); diff --git a/public/app/plugins/datasource/loki/querySplitting.ts b/public/app/plugins/datasource/loki/querySplitting.ts index cbb3df4f720..36fb89c61ce 100644 --- a/public/app/plugins/datasource/loki/querySplitting.ts +++ b/public/app/plugins/datasource/loki/querySplitting.ts @@ -17,7 +17,7 @@ import { LoadingState } from '@grafana/schema'; import { LokiDatasource } from './datasource'; import { splitTimeRange as splitLogsTimeRange } from './logsTimeSplitting'; import { splitTimeRange as splitMetricTimeRange } from './metricTimeSplitting'; -import { isLogsQuery, isQueryWithDistinct } from './queryUtils'; +import { isLogsQuery, isQueryWithDistinct, isQueryWithRangeVariable } from './queryUtils'; import { combineResponses } from './responseUtils'; import { trackGroupedQueries } from './tracking'; import { LokiGroupedRequest, LokiQuery, LokiQueryType } from './types'; @@ -213,13 +213,19 @@ function getNextRequestPointers(requests: LokiGroupedRequest[], requestGroup: nu }; } -function querySupporstSplitting(query: LokiQuery) { - return query.queryType !== LokiQueryType.Instant && !isQueryWithDistinct(query.expr); +function querySupportsSplitting(query: LokiQuery) { + return ( + query.queryType !== LokiQueryType.Instant && + !isQueryWithDistinct(query.expr) && + // Queries with $__range variable should not be split because then the interpolated $__range variable is incorrect + // because it is interpolated on the backend with the split timeRange + !isQueryWithRangeVariable(query.expr) + ); } export function runSplitQuery(datasource: LokiDatasource, request: DataQueryRequest) { const queries = request.targets.filter((query) => !query.hide); - const [nonSplittingQueries, normalQueries] = partition(queries, (query) => !querySupporstSplitting(query)); + const [nonSplittingQueries, normalQueries] = partition(queries, (query) => !querySupportsSplitting(query)); const [logQueries, metricQueries] = partition(normalQueries, (query) => isLogsQuery(query.expr)); request.queryGroupId = uuidv4(); @@ -275,7 +281,9 @@ export function runSplitQuery(datasource: LokiDatasource, request: DataQueryRequ return runSplitGroupedQueries(datasource, requests).pipe( tap((response) => { if (response.state === LoadingState.Done) { - trackGroupedQueries(response, requests, request, startTime); + trackGroupedQueries(response, requests, request, startTime, { + predefinedOperations: datasource.predefinedOperations, + }); } }) ); diff --git a/public/app/plugins/datasource/loki/queryUtils.test.ts b/public/app/plugins/datasource/loki/queryUtils.test.ts index c1d2559bb42..5a500457397 100644 --- a/public/app/plugins/datasource/loki/queryUtils.test.ts +++ b/public/app/plugins/datasource/loki/queryUtils.test.ts @@ -10,6 +10,7 @@ import { obfuscate, requestSupportsSplitting, isQueryWithDistinct, + isQueryWithRangeVariable, } from './queryUtils'; import { LokiQuery, LokiQueryType } from './types'; @@ -294,6 +295,28 @@ describe('isQueryWithDistinct', () => { }); }); +describe('isQueryWithRangeVariableDuration', () => { + it('identifies queries using $__range variable', () => { + expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range])')).toBe(true); + }); + + it('identifies queries using $__range_s variable', () => { + expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range_s])')).toBe(true); + }); + + it('identifies queries using $__range_ms variable', () => { + expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range_ms])')).toBe(true); + }); + + it('does not return false positives', () => { + expect(isQueryWithRangeVariable('rate({job="grafana"} | logfmt | value="$__range" [5m])')).toBe(false); + expect(isQueryWithRangeVariable('rate({job="grafana"} | logfmt | value="[$__range]" [5m])')).toBe(false); + expect(isQueryWithRangeVariable('rate({job="grafana"} [$range])')).toBe(false); + expect(isQueryWithRangeVariable('rate({job="grafana"} [$_range])')).toBe(false); + expect(isQueryWithRangeVariable('rate({job="grafana"} [$_range_ms])')).toBe(false); + }); +}); + describe('getParserFromQuery', () => { it('returns no parser', () => { expect(getParserFromQuery('{job="grafana"}')).toBeUndefined(); diff --git a/public/app/plugins/datasource/loki/queryUtils.ts b/public/app/plugins/datasource/loki/queryUtils.ts index 3547891145f..a94a91df256 100644 --- a/public/app/plugins/datasource/loki/queryUtils.ts +++ b/public/app/plugins/datasource/loki/queryUtils.ts @@ -18,6 +18,7 @@ import { Matcher, Identifier, Distinct, + Range, } from '@grafana/lezer-logql'; import { DataQuery } from '@grafana/schema'; @@ -303,6 +304,22 @@ export function isQueryWithDistinct(query: string): boolean { return hasDistinct; } +export function isQueryWithRangeVariable(query: string): boolean { + let hasRangeVariableDuration = false; + const tree = parser.parse(query); + tree.iterate({ + enter: ({ type, from, to }): false | void => { + if (type.id === Range) { + if (query.substring(from, to).match(/\[\$__range(_s|_ms)?/)) { + hasRangeVariableDuration = true; + return false; + } + } + }, + }); + return hasRangeVariableDuration; +} + export function getStreamSelectorsFromQuery(query: string): string[] { const labelMatcherPositions = getStreamSelectorPositions(query); diff --git a/public/app/plugins/datasource/loki/tracking.test.ts b/public/app/plugins/datasource/loki/tracking.test.ts index 34ee5a6efbd..24d530efbdf 100644 --- a/public/app/plugins/datasource/loki/tracking.test.ts +++ b/public/app/plugins/datasource/loki/tracking.test.ts @@ -92,6 +92,35 @@ test('Tracks queries', () => { time_range_from: '2023-02-08T05:00:00.000Z', time_range_to: '2023-02-10T06:00:00.000Z', time_taken: 0, + predefined_operations_applied: 'n/a', + }); +}); + +test('Tracks predefined operations', () => { + trackQuery({ data: [] }, originalRequest, new Date(), { predefinedOperations: 'count_over_time' }); + + expect(reportInteraction).toHaveBeenCalledWith('grafana_loki_query_executed', { + app: 'explore', + bytes_processed: 0, + editor_mode: 'builder', + grafana_version: '1.0', + has_data: false, + has_error: false, + is_split: false, + legend: undefined, + line_limit: undefined, + obfuscated_query: 'count_over_time({Identifier=String}[1m])', + parsed_query: + 'LogQL,Expr,MetricExpr,RangeAggregationExpr,RangeOp,CountOverTime,LogRangeExpr,Selector,Matchers,Matcher,Identifier,Eq,String,Range,Duration', + query_type: 'metric', + query_vector_type: undefined, + resolution: 1, + simultaneously_executed_query_count: 2, + simultaneously_hidden_query_count: 1, + time_range_from: '2023-02-08T05:00:00.000Z', + time_range_to: '2023-02-10T06:00:00.000Z', + time_taken: 0, + predefined_operations_applied: true, }); }); @@ -123,6 +152,7 @@ test('Tracks grouped queries', () => { time_range_from: '2023-02-08T05:00:00.000Z', time_range_to: '2023-02-10T06:00:00.000Z', time_taken: 0, + predefined_operations_applied: 'n/a', }); expect(reportInteraction).toHaveBeenCalledWith('grafana_loki_query_executed', { @@ -149,5 +179,6 @@ test('Tracks grouped queries', () => { time_range_from: '2023-02-08T05:00:00.000Z', time_range_to: '2023-02-10T06:00:00.000Z', time_taken: 0, + predefined_operations_applied: 'n/a', }); }); diff --git a/public/app/plugins/datasource/loki/tracking.ts b/public/app/plugins/datasource/loki/tracking.ts index 7ed19a40ea6..cd6c117e147 100644 --- a/public/app/plugins/datasource/loki/tracking.ts +++ b/public/app/plugins/datasource/loki/tracking.ts @@ -53,6 +53,10 @@ type LokiOnDashboardLoadedTrackingEvent = { queries_with_changed_legend_count: number; }; +export type LokiTrackingSettings = { + predefinedOperations?: string; +}; + export const onDashboardLoadedHandler = ({ payload: { dashboardId, orgId, grafanaVersion, queries }, }: DashboardLoadedEvent) => { @@ -148,6 +152,7 @@ export function trackQuery( response: DataQueryResponse, request: DataQueryRequest, startTime: Date, + trackingSettings: LokiTrackingSettings = {}, extraPayload: Record = {} ): void { // We only want to track usage for these specific apps @@ -184,6 +189,9 @@ export function trackQuery( time_taken: Date.now() - startTime.getTime(), bytes_processed: totalBytes, is_split: false, + predefined_operations_applied: trackingSettings.predefinedOperations + ? query.expr.includes(trackingSettings.predefinedOperations) + : 'n/a', ...extraPayload, }); } @@ -193,7 +201,8 @@ export function trackGroupedQueries( response: DataQueryResponse, groupedRequests: LokiGroupedRequest[], originalRequest: DataQueryRequest, - startTime: Date + startTime: Date, + trackingSettings: LokiTrackingSettings = {} ): void { const splittingPayload = { split_query_group_count: groupedRequests.length, @@ -206,7 +215,7 @@ export function trackGroupedQueries( for (const group of groupedRequests) { const split_query_partition_size = group.partition.length; - trackQuery(response, group.request, startTime, { + trackQuery(response, group.request, startTime, trackingSettings, { ...splittingPayload, split_query_partition_size, }); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.tsx index 15545025545..98689be099d 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/MetricSelect.tsx @@ -49,6 +49,14 @@ export function MetricSelect({ initialMetrics?: string[]; }>({}); + const metricsModalOption: SelectableValue[] = [ + { + value: 'BrowseMetrics', + label: 'Metrics explorer', + description: 'Browse and filter metrics and metadata with a fuzzy search', + }, + ]; + const customFilterOption = useCallback((option: SelectableValue, searchQuery: string) => { const label = option.label ?? option.value; if (!label) { @@ -61,7 +69,16 @@ export function MetricSelect({ } const searchWords = searchQuery.split(splitSeparator); - return searchWords.reduce((acc, cur) => acc && label.toLowerCase().includes(cur.toLowerCase()), true); + return searchWords.reduce((acc, cur) => { + const matcheSearch = label.toLowerCase().includes(cur.toLowerCase()); + + let browseOption = false; + if (prometheusMetricEncyclopedia) { + browseOption = label === 'Metrics explorer'; + } + + return acc && (matcheSearch || browseOption); + }, true); }, []); const formatOptionLabel = useCallback( @@ -70,7 +87,8 @@ export function MetricSelect({ if (option['__isNew__']) { return option.label; } - + // only matches on input, does not match on regex + // look into matching for regex input return ( PROMETHEUS_QUERY_BUILDER_MAX_RESULTS) { results.splice(0, results.length - PROMETHEUS_QUERY_BUILDER_MAX_RESULTS); } - return results.map((result) => { + + const resultsOptions = results.map((result) => { return { label: result.text, value: result.text, }; }); + + if (prometheusMetricEncyclopedia) { + return [...metricsModalOption, ...resultsOptions]; + } else { + return resultsOptions; + } }); }; @@ -201,18 +226,11 @@ export function MetricSelect({ } if (prometheusMetricEncyclopedia) { - // pass the initial metrics, possibly filtered by labels into the Metrics Modal - const metricsModalOption: SelectableValue[] = [ - { - value: 'BrowseMetrics', - label: 'Metrics explorer', - description: 'Browse and filter metrics and metadata with a fuzzy search', - }, - ]; - // pass the initial metrics into the Metrics Modal setState({ + // add the modal butoon option to the options metrics: [...metricsModalOption, ...metrics], isLoading: undefined, + // pass the initial metrics into the Metrics Modal initialMetrics: initialMetrics, }); } else { diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx index 3991277ed7a..b5331ef8d49 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx @@ -14,18 +14,11 @@ type AdditionalSettingsProps = { onChangeIncludeNullMetadata: () => void; onChangeDisableTextWrap: () => void; onChangeUseBackend: () => void; - onChangeInferType: () => void; }; export function AdditionalSettings(props: AdditionalSettingsProps) { - const { - state, - onChangeFullMetaSearch, - onChangeIncludeNullMetadata, - onChangeDisableTextWrap, - onChangeUseBackend, - onChangeInferType, - } = props; + const { state, onChangeFullMetaSearch, onChangeIncludeNullMetadata, onChangeDisableTextWrap, onChangeUseBackend } = + props; const theme = useTheme2(); const styles = getStyles(theme); @@ -63,18 +56,6 @@ export function AdditionalSettings(props: AdditionalSettingsProps) {
-
- onChangeInferType()} /> -
{placeholders.inferType} 
- - - -
); } diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.test.tsx index 94ec3155abc..cc0c82f3e8c 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.test.tsx @@ -159,7 +159,7 @@ describe('MetricsModal', () => { }); }); - it('searches by all metric metadata with a fuzzy search', async () => { + it('searches by name and description with a fuzzy search when setting is turned on', async () => { // search for a_bucket by metadata type counter but only type countt setup(defaultQuery, listOfMetrics); let metricABucket: HTMLElement | null; @@ -179,7 +179,7 @@ describe('MetricsModal', () => { const searchMetric = screen.getByTestId(testIds.searchMetric); expect(searchMetric).toBeInTheDocument(); - await userEvent.type(searchMetric, 'countt'); + await userEvent.type(searchMetric, 'functions'); await waitFor(() => { metricABucket = screen.getByText('a_bucket'); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.tsx index 1e5895f21f0..f7786e3c3bd 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.tsx @@ -69,7 +69,6 @@ const { setSelectedIdx, setDisableTextWrap, showAdditionalSettings, - setInferType, } = stateSlice.actions; export const MetricsModal = (props: MetricsModalProps) => { @@ -83,31 +82,26 @@ export const MetricsModal = (props: MetricsModalProps) => { /** * loads metrics and metadata on opening modal and switching off useBackend */ - const updateMetricsMetadata = useCallback( - async (inferType: boolean) => { - // *** Loading Gif - dispatch(setIsLoading(true)); + const updateMetricsMetadata = useCallback(async () => { + // *** Loading Gif + dispatch(setIsLoading(true)); - const data: MetricsModalMetadata = await setMetrics(datasource, query, inferType, initialMetrics); - - dispatch( - buildMetrics({ - isLoading: false, - hasMetadata: data.hasMetadata, - metrics: data.metrics, - metaHaystackDictionary: data.metaHaystackDictionary, - nameHaystackDictionary: data.nameHaystackDictionary, - totalMetricCount: data.metrics.length, - filteredMetricCount: data.metrics.length, - }) - ); - }, - [query, datasource, initialMetrics] - ); + const data: MetricsModalMetadata = await setMetrics(datasource, query, initialMetrics); + dispatch( + buildMetrics({ + isLoading: false, + hasMetadata: data.hasMetadata, + metrics: data.metrics, + metaHaystackDictionary: data.metaHaystackDictionary, + nameHaystackDictionary: data.nameHaystackDictionary, + totalMetricCount: data.metrics.length, + filteredMetricCount: data.metrics.length, + }) + ); + }, [query, datasource, initialMetrics]); useEffect(() => { - updateMetricsMetadata(state.inferType); - // eslint-disable-next-line react-hooks/exhaustive-deps + updateMetricsMetadata(); }, [updateMetricsMetadata]); const typeOptions: SelectableValue[] = promTypes.map((t: PromFilterOption) => { @@ -123,10 +117,10 @@ export const MetricsModal = (props: MetricsModalProps) => { */ const debouncedBackendSearch = useMemo( () => - debounce(async (metricText: string, inferType: boolean) => { + debounce(async (metricText: string) => { dispatch(setIsLoading(true)); - const metrics = await getBackendSearchMetrics(metricText, query.labels, datasource, inferType); + const metrics = await getBackendSearchMetrics(metricText, query.labels, datasource); dispatch( filterMetricsBackend({ @@ -150,9 +144,9 @@ export const MetricsModal = (props: MetricsModalProps) => { function searchCallback(query: string, fullMetaSearchVal: boolean) { if (state.useBackend && query === '') { // get all metrics data if a user erases everything in the input - updateMetricsMetadata(state.inferType); + updateMetricsMetadata(); } else if (state.useBackend) { - debouncedBackendSearch(query, state.inferType); + debouncedBackendSearch(query); } else { // search either the names or all metadata // fuzzy search go! @@ -200,31 +194,17 @@ export const MetricsModal = (props: MetricsModalProps) => { onChange({ ...query, disableTextWrap: !state.disableTextWrap }); tracking('grafana_prom_metric_encycopedia_disable_text_wrap_interaction', state, ''); }} - onChangeInferType={() => { - const inferType = !state.inferType; - dispatch(setInferType(inferType)); - // update the type - if (state.useBackend) { - // if there is no query yet, it will infer the type on the api call - if (state.fuzzySearchQuery !== '') { - debouncedBackendSearch(state.fuzzySearchQuery, inferType); - } - } else { - // updates the metadata with the inferred type - updateMetricsMetadata(inferType); - } - }} onChangeUseBackend={() => { const newVal = !state.useBackend; dispatch(setUseBackend(newVal)); onChange({ ...query, useBackend: newVal }); if (newVal === false) { // rebuild the metrics metadata if we turn off useBackend - updateMetricsMetadata(state.inferType); + updateMetricsMetadata(); } else { // check if there is text in the browse search and update if (state.fuzzySearchQuery !== '') { - debouncedBackendSearch(state.fuzzySearchQuery, state.inferType); + debouncedBackendSearch(state.fuzzySearchQuery); } // otherwise wait for user typing } @@ -259,9 +239,6 @@ export const MetricsModal = (props: MetricsModalProps) => { }} />
-
- -
{state.hasMetadata && (
{ />
)} +
+ +
{ size="md" onClick={() => dispatch(showAdditionalSettings())} data-testid={testIds.showAdditionalSettings} + className={styles.noBorder} > Additional Settings -
@@ -368,5 +353,4 @@ export const testIds = { resultsPerPage: 'results-per-page', setUseBackend: 'set-use-backend', showAdditionalSettings: 'show-additional-settings', - inferType: 'set-infer-type', }; diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/ResultsTable.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/ResultsTable.tsx index c2cdbfd2187..367059af361 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/ResultsTable.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/ResultsTable.tsx @@ -3,8 +3,9 @@ import React, { ReactElement, useEffect, useRef } from 'react'; import Highlighter from 'react-highlight-words'; import { GrafanaTheme2 } from '@grafana/data'; -import { Icon, Tooltip, useTheme2 } from '@grafana/ui'; +import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui'; +import { docsTip } from '../../../configuration/ConfigEditor'; import { PromVisualQuery } from '../../types'; import { tracking } from './state/helpers'; @@ -51,15 +52,7 @@ export function ResultsTable(props: ResultsTableProps) { if (state.fullMetaSearch && metric) { return ( <> - - {' '} - {inferredType(metric.inferred ?? false)} - + {displayType(metric.type ?? '')} - - {metric.type ?? ''} {inferredType(metric.inferred ?? false)} - + {displayType(metric.type ?? '')} {metric.description ?? ''} ); } } - function inferredType(inferred: boolean): JSX.Element | undefined { - if (inferred) { - return ( - - - - ); - } else { - return undefined; + function addHelpIcon(fullType: string, descriptiveType: string, link: string) { + return ( + <> + {fullType} + + + When creating a {descriptiveType}, Prometheus exposes multiple series with the type counter.{' '} + {docsTip(link)} + + } + placement="bottom-start" + interactive={true} + > + + + + + ); + } + + function displayType(type: string | null) { + if (!type) { + return ''; } + + if (type.includes('(summary)')) { + return addHelpIcon(type, 'summary', 'https://prometheus.io/docs/concepts/metric_types/#summary'); + } + + if (type.includes('(histogram)')) { + return addHelpIcon(type, 'histogram', 'https://prometheus.io/docs/concepts/metric_types/#histogram'); + } + + return type; } function noMetricsMessages(): ReactElement { @@ -105,7 +122,7 @@ export function ResultsTable(props: ResultsTableProps) { message = 'There are no metrics found. Try to expand your label filters.'; } - if (state.fuzzySearchQuery) { + if (state.fuzzySearchQuery || state.selectedTypes.length > 0) { message = 'There are no metrics found. Try to expand your search and filters.'; } @@ -116,6 +133,21 @@ export function ResultsTable(props: ResultsTableProps) { ); } + function textHighlight(state: MetricsModalState) { + if (state.useBackend) { + // highlight the input only for the backend search + // this highlight is equivalent to how the metric select highlights + // look into matching on regex input + return [state.fuzzySearchQuery]; + } else if (state.fullMetaSearch) { + // highlight the matches in the ufuzzy metaHaystack + return state.metaHaystackMatches; + } else { + // highlight the ufuzzy name matches + return state.nameHaystackMatches; + } + } + return ( @@ -124,9 +156,10 @@ export function ResultsTable(props: ResultsTableProps) { {state.hasMetadata && ( <> - + )} + @@ -137,8 +170,6 @@ export function ResultsTable(props: ResultsTableProps) { selectMetric(metric)} - tabIndex={0} onFocus={() => onFocusRow(idx)} onKeyDown={(e) => { if (e.code === 'Enter' && e.currentTarget.classList.contains('selected-row')) { @@ -149,12 +180,22 @@ export function ResultsTable(props: ResultsTableProps) { {state.hasMetadata && metaRows(metric)} + ); })} @@ -186,7 +227,6 @@ const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { `, row: css` label: row; - cursor: pointer; border-bottom: 1px solid ${theme.colors.border.weak} &:last-child { border-bottom: 0; @@ -207,13 +247,19 @@ const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { background-color: ${theme.components.textHighlight.background}; `, nameWidth: css` - ${disableTextWrap ? '' : 'width: 40%;'} + ${disableTextWrap ? '' : 'width: 37.5%;'} `, nameOverflow: css` ${disableTextWrap ? '' : 'overflow-wrap: anywhere;'} `, typeWidth: css` - ${disableTextWrap ? '' : 'width: 16%;'} + ${disableTextWrap ? '' : 'width: 15%;'} + `, + descriptionWidth: css` + ${disableTextWrap ? '' : 'width: 35%;'} + `, + selectButtonWidth: css` + ${disableTextWrap ? '' : 'width: 12.5%;'} `, stickyHeader: css` position: sticky; @@ -224,8 +270,13 @@ const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { text-align: center; color: ${theme.colors.text.secondary}; `, - italicized: css` - font-style: italic; + tooltipSpace: css` + margin-left: 4px; + `, + centerButton: css` + display: block; + margin: auto; + border: none; `, }; }; diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts index ef6b7108795..e384c3083c1 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts @@ -16,7 +16,6 @@ const { setFilteredMetricCount } = stateSlice.actions; export async function setMetrics( datasource: PrometheusDatasource, query: PromVisualQuery, - inferType: boolean, initialMetrics?: string[] ): Promise { // metadata is set in the metric select now @@ -34,9 +33,9 @@ export async function setMetrics( let metricsData: MetricsData | undefined; metricsData = initialMetrics?.map((m: string) => { - const metricData = buildMetricData(m, inferType, datasource); + const metricData = buildMetricData(m, datasource); - const metaDataString = `${m}¦${metricData.type}¦${metricData.description}`; + const metaDataString = `${m}¦${metricData.description}`; nameHaystackDictionaryData[m] = metricData; metaHaystackDictionaryData[metaDataString] = metricData; @@ -56,34 +55,27 @@ export async function setMetrics( } /** - * Builds the metric data object with type, description and inferred flag + * Builds the metric data object with type and description * * @param metric The metric name - * @param inferType state attribute that the infer type setting is on or off * @param datasource The Prometheus datasource for mapping metradata to the metric name * @returns A MetricData object. */ -function buildMetricData(metric: string, inferType: boolean, datasource: PrometheusDatasource): MetricData { +function buildMetricData(metric: string, datasource: PrometheusDatasource): MetricData { let type = getMetadataType(metric, datasource.languageProvider.metricsMetadata!); - let inferredType; - if (!type && inferType) { - type = metricTypeHints(metric); - if (type) { - inferredType = true; - } - } const description = getMetadataHelp(metric, datasource.languageProvider.metricsMetadata!); - if (description?.toLowerCase().includes('histogram') && type !== 'histogram') { - type += ' (histogram)'; - } + ['histogram', 'summary'].forEach((t) => { + if (description?.toLowerCase().includes(t) && type !== t) { + type += ` (${t})`; + } + }); const metricData: MetricData = { value: metric, type: type, description: description, - inferred: inferredType, }; return metricData; @@ -123,22 +115,21 @@ export function filterMetrics(state: MetricsModalState): MetricsData { if (m.type && t.value) { return m.type.includes(t.value); } + + if (!m.type && t.value === 'no type') { + return true; + } + return false; }); - // missing type - const hasNoType = !m.type; - - return matchesSelectedType || (hasNoType && state.includeNullMetadata); + // when a user filters for type, only return metrics with defined types + return matchesSelectedType; }); } if (!state.includeNullMetadata) { filteredMetrics = filteredMetrics.filter((m: MetricData) => { - if (state.inferType && m.inferred) { - return true; - } - return m.type !== undefined && m.description !== undefined; }); } @@ -188,8 +179,7 @@ export const calculateResultsPerPage = (results: number, defaultResults: number, export async function getBackendSearchMetrics( metricText: string, labels: QueryBuilderLabelFilter[], - datasource: PrometheusDatasource, - inferType: boolean + datasource: PrometheusDatasource ): Promise> { const queryString = regexifyLabelValuesQueryString(metricText); @@ -202,24 +192,10 @@ export async function getBackendSearchMetrics( const results = datasource.metricFindQuery(params); return await results.then((results) => { - return results.map((result) => buildMetricData(result.text, inferType, datasource)); + return results.map((result) => buildMetricData(result.text, datasource)); }); } -function metricTypeHints(metric: string): string { - const histogramMetric = metric.match(/^\w+_bucket$|^\w+_bucket{.*}$/); - if (histogramMetric) { - return 'counter (histogram)'; - } - - const counterMatch = metric.match(/\b(\w+_(total|sum|count))\b/); - if (counterMatch) { - return 'counter'; - } - - return ''; -} - export function tracking(event: string, state?: MetricsModalState | null, metric?: string, query?: PromVisualQuery) { switch (event) { case 'grafana_prom_metric_encycopedia_tracking': @@ -230,7 +206,6 @@ export function tracking(event: string, state?: MetricsModalState | null, metric fuzzySearchQuery: state?.fuzzySearchQuery, fullMetaSearch: state?.fullMetaSearch, selectedTypes: state?.selectedTypes, - inferType: state?.inferType, }); case 'grafana_prom_metric_encycopedia_disable_text_wrap_interaction': reportInteraction(event, { @@ -263,13 +238,20 @@ export const promTypes: PromFilterOption[] = [ description: 'A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.', }, + { + value: 'unknown', + description: 'These metrics have been given the type unknown in the metadata.', + }, + { + value: 'no type', + description: 'These metrics have no defined type in the metadata.', + }, ]; export const placeholders = { browse: 'Search metrics by name', - metadataSearchSwitch: 'Include search with type and description', + metadataSearchSwitch: 'Include description in search', type: 'Filter by type', includeNullMetadata: 'Include results with no metadata', setUseBackend: 'Enable regex search', - inferType: 'Infer metric type', }; diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/state.ts b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/state.ts index 7e6749a481a..c56b3d77457 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/state.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/state.ts @@ -84,9 +84,6 @@ export const stateSlice = createSlice({ showAdditionalSettings: (state) => { state.showAdditionalSettings = !state.showAdditionalSettings; }, - setInferType: (state, action: PayloadAction) => { - state.inferType = action.payload; - }, }, }); @@ -117,7 +114,6 @@ export function initialState(query?: PromVisualQuery): MetricsModalState { disableTextWrap: query?.disableTextWrap ?? false, selectedIdx: 0, showAdditionalSettings: false, - inferType: true, }; } @@ -171,8 +167,6 @@ export interface MetricsModalState { selectedIdx: number; /** Display toggle switches for settings */ showAdditionalSettings: boolean; - /** Check metric to match on substrings to infer prometheus type */ - inferType: boolean; } /** diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/styles.ts b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/styles.ts index bd5ad66f45e..e8eb2fe86dd 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/styles.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/styles.ts @@ -17,10 +17,14 @@ export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { display: flex; flex-direction: row; flex-wrap: wrap; - gap: ${theme.spacing(2)}; `, inputItemFirst: css` flex-basis: 40%; + padding-right: 16px; + ${theme.breakpoints.down('md')} { + padding-right: 0px; + padding-bottom: 16px; + } `, inputItem: css` flex-grow: 1; @@ -80,6 +84,9 @@ export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { settingsBtn: css` float: right; `, + noBorder: css` + border: none; + `, resultsPerPageLabel: css` color: ${theme.colors.text.secondary}; opacity: 75%; diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/types.ts b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/types.ts index 2f25a7d49f5..d4be9a48638 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/types.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/types.ts @@ -4,7 +4,6 @@ export type MetricData = { value: string; type?: string | null; description?: string; - inferred?: boolean; }; export type PromFilterOption = { diff --git a/public/app/plugins/datasource/tempo/graphTransform.test.ts b/public/app/plugins/datasource/tempo/graphTransform.test.ts index c5e8f9f049b..a81f776de51 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.test.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.test.ts @@ -1,4 +1,4 @@ -import { DataFrameView, dateTime, createDataFrame } from '@grafana/data'; +import { DataFrameView, dateTime, createDataFrame, FieldType } from '@grafana/data'; import { createGraphFrames, mapPromMetricsToServiceMap } from './graphTransform'; import { bigResponse } from './testResponse'; @@ -59,6 +59,26 @@ describe('createGraphFrames', () => { }); }); +it('assigns correct field type even if values are numbers', async () => { + const range = { + from: dateTime('2000-01-01T00:00:00'), + to: dateTime('2000-01-01T00:01:00'), + }; + const { nodes } = mapPromMetricsToServiceMap([{ data: [serverIsANumber, serverIsANumber] }], { + ...range, + raw: range, + }); + + expect(nodes.fields).toMatchObject([ + { name: 'id', values: ['0', '1'], type: FieldType.string }, + { name: 'title', values: ['0', '1'], type: FieldType.string }, + { name: 'mainstat', values: [NaN, NaN], type: FieldType.number }, + { name: 'secondarystat', values: [10, 20], type: FieldType.number }, + { name: 'arc__success', values: [1, 1], type: FieldType.number }, + { name: 'arc__failed', values: [0, 0], type: FieldType.number }, + ]); +}); + describe('mapPromMetricsToServiceMap', () => { it('transforms prom metrics to service graph', async () => { const range = { @@ -191,3 +211,16 @@ const invalidFailedPromMetric = createDataFrame({ { name: 'Value #traces_service_graph_request_failed_total', values: [20, 40] }, ], }); + +const serverIsANumber = createDataFrame({ + refId: 'traces_service_graph_request_total', + fields: [ + { name: 'Time', values: [1628169788000, 1628169788000] }, + { name: 'client', values: ['0', '1'] }, + { name: 'instance', values: ['127.0.0.1:12345', '127.0.0.1:12345'] }, + { name: 'job', values: ['local_scrape', 'local_scrape'] }, + { name: 'server', values: ['0', '1'] }, + { name: 'tempo_config', values: ['default', 'default'] }, + { name: 'Value #traces_service_graph_request_total', values: [10, 20] }, + ], +}); diff --git a/public/app/plugins/datasource/tempo/graphTransform.ts b/public/app/plugins/datasource/tempo/graphTransform.ts index 292d059876f..eb36c6f8737 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.ts @@ -7,6 +7,7 @@ import { MutableDataFrame, NodeGraphDataFrameFieldNames as Fields, TimeRange, + FieldType, } from '@grafana/data'; import { getNonOverlappingDuration, getStats, makeFrames, makeSpanMap } from '../../../core/utils/tracing'; @@ -188,28 +189,35 @@ function createServiceMapDataFrames() { } const nodes = createDF('Nodes', [ - { name: Fields.id }, - { name: Fields.title, config: { displayName: 'Service name' } }, - { name: Fields.mainStat, config: { unit: 'ms/r', displayName: 'Average response time' } }, + { name: Fields.id, type: FieldType.string }, + { name: Fields.title, type: FieldType.string, config: { displayName: 'Service name' } }, + { name: Fields.mainStat, type: FieldType.number, config: { unit: 'ms/r', displayName: 'Average response time' } }, { name: Fields.secondaryStat, + type: FieldType.number, config: { unit: 'r/sec', displayName: 'Requests per second' }, }, { name: Fields.arc + 'success', + type: FieldType.number, config: { displayName: 'Success', color: { fixedColor: 'green', mode: FieldColorModeId.Fixed } }, }, { name: Fields.arc + 'failed', + type: FieldType.number, config: { displayName: 'Failed', color: { fixedColor: 'red', mode: FieldColorModeId.Fixed } }, }, ]); const edges = createDF('Edges', [ - { name: Fields.id }, - { name: Fields.source }, - { name: Fields.target }, - { name: Fields.mainStat, config: { unit: 'ms/r', displayName: 'Average response time' } }, - { name: Fields.secondaryStat, config: { unit: 'r/sec', displayName: 'Requests per second' } }, + { name: Fields.id, type: FieldType.string }, + { name: Fields.source, type: FieldType.string }, + { name: Fields.target, type: FieldType.string }, + { name: Fields.mainStat, type: FieldType.number, config: { unit: 'ms/r', displayName: 'Average response time' } }, + { + name: Fields.secondaryStat, + type: FieldType.number, + config: { unit: 'r/sec', displayName: 'Requests per second' }, + }, ]); return [nodes, edges]; diff --git a/public/app/plugins/panel/canvas/editor/inline/InlineEdit.tsx b/public/app/plugins/panel/canvas/editor/inline/InlineEdit.tsx index a0b3ecc159a..6f3294c2a10 100644 --- a/public/app/plugins/panel/canvas/editor/inline/InlineEdit.tsx +++ b/public/app/plugins/panel/canvas/editor/inline/InlineEdit.tsx @@ -111,7 +111,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ flex-direction: column; background: ${theme.components.panel.background}; border: 1px solid ${theme.colors.border.strong}; - box-shadow: 5px 5px 20px -5px #000000; + box-shadow: ${theme.shadows.z3}; z-index: 1000; opacity: 1; min-width: 400px; diff --git a/public/app/plugins/panel/geomap/utils/tootltip.ts b/public/app/plugins/panel/geomap/utils/tootltip.ts index dbdd792418c..aa5600dbee3 100644 --- a/public/app/plugins/panel/geomap/utils/tootltip.ts +++ b/public/app/plugins/panel/geomap/utils/tootltip.ts @@ -32,9 +32,12 @@ export const pointerMoveListener = (evt: MapBrowserEvent, panel: Geo if (panel.state.measureMenuActive) { return true; } - if (!panel.map || panel.state.ttipOpen) { + + // Eject out of this function if map is not loaded or valid tooltip is already open + if (!panel.map || (panel.state.ttipOpen && panel.state?.ttip?.layers?.length)) { return false; } + const mouse = evt.originalEvent; const pixel = panel.map.getEventPixel(mouse); const hover = toLonLat(panel.map.getCoordinateFromPixel(pixel)); diff --git a/public/app/plugins/panel/heatmap/fields.ts b/public/app/plugins/panel/heatmap/fields.ts index 07bc18baf8b..a11390b7ef4 100644 --- a/public/app/plugins/panel/heatmap/fields.ts +++ b/public/app/plugins/panel/heatmap/fields.ts @@ -108,7 +108,20 @@ export function prepareHeatmapData( })!, ][0]; } else { - rowsHeatmap = frames[0]; + let frame = frames[0]; + let numberFields = frame.fields.filter((field) => field.type === FieldType.number); + let allNamesNumeric = numberFields.every((field) => !Number.isNaN(parseSampleValue(field.name))); + + if (allNamesNumeric) { + numberFields.sort((a, b) => parseSampleValue(a.name) - parseSampleValue(b.name)); + + rowsHeatmap = { + ...frame, + fields: [frame.fields.find((f) => f.type === FieldType.time)!, ...numberFields], + }; + } else { + rowsHeatmap = frame; + } } } diff --git a/public/app/plugins/panel/logs/LogsPanel.tsx b/public/app/plugins/panel/logs/LogsPanel.tsx index 3aff98323e9..85ab80ae0b5 100644 --- a/public/app/plugins/panel/logs/LogsPanel.tsx +++ b/public/app/plugins/panel/logs/LogsPanel.tsx @@ -1,4 +1,4 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import React, { useCallback, useMemo, useRef, useLayoutEffect, useState } from 'react'; import { @@ -42,7 +42,7 @@ export const LogsPanel = ({ id, }: LogsPanelProps) => { const isAscending = sortOrder === LogsSortOrder.Ascending; - const style = useStyles2(getStyles(title, isAscending)); + const style = useStyles2(getStyles); const [scrollTop, setScrollTop] = useState(0); const logsContainerRef = useRef(null); @@ -95,7 +95,7 @@ export const LogsPanel = ({ } const renderCommonLabels = () => ( -
+
Common labels:
@@ -127,20 +127,21 @@ export const LogsPanel = ({ ); }; -const getStyles = (title: string, isAscending: boolean) => (theme: GrafanaTheme2) => ({ - container: css` - margin-bottom: ${theme.spacing(1.5)}; - //We can remove this hot-fix when we fix panel menu with no title overflowing top of all panels - margin-top: ${theme.spacing(!title ? 2.5 : 0)}; - `, - labelContainer: css` - margin: ${isAscending ? theme.spacing(0.5, 0, 0.5, 0) : theme.spacing(0, 0, 0.5, 0.5)}; - display: flex; - align-items: center; - `, - label: css` - margin-right: ${theme.spacing(0.5)}; - font-size: ${theme.typography.bodySmall.fontSize}; - font-weight: ${theme.typography.fontWeightMedium}; - `, +const getStyles = (theme: GrafanaTheme2) => ({ + container: css({ + marginBottom: theme.spacing(1.5), + }), + labelContainer: css({ + margin: theme.spacing(0, 0, 0.5, 0.5), + display: 'flex', + alignItems: 'center', + }), + labelContainerAscending: css({ + margin: theme.spacing(0.5, 0, 0.5, 0), + }), + label: css({ + marginRight: theme.spacing(0.5), + fontSize: theme.typography.bodySmall.fontSize, + fontWeight: theme.typography.fontWeightMedium, + }), }); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 920c860799c..f789b63014b 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -408,8 +408,8 @@ "dashboard-actions": { "import": "Import", "new": "New", - "new-dashboard": "New Dashboard", - "new-folder": "New Folder" + "new-dashboard": "New dashboard", + "new-folder": "New folder" }, "folder-view": { "go-to-folder": "Go to folder", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index bbfc8c0ab83..f0f3b927701 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -408,8 +408,8 @@ "dashboard-actions": { "import": "Ĩmpőřŧ", "new": "Ńęŵ", - "new-dashboard": "Ńęŵ Đäşĥþőäřđ", - "new-folder": "Ńęŵ Főľđęř" + "new-dashboard": "Ńęŵ đäşĥþőäřđ", + "new-folder": "Ńęŵ ƒőľđęř" }, "folder-view": { "go-to-folder": "Ğő ŧő ƒőľđęř", diff --git a/scripts/drone/events/cron.star b/scripts/drone/events/cron.star index 9b718f79572..d73e1074562 100644 --- a/scripts/drone/events/cron.star +++ b/scripts/drone/events/cron.star @@ -6,7 +6,10 @@ load("scripts/drone/vault.star", "from_secret") load( "scripts/drone/steps/lib.star", "compile_build_cmd", - "publish_image", +) +load( + "scripts/drone/utils/images.star", + "images", ) aquasec_trivy_image = "aquasec/trivy:0.21.0" @@ -17,6 +20,7 @@ def cronjobs(): scan_docker_image_pipeline("main"), scan_docker_image_pipeline("latest-ubuntu"), scan_docker_image_pipeline("main-ubuntu"), + scan_build_test_publish_docker_image_pipeline(), grafana_com_nightly_pipeline(), ] @@ -54,34 +58,77 @@ def scan_docker_image_pipeline(tag): cronName = "nightly", name = "scan-" + docker_image + "-image", steps = [ - scan_docker_image_unkown_low_medium_vulnerabilities_step(docker_image), + scan_docker_image_unknown_low_medium_vulnerabilities_step(docker_image), scan_docker_image_high_critical_vulnerabilities_step(docker_image), slack_job_failed_step("grafana-backend-ops", docker_image), ], ) -def scan_docker_image_unkown_low_medium_vulnerabilities_step(docker_image): - return { - "name": "scan-unkown-low-medium-vulnerabilities", - "image": aquasec_trivy_image, - "commands": [ - "trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM " + docker_image, +def scan_build_test_publish_docker_image_pipeline(): + """Generates a cronjob pipeline for nightly scans of grafana Docker images. + + Returns: + Drone cronjob pipeline. + """ + + return cron_job_pipeline( + cronName = "nightly", + name = "scan-build-test-and-publish-docker-images", + steps = [ + scan_docker_image_unknown_low_medium_vulnerabilities_step("all"), + scan_docker_image_high_critical_vulnerabilities_step("all"), + slack_job_failed_step("grafana-backend-ops", "build-images"), ], + ) + +def scan_docker_image_unknown_low_medium_vulnerabilities_step(docker_image): + """Generates a step for scans of Grafana Docker images. + + Args: + docker_image: determines which image is scanned. + + Returns: + Drone cronjob step . + """ + + cmds = [] + if docker_image == "all": + for key in images: + cmds = cmds + ["trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM " + images[key]] + else: + cmds = ["trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM " + docker_image] + return { + "name": "scan-unknown-low-medium-vulnerabilities", + "image": aquasec_trivy_image, + "commands": cmds, } def scan_docker_image_high_critical_vulnerabilities_step(docker_image): + """Generates a step for scans of Grafana Docker images. + + Args: + docker_image: determines which image is scanned. + + Returns: + Drone cronjob step . + """ + + cmds = [] + if docker_image == "all": + for key in images: + cmds = cmds + ["trivy --exit-code 1 --severity HIGH,CRITICAL " + images[key]] + else: + cmds = ["trivy --exit-code 1 --severity HIGH,CRITICAL " + docker_image] return { "name": "scan-high-critical-vulnerabilities", "image": aquasec_trivy_image, - "commands": [ - "trivy --exit-code 1 --severity HIGH,CRITICAL " + docker_image, - ], + "commands": cmds, } def slack_job_failed_step(channel, image): return { "name": "slack-notify-failure", - "image": "plugins/slack", + "image": images["plugins_slack_image"], "settings": { "webhook": from_secret("slack_webhook_backend"), "channel": channel, @@ -95,7 +142,7 @@ def slack_job_failed_step(channel, image): def post_to_grafana_com_step(): return { "name": "post-to-grafana-com", - "image": publish_image, + "image": images["publish_image"], "environment": { "GRAFANA_COM_API_KEY": from_secret("grafana_api_key"), "GCP_KEY": from_secret("gcp_key"), diff --git a/scripts/drone/events/release.star b/scripts/drone/events/release.star index 1749e8312ff..aae0ed6428b 100644 --- a/scripts/drone/events/release.star +++ b/scripts/drone/events/release.star @@ -9,11 +9,9 @@ load( "build_docker_images_step", "build_frontend_package_step", "build_frontend_step", - "build_image", "build_plugins_step", "build_storybook_step", "clone_enterprise_step", - "cloudsdk_image", "compile_build_cmd", "copy_packages_for_docker_step", "download_grabpl_step", @@ -29,7 +27,6 @@ load( "package_step", "postgres_integration_tests_step", "publish_grafanacom_step", - "publish_image", "publish_images_step", "publish_linux_packages_step", "redis_integration_tests_step", @@ -63,6 +60,10 @@ load( "test_backend_enterprise", ) load("scripts/drone/vault.star", "from_secret", "prerelease_bucket") +load( + "scripts/drone/utils/images.star", + "images", +) ver_mode = "release" release_trigger = { @@ -84,7 +85,7 @@ release_trigger = { def store_npm_packages_step(): return { "name": "store-npm-packages", - "image": build_image, + "image": images["build_image"], "depends_on": [ "compile-build-cmd", "build-frontend-packages", @@ -99,7 +100,7 @@ def store_npm_packages_step(): def retrieve_npm_packages_step(): return { "name": "retrieve-npm-packages", - "image": publish_image, + "image": images["publish_image"], "depends_on": [ "compile-build-cmd", "yarn-install", @@ -115,7 +116,7 @@ def retrieve_npm_packages_step(): def release_npm_packages_step(): return { "name": "release-npm-packages", - "image": build_image, + "image": images["build_image"], "depends_on": [ "compile-build-cmd", "retrieve-npm-packages", @@ -554,7 +555,7 @@ def publish_artifacts_step(mode): security = "--security " return { "name": "publish-artifacts", - "image": publish_image, + "image": images["publish_image"], "environment": { "GCP_KEY": from_secret("gcp_key"), "PRERELEASE_BUCKET": from_secret("prerelease_bucket"), @@ -572,7 +573,7 @@ def publish_artifacts_step(mode): def publish_static_assets_step(): return { "name": "publish-static-assets", - "image": publish_image, + "image": images["publish_image"], "environment": { "GCP_KEY": from_secret("gcp_key"), "PRERELEASE_BUCKET": from_secret("prerelease_bucket"), @@ -587,7 +588,7 @@ def publish_static_assets_step(): def publish_storybook_step(): return { "name": "publish-storybook", - "image": publish_image, + "image": images["publish_image"], "environment": { "GCP_KEY": from_secret("gcp_key"), "PRERELEASE_BUCKET": from_secret("prerelease_bucket"), @@ -817,7 +818,7 @@ def verify_release_pipeline( step = { "name": "gsutil-stat", "depends_on": ["clone"], - "image": cloudsdk_image, + "image": images["cloudsdk_image"], "environment": { "BUCKET": bucket, "GCP_KEY": gcp_key, diff --git a/scripts/drone/pipelines/aws_marketplace.star b/scripts/drone/pipelines/aws_marketplace.star index a9ca6c57ab4..82d40a4fa57 100644 --- a/scripts/drone/pipelines/aws_marketplace.star +++ b/scripts/drone/pipelines/aws_marketplace.star @@ -5,18 +5,21 @@ This module contains steps and pipelines publishing to AWS Marketplace. load( "scripts/drone/steps/lib.star", "compile_build_cmd", - "publish_image", ) load("scripts/drone/vault.star", "from_secret") load( "scripts/drone/utils/utils.star", "pipeline", ) +load( + "scripts/drone/utils/images.star", + "images", +) def publish_aws_marketplace_step(): return { "name": "publish-aws-marketplace", - "image": publish_image, + "image": images["publish_image"], "commands": ["./bin/build publish aws --image grafana/grafana-enterprise --repo grafana-labs/grafanaenterprise --product 422b46fb-bea6-4f27-8bcc-832117bd627e"], "depends_on": ["compile-build-cmd"], "environment": { diff --git a/scripts/drone/pipelines/ci_images.star b/scripts/drone/pipelines/ci_images.star index fa6f458cd2b..734b731c2a8 100644 --- a/scripts/drone/pipelines/ci_images.star +++ b/scripts/drone/pipelines/ci_images.star @@ -2,10 +2,6 @@ This module contains steps and pipelines relating to creating CI Docker images. """ -load( - "scripts/drone/steps/lib.star", - "wix_image", -) load( "scripts/drone/utils/utils.star", "pipeline", @@ -14,6 +10,10 @@ load( "scripts/drone/vault.star", "from_secret", ) +load( + "scripts/drone/utils/images.star", + "images", +) def publish_ci_windows_test_image_pipeline(): trigger = { @@ -28,7 +28,7 @@ def publish_ci_windows_test_image_pipeline(): steps = [ { "name": "clone", - "image": wix_image, + "image": images["wix_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -39,7 +39,7 @@ def publish_ci_windows_test_image_pipeline(): }, { "name": "build-and-publish", - "image": "docker:windowsservercore-1809", + "image": images["windows_server_core_image"], "environment": { "DOCKER_USERNAME": from_secret("docker_username"), "DOCKER_PASSWORD": from_secret("docker_password"), diff --git a/scripts/drone/pipelines/docs.star b/scripts/drone/pipelines/docs.star index 5113fc09de2..d60fcdb6cad 100644 --- a/scripts/drone/pipelines/docs.star +++ b/scripts/drone/pipelines/docs.star @@ -5,11 +5,14 @@ This module returns all the pipelines used in the event of documentation changes load( "scripts/drone/steps/lib.star", "build_docs_website_step", - "build_image", "codespell_step", "identify_runner_step", "yarn_install_step", ) +load( + "scripts/drone/utils/images.star", + "images", +) load( "scripts/drone/utils/utils.star", "pipeline", @@ -46,7 +49,7 @@ def docs_pipelines(ver_mode, trigger): def lint_docs(): return { "name": "lint-docs", - "image": build_image, + "image": images["build_image"], "depends_on": [ "yarn-install", ], diff --git a/scripts/drone/pipelines/github.star b/scripts/drone/pipelines/github.star index d3ccb6b30c9..fc43638c504 100644 --- a/scripts/drone/pipelines/github.star +++ b/scripts/drone/pipelines/github.star @@ -6,18 +6,21 @@ load( "scripts/drone/steps/lib.star", "compile_build_cmd", "fetch_images_step", - "publish_image", ) load("scripts/drone/vault.star", "from_secret") load( "scripts/drone/utils/utils.star", "pipeline", ) +load( + "scripts/drone/utils/images.star", + "images", +) def publish_github_step(): return { "name": "publish-github", - "image": publish_image, + "image": images["publish_image"], "commands": ["./bin/build publish github --repo $${GH_REGISTRY} --create"], "depends_on": ["fetch-images-enterprise2"], "environment": { diff --git a/scripts/drone/pipelines/shellcheck.star b/scripts/drone/pipelines/shellcheck.star index 2a2aa55149e..b8ef57265d7 100644 --- a/scripts/drone/pipelines/shellcheck.star +++ b/scripts/drone/pipelines/shellcheck.star @@ -2,11 +2,15 @@ This module returns a Drone step and pipeline for linting with shellcheck. """ -load("scripts/drone/steps/lib.star", "build_image", "compile_build_cmd") +load("scripts/drone/steps/lib.star", "compile_build_cmd") load( "scripts/drone/utils/utils.star", "pipeline", ) +load( + "scripts/drone/utils/images.star", + "images", +) trigger = { "event": [ @@ -25,7 +29,7 @@ trigger = { def shellcheck_step(): return { "name": "shellcheck", - "image": build_image, + "image": images["build_image"], "depends_on": [ "compile-build-cmd", ], diff --git a/scripts/drone/pipelines/windows.star b/scripts/drone/pipelines/windows.star index f00b0a16fd1..e2ecd7a4f22 100644 --- a/scripts/drone/pipelines/windows.star +++ b/scripts/drone/pipelines/windows.star @@ -10,11 +10,14 @@ load( "scripts/drone/steps/lib.star", "get_windows_steps", "windows_clone_step", - "windows_go_image", "windows_init_enterprise_steps", "windows_test_backend_step", "windows_wire_install_step", ) +load( + "scripts/drone/utils/images.star", + "images", +) def windows_test_backend(trigger, edition, ver_mode): """ Generates a pipeline that runs backend tests on Windows @@ -36,7 +39,7 @@ def windows_test_backend(trigger, edition, ver_mode): else: steps.extend([{ "name": "windows-init", - "image": windows_go_image, + "image": images["windows_go_image"], "depends_on": ["clone"], "commands": [], }]) diff --git a/scripts/drone/services/services.star b/scripts/drone/services/services.star index 93113810157..0140ad0b433 100644 --- a/scripts/drone/services/services.star +++ b/scripts/drone/services/services.star @@ -2,6 +2,11 @@ This module has functions for Drone services to be used in pipelines. """ +load( + "scripts/drone/utils/images.star", + "images", +) + def integration_test_services_volumes(): return [ {"name": "postgres", "temp": {"medium": "memory"}}, @@ -13,7 +18,7 @@ def integration_test_services(): services = [ { "name": "postgres", - "image": "postgres:12.3-alpine", + "image": images["postgres_alpine_image"], "environment": { "POSTGRES_USER": "grafanatest", "POSTGRES_PASSWORD": "grafanatest", @@ -26,7 +31,7 @@ def integration_test_services(): }, { "name": "mysql57", - "image": "mysql:5.7.39", + "image": images["mysql5_image"], "environment": { "MYSQL_ROOT_PASSWORD": "rootpass", "MYSQL_DATABASE": "grafana_tests", @@ -37,7 +42,7 @@ def integration_test_services(): }, { "name": "mysql80", - "image": "mysql:8.0.32", + "image": images["mysql8_image"], "environment": { "MYSQL_ROOT_PASSWORD": "rootpass", "MYSQL_DATABASE": "grafana_tests", @@ -49,12 +54,12 @@ def integration_test_services(): }, { "name": "redis", - "image": "redis:6.2.11-alpine", + "image": images["redis_alpine_image"], "environment": {}, }, { "name": "memcached", - "image": "memcached:1.6.9-alpine", + "image": images["memcached_alpine_image"], "environment": {}, }, ] @@ -64,7 +69,7 @@ def integration_test_services(): def ldap_service(): return { "name": "ldap", - "image": "osixia/openldap:1.4.0", + "image": images["openldap_image"], "environment": { "LDAP_ADMIN_PASSWORD": "grafana", "LDAP_DOMAIN": "grafana.org", diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index ba6a38cbdb5..3962936c21e 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -7,18 +7,12 @@ load( "from_secret", "prerelease_bucket", ) +load( + "scripts/drone/utils/images.star", + "images", +) grabpl_version = "v3.0.38" -cloudsdk_image = "google/cloud-sdk:431.0.0" -build_image = "grafana/build-container:1.7.4" -publish_image = "grafana/grafana-ci-deploy:1.3.3" -deploy_docker_image = "us.gcr.io/kubernetes-dev/drone/plugins/deploy-image" -alpine_image = "alpine:3.17.1" -curl_image = "byrnedo/alpine-curl:0.1.8" -windows_image = "mcr.microsoft.com/windows:1809" -wix_image = "grafana/ci-wix:0.1.1" -go_image = "golang:1.20.4" -windows_go_image = "grafana/grafana-ci-windows-test:0.1.0" trigger_oss = { "repo": [ @@ -29,7 +23,7 @@ trigger_oss = { def slack_step(channel, template, secret): return { "name": "slack", - "image": "plugins/slack", + "image": images["plugins_slack_image"], "settings": { "webhook": from_secret(secret), "channel": channel, @@ -40,7 +34,7 @@ def slack_step(channel, template, secret): def yarn_install_step(): return { "name": "yarn-install", - "image": build_image, + "image": images["build_image"], "commands": [ "yarn install --immutable", ], @@ -50,7 +44,7 @@ def yarn_install_step(): def wire_install_step(): return { "name": "wire-install", - "image": build_image, + "image": images["build_image"], "commands": [ "make gen-go", ], @@ -62,7 +56,7 @@ def wire_install_step(): def windows_wire_install_step(edition): return { "name": "wire-install", - "image": windows_go_image, + "image": images["windows_go_image"], "commands": [ "go install github.com/google/wire/cmd/wire@v0.5.0", "wire gen -tags {} ./pkg/server".format(edition), @@ -76,7 +70,7 @@ def identify_runner_step(platform = "linux"): if platform == "linux": return { "name": "identify-runner", - "image": alpine_image, + "image": images["alpine_image"], "commands": [ "echo $DRONE_RUNNER_NAME", ], @@ -84,7 +78,7 @@ def identify_runner_step(platform = "linux"): else: return { "name": "identify-runner", - "image": windows_image, + "image": images["windows_image"], "commands": [ "echo $env:DRONE_RUNNER_NAME", ], @@ -111,7 +105,7 @@ def clone_enterprise_step(source = "${DRONE_COMMIT}"): """ step = { "name": "clone-enterprise", - "image": build_image, + "image": images["build_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -137,7 +131,7 @@ def clone_enterprise_step_pr(source = "${DRONE_COMMIT}", target = "main", canFai """ step = { "name": "clone-enterprise", - "image": build_image, + "image": images["build_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -183,7 +177,7 @@ def init_enterprise_step(ver_mode): token = "" return { "name": "init-enterprise", - "image": build_image, + "image": images["build_image"], "depends_on": [ "clone-enterprise", "grabpl", @@ -241,7 +235,7 @@ def windows_init_enterprise_steps(ver_mode): download_grabpl_step(platform = "windows"), { "name": "clone", - "image": wix_image, + "image": images["wix_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -249,7 +243,7 @@ def windows_init_enterprise_steps(ver_mode): }, { "name": "windows-init", - "image": wix_image, + "image": images["wix_image"], "commands": init_cmds, "depends_on": ["clone"], "environment": {"GITHUB_TOKEN": from_secret("github_token")}, @@ -262,7 +256,7 @@ def download_grabpl_step(platform = "linux"): if platform == "windows": return { "name": "grabpl", - "image": wix_image, + "image": images["wix_image"], "commands": [ '$$ProgressPreference = "SilentlyContinue"', "Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/{}/windows/grabpl.exe -OutFile grabpl.exe".format( @@ -273,7 +267,7 @@ def download_grabpl_step(platform = "linux"): return { "name": "grabpl", - "image": curl_image, + "image": images["curl_image"], "commands": [ "mkdir -p bin", "curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/{}/grabpl".format( @@ -286,7 +280,7 @@ def download_grabpl_step(platform = "linux"): def lint_drone_step(): return { "name": "lint-drone", - "image": curl_image, + "image": images["curl_image"], "commands": [ "./bin/build verify-drone", ], @@ -298,7 +292,7 @@ def lint_drone_step(): def lint_starlark_step(): return { "name": "lint-starlark", - "image": build_image, + "image": images["build_image"], "commands": [ "./bin/build verify-starlark .", ], @@ -325,7 +319,7 @@ def enterprise_downstream_step(ver_mode): step = { "name": "trigger-enterprise-downstream", - "image": "grafana/drone-downstream", + "image": images["drone_downstream_image"], "settings": { "server": "https://drone.grafana.net", "token": from_secret("drone_token"), @@ -349,7 +343,7 @@ def lint_backend_step(): return { "name": "lint-backend", # TODO: build_image or go_image? - "image": go_image, + "image": images["go_image"], "environment": { # We need CGO because of go-sqlite3 "CGO_ENABLED": "1", @@ -367,7 +361,7 @@ def lint_backend_step(): def validate_modfile_step(): return { "name": "validate-modfile", - "image": go_image, + "image": images["go_image"], "failure": "ignore", "commands": [ "go run scripts/modowners/modowners.go check go.mod", @@ -377,7 +371,7 @@ def validate_modfile_step(): def benchmark_ldap_step(): return { "name": "benchmark-ldap", - "image": build_image, + "image": images["build_image"], "environment": { "LDAP_HOSTNAME": "ldap", }, @@ -390,7 +384,7 @@ def benchmark_ldap_step(): def build_storybook_step(ver_mode): return { "name": "build-storybook", - "image": build_image, + "image": images["build_image"], "depends_on": [ # Best to ensure that this step doesn't mess with what's getting built and packaged "build-frontend", @@ -434,7 +428,7 @@ def store_storybook_step(ver_mode, trigger = None): step = { "name": "store-storybook", - "image": publish_image, + "image": images["publish_image"], "depends_on": [ "build-storybook", ] + @@ -464,7 +458,7 @@ def store_storybook_step(ver_mode, trigger = None): def e2e_tests_artifacts(): return { "name": "e2e-tests-artifacts-upload", - "image": cloudsdk_image, + "image": images["cloudsdk_image"], "depends_on": [ "end-to-end-tests-dashboards-suite", "end-to-end-tests-panels-suite", @@ -526,7 +520,7 @@ def upload_cdn_step(edition, ver_mode, trigger = None): step = { "name": "upload-cdn-assets" + enterprise2_suffix(edition), - "image": publish_image, + "image": images["publish_image"], "depends_on": deps, "environment": { "GCP_KEY": from_secret("gcp_key"), @@ -578,7 +572,7 @@ def build_backend_step(edition, ver_mode, variants = None): return { "name": "build-backend" + enterprise2_suffix(edition), - "image": build_image, + "image": images["build_image"], "depends_on": [ "wire-install", "compile-build-cmd", @@ -613,7 +607,7 @@ def build_frontend_step(edition, ver_mode): return { "name": "build-frontend", - "image": build_image, + "image": images["build_image"], "environment": { "NODE_OPTIONS": "--max_old_space_size=8192", }, @@ -653,7 +647,7 @@ def build_frontend_package_step(edition, ver_mode): return { "name": "build-frontend-packages", - "image": build_image, + "image": images["build_image"], "environment": { "NODE_OPTIONS": "--max_old_space_size=8192", }, @@ -673,7 +667,7 @@ def build_plugins_step(edition, ver_mode): env = None return { "name": "build-plugins", - "image": build_image, + "image": images["build_image"], "environment": env, "depends_on": [ "compile-build-cmd", @@ -685,7 +679,7 @@ def build_plugins_step(edition, ver_mode): ], } -def test_backend_step(image = build_image): +def test_backend_step(image = images["build_image"]): return { "name": "test-backend", "image": image, @@ -698,13 +692,13 @@ def test_backend_step(image = build_image): } def windows_test_backend_step(): - step = test_backend_step(image = windows_go_image) + step = test_backend_step(image = images["windows_go_image"]) return step def test_backend_integration_step(): return { "name": "test-backend-integration", - "image": build_image, + "image": images["build_image"], "depends_on": [ "wire-install", ], @@ -729,7 +723,7 @@ def betterer_frontend_step(edition = "oss"): deps.extend(["yarn-install"]) return { "name": "betterer-frontend", - "image": build_image, + "image": images["build_image"], "depends_on": deps, "commands": [ "yarn betterer ci", @@ -752,7 +746,7 @@ def test_frontend_step(edition = "oss"): deps.extend(["yarn-install"]) return { "name": "test-frontend", - "image": build_image, + "image": images["build_image"], "environment": { "TEST_MAX_WORKERS": "50%", }, @@ -765,7 +759,7 @@ def test_frontend_step(edition = "oss"): def lint_frontend_step(): return { "name": "lint-frontend", - "image": build_image, + "image": images["build_image"], "environment": { "TEST_MAX_WORKERS": "50%", }, @@ -784,7 +778,7 @@ def verify_i18n_step(): uncommited_error_message = "\nTranslation extraction has not been committed. Please run 'yarn i18n:extract', commit the changes and push again." return { "name": "verify-i18n", - "image": build_image, + "image": images["build_image"], "depends_on": [ "yarn-install", ], @@ -836,7 +830,7 @@ def test_a11y_frontend_step(ver_mode, port = 3001): return { "name": "test-a11y-frontend", # TODO which image should be used? - "image": "grafana/docker-puppeteer:1.1.0", + "image": images["docker_puppeteer_image"], "depends_on": [ "grafana-server", ], @@ -861,7 +855,7 @@ def frontend_metrics_step(trigger = None): """ step = { "name": "publish-frontend-metrics", - "image": build_image, + "image": images["build_image"], "depends_on": [ "test-a11y-frontend", ], @@ -880,7 +874,7 @@ def frontend_metrics_step(trigger = None): def codespell_step(): return { "name": "codespell", - "image": build_image, + "image": images["build_image"], "commands": [ # Important: all words have to be in lowercase, and separated by "\n". 'echo -e "unknwon\nreferer\nerrorstring\neror\niam\nwan" > words_to_ignore.txt', @@ -939,7 +933,7 @@ def package_step(edition, ver_mode): return { "name": "package" + enterprise2_suffix(edition), - "image": build_image, + "image": images["build_image"], "depends_on": deps, "environment": env, "commands": cmds, @@ -962,7 +956,7 @@ def grafana_server_step(edition, port = 3001): return { "name": "grafana-server", - "image": build_image, + "image": images["build_image"], "detach": True, "depends_on": [ "build-plugins", @@ -982,7 +976,7 @@ def e2e_tests_step(suite, port = 3001, tries = None): cmd += " --tries {}".format(tries) return { "name": "end-to-end-tests-{}".format(suite), - "image": "cypress/included:9.5.1-node16.14.0-slim-chrome99-ff97", + "image": images["cypress_image"], "depends_on": [ "grafana-server", ], @@ -1034,7 +1028,7 @@ def cloud_plugins_e2e_tests_step(suite, cloud, trigger = None): branch = "${DRONE_SOURCE_BRANCH}".replace("/", "-") step = { "name": "end-to-end-tests-{}-{}".format(suite, cloud), - "image": "us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e:latest", + "image": images["cloud_datasources_e2e_image"], "depends_on": [ "grafana-server", ], @@ -1048,7 +1042,7 @@ def build_docs_website_step(): return { "name": "build-docs-website", # Use latest revision here, since we want to catch if it breaks - "image": "grafana/docs-base:latest", + "image": images["docs_image"], "commands": [ "mkdir -p /hugo/content/docs/grafana/latest", "cp -r docs/sources/* /hugo/content/docs/grafana/latest/", @@ -1059,7 +1053,7 @@ def build_docs_website_step(): def copy_packages_for_docker_step(edition = None): return { "name": "copy-packages-for-docker", - "image": build_image, + "image": images["build_image"], "depends_on": [ "package" + enterprise2_suffix(edition), ], @@ -1107,7 +1101,7 @@ def build_docker_images_step(edition, archs = None, ubuntu = False, publish = Fa return { "name": "build-docker-images" + ubuntu_sfx, - "image": cloudsdk_image, + "image": images["cloudsdk_image"], "depends_on": [ "copy-packages-for-docker", "compile-build-cmd", @@ -1120,7 +1114,7 @@ def build_docker_images_step(edition, archs = None, ubuntu = False, publish = Fa def fetch_images_step(edition): return { "name": "fetch-images-{}".format(edition), - "image": cloudsdk_image, + "image": images["cloudsdk_image"], "environment": { "GCP_KEY": from_secret("gcp_key"), "DOCKER_USER": from_secret("docker_username"), @@ -1199,7 +1193,7 @@ def publish_images_step(edition, ver_mode, mode, docker_repo, trigger = None): step = { "name": "publish-images-{}".format(name), - "image": cloudsdk_image, + "image": images["cloudsdk_image"], "environment": environment, "commands": [cmd], "depends_on": deps, @@ -1224,7 +1218,7 @@ def postgres_integration_tests_step(): ] return { "name": "postgres-integration-tests", - "image": build_image, + "image": images["build_image"], "depends_on": ["wire-install"], "environment": { "PGPASSWORD": "grafanatest", @@ -1245,7 +1239,7 @@ def mysql_integration_tests_step(hostname, version): ] return { "name": "mysql-{}-integration-tests".format(version), - "image": build_image, + "image": images["build_image"], "depends_on": ["wire-install"], "environment": { "GRAFANA_TEST_DB": "mysql", @@ -1257,7 +1251,7 @@ def mysql_integration_tests_step(hostname, version): def redis_integration_tests_step(): return { "name": "redis-integration-tests", - "image": build_image, + "image": images["build_image"], "depends_on": ["wire-install"], "environment": { "REDIS_URL": "redis://redis:6379/0", @@ -1272,7 +1266,7 @@ def redis_integration_tests_step(): def memcached_integration_tests_step(): return { "name": "memcached-integration-tests", - "image": build_image, + "image": images["build_image"], "depends_on": ["wire-install"], "environment": { "MEMCACHED_HOSTS": "memcached:11211", @@ -1296,7 +1290,7 @@ def release_canary_npm_packages_step(trigger = None): """ step = { "name": "release-canary-npm-packages", - "image": build_image, + "image": images["build_image"], "depends_on": end_to_end_tests_deps(), "environment": { "NPM_TOKEN": from_secret("npm_token"), @@ -1339,7 +1333,7 @@ def upload_packages_step(edition, ver_mode, trigger = None): """ step = { "name": "upload-packages" + enterprise2_suffix(edition), - "image": publish_image, + "image": images["publish_image"], "depends_on": end_to_end_tests_deps(), "environment": { "GCP_KEY": from_secret("gcp_key"), @@ -1381,7 +1375,7 @@ def publish_grafanacom_step(edition, ver_mode): return { "name": "publish-grafanacom-{}".format(edition), - "image": publish_image, + "image": images["publish_image"], "depends_on": [ "publish-linux-packages-deb", "publish-linux-packages-rpm", @@ -1399,7 +1393,7 @@ def publish_linux_packages_step(edition, package_manager = "deb"): return { "name": "publish-linux-packages-{}".format(package_manager), # See https://github.com/grafana/deployment_tools/blob/master/docker/package-publish/README.md for docs on that image - "image": "us.gcr.io/kubernetes-dev/package-publish:latest", + "image": images["package_publish_image"], "depends_on": ["compile-build-cmd"], "privileged": True, "settings": { @@ -1421,7 +1415,7 @@ def publish_linux_packages_step(edition, package_manager = "deb"): def windows_clone_step(): return { "name": "clone", - "image": wix_image, + "image": images["wix_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -1481,7 +1475,7 @@ def get_windows_steps(edition, ver_mode): [ { "name": "clone", - "image": wix_image, + "image": images["wix_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token"), }, @@ -1489,7 +1483,7 @@ def get_windows_steps(edition, ver_mode): }, { "name": "windows-init", - "image": wix_image, + "image": images["wix_image"], "commands": init_cmds, "depends_on": ["clone"], "environment": {"GITHUB_TOKEN": from_secret("github_token")}, @@ -1508,7 +1502,7 @@ def get_windows_steps(edition, ver_mode): [ { "name": "windows-init", - "image": wix_image, + "image": images["wix_image"], "commands": init_cmds, }, ], @@ -1583,7 +1577,7 @@ def get_windows_steps(edition, ver_mode): steps.append( { "name": "build-windows-installer", - "image": wix_image, + "image": images["wix_image"], "depends_on": [ "windows-init", ], @@ -1601,7 +1595,7 @@ def get_windows_steps(edition, ver_mode): def verify_gen_cue_step(): return { "name": "verify-gen-cue", - "image": build_image, + "image": images["build_image"], "depends_on": [], "commands": [ "# It is required that code generated from Thema/CUE be committed and in sync with its inputs.", @@ -1613,7 +1607,7 @@ def verify_gen_cue_step(): def verify_gen_jsonnet_step(): return { "name": "verify-gen-jsonnet", - "image": build_image, + "image": images["build_image"], "depends_on": [], "commands": [ "# It is required that generated jsonnet is committed and in sync with its inputs.", @@ -1625,7 +1619,7 @@ def verify_gen_jsonnet_step(): def trigger_test_release(): return { "name": "trigger-test-release", - "image": build_image, + "image": images["build_image"], "environment": { "GITHUB_TOKEN": from_secret("github_token_pr"), "TEST_TAG": "v0.0.0-test", @@ -1659,7 +1653,7 @@ def trigger_test_release(): def artifacts_page_step(): return { "name": "artifacts-page", - "image": build_image, + "image": images["build_image"], "depends_on": [ "compile-build-cmd", ], @@ -1687,7 +1681,7 @@ def compile_build_cmd(edition = "oss"): ] return { "name": "compile-build-cmd", - "image": go_image, + "image": images["go_image"], "commands": [ "go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd", ], diff --git a/scripts/drone/utils/images.star b/scripts/drone/utils/images.star new file mode 100644 index 00000000000..0450301c1c2 --- /dev/null +++ b/scripts/drone/utils/images.star @@ -0,0 +1,29 @@ +""" +This module contains all the docker images that are used to build test and publish Grafana. +""" + +images = { + "cloudsdk_image": "google/cloud-sdk:431.0.0", + "build_image": "grafana/build-container:1.7.4", + "publish_image": "grafana/grafana-ci-deploy:1.3.3", + "alpine_image": "alpine:3.17.1", + "curl_image": "byrnedo/alpine-curl:0.1.8", + "windows_image": "mcr.microsoft.com/windows:1809", + "wix_image": "grafana/ci-wix:0.1.1", + "go_image": "golang:1.20.4", + "windows_go_image": "grafana/grafana-ci-windows-test:0.1.0", + "plugins_slack_image": "plugins/slack", + "postgres_alpine_image": "postgres:12.3-alpine", + "mysql5_image": "mysql:5.7.39", + "mysql8_image": "mysql:8.0.32", + "redis_alpine_image": "redis:6.2.11-alpine", + "memcached_alpine_image": "memcached:1.6.9-alpine", + "windows_server_core_image": "docker:windowsservercore-1809", + "package_publish_image": "us.gcr.io/kubernetes-dev/package-publish:latest", + "openldap_image": "osixia/openldap:1.4.0", + "drone_downstream_image": "grafana/drone-downstream", + "docker_puppeteer_image": "grafana/docker-puppeteer:1.1.0", + "docs_image": "grafana/docs-base:latest", + "cypress_image": "cypress/included:9.5.1-node16.14.0-slim-chrome99-ff97", + "cloud_datasources_e2e_image": "us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e:latest", +} diff --git a/yarn.lock b/yarn.lock index 3b2910090a4..f0a0f739f17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,6 +42,15 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/code-frame@npm:7.22.5" + dependencies: + "@babel/highlight": ^7.22.5 + checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.4, @babel/compat-data@npm:^7.22.0, @babel/compat-data@npm:^7.22.3": version: 7.22.3 resolution: "@babel/compat-data@npm:7.22.3" @@ -119,6 +128,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/generator@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: efa64da70ca88fe69f05520cf5feed6eba6d30a85d32237671488cc355fdc379fe2c3246382a861d49574c4c2f82a317584f8811e95eb024e365faff3232b49d + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" @@ -128,6 +149,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 53da330f1835c46f26b7bf4da31f7a496dee9fd8696cca12366b94ba19d97421ce519a74a837f687749318f94d1a37f8d1abcbf35e8ed22c32d16373b2f6198d + languageName: node + linkType: hard + "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": version: 7.18.9 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.9" @@ -172,6 +202,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: f1e91deae06dbee6dd956c0346bca600adfbc7955427795d9d8825f0439a3c3290c789ba2b4a02a1cdf6c1a1bd163dfa16d3d5e96b02a8efb639d2a774e88ed9 + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.1": version: 7.22.1 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.1" @@ -208,6 +257,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-environment-visitor@npm:7.22.5" + checksum: 248532077d732a34cd0844eb7b078ff917c3a8ec81a7f133593f71a860a582f05b60f818dc5049c2212e5baa12289c27889a4b81d56ef409b4863db49646c4b1 + languageName: node + linkType: hard + "@babel/helper-explode-assignable-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" @@ -227,6 +283,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-function-name@npm:7.22.5" + dependencies: + "@babel/template": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: 6b1f6ce1b1f4e513bf2c8385a557ea0dd7fa37971b9002ad19268ca4384bbe90c09681fe4c076013f33deabc63a53b341ed91e792de741b4b35e01c00238177a + languageName: node + linkType: hard + "@babel/helper-hoist-variables@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-hoist-variables@npm:7.18.6" @@ -236,6 +302,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-hoist-variables@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-hoist-variables@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 394ca191b4ac908a76e7c50ab52102669efe3a1c277033e49467913c7ed6f7c64d7eacbeabf3bed39ea1f41731e22993f763b1edce0f74ff8563fd1f380d92cc + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.0.0, @babel/helper-member-expression-to-functions@npm:^7.22.0": version: 7.22.3 resolution: "@babel/helper-member-expression-to-functions@npm:7.22.3" @@ -245,6 +320,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 4bd5791529c280c00743e8bdc669ef0d4cd1620d6e3d35e0d42b862f8262bc2364973e5968007f960780344c539a4b9cf92ab41f5b4f94560a9620f536de2a39 + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-module-imports@npm:7.18.6" @@ -263,6 +347,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-module-imports@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2, @babel/helper-module-transforms@npm:^7.21.5, @babel/helper-module-transforms@npm:^7.22.1": version: 7.22.1 resolution: "@babel/helper-module-transforms@npm:7.22.1" @@ -279,6 +372,22 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-module-transforms@npm:7.22.5" + dependencies: + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: 8985dc0d971fd17c467e8b84fe0f50f3dd8610e33b6c86e5b3ca8e8859f9448bcc5c84e08a2a14285ef388351c0484797081c8f05a03770bf44fc27bf4900e68 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.0.0, @babel/helper-optimise-call-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" @@ -288,6 +397,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: c70ef6cc6b6ed32eeeec4482127e8be5451d0e5282d5495d5d569d39eb04d7f1d66ec99b327f45d1d5842a9ad8c22d48567e93fc502003a47de78d122e355f7c + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.20.2 resolution: "@babel/helper-plugin-utils@npm:7.20.2" @@ -302,6 +420,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-plugin-utils@npm:7.22.5" + checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" @@ -330,6 +455,20 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-replace-supers@npm:7.22.5" + dependencies: + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: af29deff6c6dc3fa2d1a517390716aa3f4d329855e8689f1d5c3cb07c1b898e614a5e175f1826bb58e9ff1480e6552885a71a9a0ba5161787aaafa2c79b216cc + languageName: node + linkType: hard + "@babel/helper-simple-access@npm:^7.20.2": version: 7.20.2 resolution: "@babel/helper-simple-access@npm:7.20.2" @@ -348,6 +487,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-simple-access@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-simple-access@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: fe9686714caf7d70aedb46c3cce090f8b915b206e09225f1e4dbc416786c2fdbbee40b38b23c268b7ccef749dd2db35f255338fb4f2444429874d900dede5ad2 + languageName: node + linkType: hard + "@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": version: 7.20.0 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" @@ -357,6 +505,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 1012ef2295eb12dc073f2b9edf3425661e9b8432a3387e62a8bc27c42963f1f216ab3124228015c748770b2257b4f1fda882ca8fa34c0bf485e929ae5bc45244 + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-split-export-declaration@npm:7.18.6" @@ -366,6 +523,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-split-export-declaration@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: d10e05a02f49c1f7c578cea63d2ac55356501bbf58856d97ac9bfde4957faee21ae97c7f566aa309e38a256eef58b58e5b670a7f568b362c00e93dfffe072650 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.19.4": version: 7.19.4 resolution: "@babel/helper-string-parser@npm:7.19.4" @@ -380,6 +546,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-string-parser@npm:7.22.5" + checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": version: 7.19.1 resolution: "@babel/helper-validator-identifier@npm:7.19.1" @@ -387,6 +560,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-identifier@npm:7.22.5" + checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helper-validator-option@npm:7.21.0" @@ -394,6 +574,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-option@npm:7.22.5" + checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.18.9": version: 7.20.5 resolution: "@babel/helper-wrap-function@npm:7.20.5" @@ -428,6 +615,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/highlight@npm:7.22.5" + dependencies: + "@babel/helper-validator-identifier": ^7.22.5 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.9, @babel/parser@npm:^7.22.0, @babel/parser@npm:^7.22.4": version: 7.22.4 resolution: "@babel/parser@npm:7.22.4" @@ -446,6 +644,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/parser@npm:7.22.5" + bin: + parser: ./bin/babel-parser.js + checksum: 470ebba516417ce8683b36e2eddd56dcfecb32c54b9bb507e28eb76b30d1c3e618fd0cfeee1f64d8357c2254514e1a19e32885cfb4e73149f4ae875436a6d59c + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" @@ -802,6 +1009,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -901,6 +1119,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a + languageName: node + linkType: hard + "@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" @@ -1253,6 +1482,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" + dependencies: + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2067aca8f6454d54ffcce69b02c457cfa61428e11372f6a1d99ff4fcfbb55c396ed2ca6ca886bf06c852e38c1a205b8095921b2364fd0243f3e66bc1dda61caa + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.20.11, @babel/plugin-transform-modules-systemjs@npm:^7.22.3": version: 7.22.3 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.3" @@ -1607,6 +1849,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-typescript@npm:7.22.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-typescript": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d12f1ca1ef1f2a54432eb044d2999705d1205ebe211c2a7f05b12e8eb2d2a461fd7657b5486b2f2f1efe7c0c0dc8e80725b767073d40fe4ae059a7af057b05e4 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.18.10": version: 7.18.10 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.18.10" @@ -1894,18 +2150,18 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:7.21.5": - version: 7.21.5 - resolution: "@babel/preset-typescript@npm:7.21.5" +"@babel/preset-typescript@npm:7.22.5": + version: 7.22.5 + resolution: "@babel/preset-typescript@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-syntax-jsx": ^7.21.4 - "@babel/plugin-transform-modules-commonjs": ^7.21.5 - "@babel/plugin-transform-typescript": ^7.21.3 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.5 + "@babel/plugin-syntax-jsx": ^7.22.5 + "@babel/plugin-transform-modules-commonjs": ^7.22.5 + "@babel/plugin-transform-typescript": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e7b35c435139eec1d6bd9f57e8f3eb79bfc2da2c57a34ad9e9ea848ba4ecd72791cf4102df456604ab07c7f4518525b0764754b6dd5898036608b351e0792448 + checksum: 7be1670cb4404797d3a473bd72d66eb2b3e0f2f8a672a5e40bdb0812cc66085ec84bcd7b896709764cabf042fdc6b7f2d4755ac7cce10515eb596ff61dab5154 languageName: node linkType: hard @@ -1965,6 +2221,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.21.0": + version: 7.22.5 + resolution: "@babel/runtime@npm:7.22.5" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 12a50b7de2531beef38840d17af50c55a094253697600cee255311222390c68eed704829308d4fd305e1b3dfbce113272e428e9d9d45b1730e0fede997eaceb1 + languageName: node + linkType: hard + "@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.21.9, @babel/template@npm:^7.3.3": version: 7.21.9 resolution: "@babel/template@npm:7.21.9" @@ -1976,6 +2241,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/template@npm:7.22.5" + dependencies: + "@babel/code-frame": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: c5746410164039aca61829cdb42e9a55410f43cace6f51ca443313f3d0bdfa9a5a330d0b0df73dc17ef885c72104234ae05efede37c1cc8a72dc9f93425977a3 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.21.4, @babel/traverse@npm:~7.21.2": version: 7.21.4 resolution: "@babel/traverse@npm:7.21.4" @@ -2012,6 +2288,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/traverse@npm:7.22.5" + dependencies: + "@babel/code-frame": ^7.22.5 + "@babel/generator": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/types": ^7.22.5 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 560931422dc1761f2df723778dcb4e51ce0d02e560cf2caa49822921578f49189a5a7d053b78a32dca33e59be886a6b2200a6e24d4ae9b5086ca0ba803815694 + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.5, @babel/types@npm:^7.22.0, @babel/types@npm:^7.22.3, @babel/types@npm:^7.22.4, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.22.4 resolution: "@babel/types@npm:7.22.4" @@ -2034,6 +2328,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/types@npm:7.22.5" + dependencies: + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 + to-fast-properties: ^2.0.0 + checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 + languageName: node + linkType: hard + "@base2/pretty-print-object@npm:1.0.1": version: 1.0.1 resolution: "@base2/pretty-print-object@npm:1.0.1" @@ -3268,7 +3573,7 @@ __metadata: "@types/testing-library__jest-dom": 5.14.5 "@types/tinycolor2": 1.4.3 d3-interpolate: 3.0.1 - date-fns: 2.29.3 + date-fns: 2.30.0 dompurify: ^2.4.3 esbuild: 0.17.19 eventemitter3: 5.0.0 @@ -3463,25 +3768,26 @@ __metadata: languageName: node linkType: hard -"@grafana/faro-core@npm:1.0.2, @grafana/faro-core@npm:^1.0.2": - version: 1.0.2 - resolution: "@grafana/faro-core@npm:1.0.2" +"@grafana/faro-core@npm:1.1.0, @grafana/faro-core@npm:^1.1.0": + version: 1.1.0 + resolution: "@grafana/faro-core@npm:1.1.0" dependencies: - "@opentelemetry/api": ^1.4.0 + "@opentelemetry/api": ^1.4.1 "@opentelemetry/api-metrics": ^0.33.0 - "@opentelemetry/otlp-transformer": ^0.35.0 - checksum: 09c3c6687b955aacd17de73dca319dbb2abda8dd26f5c038618266ffefbc1f915cfe12167cd4b49db5c639ab70b7141bb9794c3cbdc682bb63174f95df9d9dfd + "@opentelemetry/otlp-transformer": ^0.37.0 + murmurhash-js: ^1.0.0 + checksum: 1e8504f3cab4ead385347002e265e6e7b32900704fa10dfe19ee72fe5bc50c9acb2fc6fc46f8e7d8efdc796fac9d06ba6d1b5a6512b47f09928c52df2a5fc3dd languageName: node linkType: hard -"@grafana/faro-web-sdk@npm:1.0.2": - version: 1.0.2 - resolution: "@grafana/faro-web-sdk@npm:1.0.2" +"@grafana/faro-web-sdk@npm:1.1.0": + version: 1.1.0 + resolution: "@grafana/faro-web-sdk@npm:1.1.0" dependencies: - "@grafana/faro-core": ^1.0.2 + "@grafana/faro-core": ^1.1.0 ua-parser-js: ^1.0.32 web-vitals: ^3.1.1 - checksum: ca04add8469778d73e1bc15f057d063d93a88b9d76325c79060848ad3211fa929e30b6a85365981f7bcef023704f9f92f67678311bfed13dd521399c0f00e92f + checksum: e4fad2ff3c7d2d3cbc9e485e05a078a4ec0bc216907ed05361f91071611b7ce4256ba9cc8bbf6b23068efd02d32dfd42cfcd5d8a2b868c8ba006db27cd71c2a7 languageName: node linkType: hard @@ -3521,7 +3827,7 @@ __metadata: dependencies: "@grafana/data": 10.1.0-pre "@grafana/e2e-selectors": 10.1.0-pre - "@grafana/faro-web-sdk": 1.0.2 + "@grafana/faro-web-sdk": 1.1.0 "@grafana/tsconfig": ^1.2.0-rc1 "@grafana/ui": 10.1.0-pre "@rollup/plugin-commonjs": 23.0.2 @@ -3558,9 +3864,9 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes@npm:^0.14.0": - version: 0.14.0 - resolution: "@grafana/scenes@npm:0.14.0" +"@grafana/scenes@npm:^0.15.0": + version: 0.15.0 + resolution: "@grafana/scenes@npm:0.15.0" dependencies: "@grafana/e2e-selectors": canary "@grafana/experimental": 1.0.1 @@ -3568,7 +3874,7 @@ __metadata: react-use: 17.4.0 react-virtualized-auto-sizer: 1.0.7 uuid: ^9.0.0 - checksum: a33193fe4b7d18d934d7885ad5e2da0051bde64af865ef593aa2f182ea2190f3f16d163da5e7b2e9ac848187323124308be9f2cea61ccd246e287738113c1107 + checksum: 2d30a1a1af4cc198c2a42fefa196e8e29ce16e5185ecc6fee30bc5ec890465822c6c17f12a04881d1bb95c6c1c374732264ae013472cf40e44525674c9a5f8a8 languageName: node linkType: hard @@ -3581,6 +3887,7 @@ __metadata: "@rollup/plugin-json": 5.0.1 "@rollup/plugin-node-resolve": 15.1.0 esbuild: 0.17.19 + glob: ^10.2.7 rimraf: 4.4.0 rollup: 2.79.1 rollup-plugin-dts: ^5.0.0 @@ -3623,7 +3930,7 @@ __metadata: "@emotion/react": 11.10.6 "@grafana/data": 10.1.0-pre "@grafana/e2e-selectors": 10.1.0-pre - "@grafana/faro-web-sdk": 1.0.2 + "@grafana/faro-web-sdk": 1.1.0 "@grafana/schema": 10.1.0-pre "@grafana/tsconfig": ^1.2.0-rc1 "@leeoniya/ufuzzy": 1.0.6 @@ -3688,11 +3995,11 @@ __metadata: calculate-size: 1.1.1 classnames: 2.3.2 common-tags: 1.8.2 - core-js: 3.30.2 - css-loader: 6.7.3 + core-js: 3.31.0 + css-loader: 6.8.1 csstype: 3.1.1 d3: 7.8.2 - date-fns: 2.29.3 + date-fns: 2.30.0 esbuild: 0.17.19 expose-loader: 4.0.0 hoist-non-react-statics: 3.3.2 @@ -3870,6 +4177,20 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + "@isaacs/string-locale-compare@npm:^1.1.0": version: 1.1.0 resolution: "@isaacs/string-locale-compare@npm:1.1.0" @@ -5801,7 +6122,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:1.4.0, @opentelemetry/api@npm:^1.4.0": +"@opentelemetry/api@npm:1.4.0": version: 1.4.0 resolution: "@opentelemetry/api@npm:1.4.0" checksum: 8dc522194e20d2e8aa6cac155dbce19d3fc9cfac59e953ece1064158c6348ccd9560ee99d2f2381e82c2f8c9a129b57fa7b640027383315504de1fa712b6d7f1 @@ -5815,6 +6136,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/api@npm:^1.4.1": + version: 1.4.1 + resolution: "@opentelemetry/api@npm:1.4.1" + checksum: e783c40d1a518abf9c4c5d65223237c1392cd9a6c53ac6e2c3ef0c05ff7266e3dfc4fd9874316dae0dcb7a97950878deb513bcbadfaad653d48f0215f2a0911b + languageName: node + linkType: hard + "@opentelemetry/core@npm:0.25.0": version: 0.25.0 resolution: "@opentelemetry/core@npm:0.25.0" @@ -5827,14 +6155,14 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.9.1": - version: 1.9.1 - resolution: "@opentelemetry/core@npm:1.9.1" +"@opentelemetry/core@npm:1.11.0": + version: 1.11.0 + resolution: "@opentelemetry/core@npm:1.11.0" dependencies: - "@opentelemetry/semantic-conventions": 1.9.1 + "@opentelemetry/semantic-conventions": 1.11.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.5.0" - checksum: 5581a809e2caff142136734634f45255ce9f1ed701cf38629b9e17d91a8d15449b467fb3a7f3d0d8b076f653090e50cc31d3b1db4cfefeda9b6b901c60581024 + checksum: 866e4a85dde90d228e1822fc7238953d7353ddc1528c2936253a1c8dd47b260fe45551d9172cf97b6e34d851ee5204b391ebc66ab244e1994ac6d816e790c78e languageName: node linkType: hard @@ -5853,17 +6181,17 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:^0.35.0": - version: 0.35.1 - resolution: "@opentelemetry/otlp-transformer@npm:0.35.1" +"@opentelemetry/otlp-transformer@npm:^0.37.0": + version: 0.37.0 + resolution: "@opentelemetry/otlp-transformer@npm:0.37.0" dependencies: - "@opentelemetry/core": 1.9.1 - "@opentelemetry/resources": 1.9.1 - "@opentelemetry/sdk-metrics": 1.9.1 - "@opentelemetry/sdk-trace-base": 1.9.1 + "@opentelemetry/core": 1.11.0 + "@opentelemetry/resources": 1.11.0 + "@opentelemetry/sdk-metrics": 1.11.0 + "@opentelemetry/sdk-trace-base": 1.11.0 peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.5.0" - checksum: e0a68b2be28d5535aaaa58be31a5e85b268b42025c8b3a34498f00b019fbd172530b1b484c4257fd330c5890007130eedec288b8fe8f2b85176198f9ffc2507e + checksum: 353a0f29d27a6b969d1458de7d829da06866bce58e9e24a9ae98d463056fecc1120a88660a9e1a72643e4a3e9de0fd731e54ded02c7abb99118cf7992229234d languageName: node linkType: hard @@ -5879,15 +6207,15 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:1.9.1": - version: 1.9.1 - resolution: "@opentelemetry/resources@npm:1.9.1" +"@opentelemetry/resources@npm:1.11.0": + version: 1.11.0 + resolution: "@opentelemetry/resources@npm:1.11.0" dependencies: - "@opentelemetry/core": 1.9.1 - "@opentelemetry/semantic-conventions": 1.9.1 + "@opentelemetry/core": 1.11.0 + "@opentelemetry/semantic-conventions": 1.11.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.5.0" - checksum: cf15e5faa698df3f0abcee35f7b4271c019b6cb81cb521b07793fe622c716d9c6873216219879afd57a28202f748a839ecaf28e04268e490004f14bbb850c96e + checksum: 3d4577d06f166d7efcf33ecc909115502d30cdf6bc53536c1672a8e76071447f3a01b2546159c7f44440c83549807e1d546ff1a0d4c169c8368013ed1d7d75f5 languageName: node linkType: hard @@ -5905,16 +6233,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.9.1": - version: 1.9.1 - resolution: "@opentelemetry/sdk-metrics@npm:1.9.1" +"@opentelemetry/sdk-metrics@npm:1.11.0": + version: 1.11.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.11.0" dependencies: - "@opentelemetry/core": 1.9.1 - "@opentelemetry/resources": 1.9.1 + "@opentelemetry/core": 1.11.0 + "@opentelemetry/resources": 1.11.0 lodash.merge: 4.6.2 peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.5.0" - checksum: 08e8215841da74ffc36f2e5c414ab4230e5a2676a4329f9c98fbde4b162b6166612ab48bcc701cebf8ded3e2b5a68981f72987751df4393e0839089fa0c5ee13 + checksum: 158ebcb79fc08f7e1530dcd2f0bfb9afbe2668396721df98fc7a7f5a75ceed4bceb7102aae0efcc24cd9bd5c92b51aa55a86f2cc904b538a45641ca0315b2e06 languageName: node linkType: hard @@ -5932,16 +6260,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.9.1": - version: 1.9.1 - resolution: "@opentelemetry/sdk-trace-base@npm:1.9.1" +"@opentelemetry/sdk-trace-base@npm:1.11.0": + version: 1.11.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.11.0" dependencies: - "@opentelemetry/core": 1.9.1 - "@opentelemetry/resources": 1.9.1 - "@opentelemetry/semantic-conventions": 1.9.1 + "@opentelemetry/core": 1.11.0 + "@opentelemetry/resources": 1.11.0 + "@opentelemetry/semantic-conventions": 1.11.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.5.0" - checksum: f9448132686b1a8c1fde7539845a2b31bcb315c3bbabccb20a18142db80eeed433b3713e2761151348c1b626ad00183f4b7e9b9868d1a8ab8c541dce1d082f38 + checksum: 64f00f8ede6f9fe5749021b820c412a78d984883dbe7db0ab2ed24fb3e2db0bcb9b928361cccb839b8a89007391fac0ad18386140394cc165716299b9b3b37e9 languageName: node linkType: hard @@ -5952,6 +6280,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/semantic-conventions@npm:1.11.0": + version: 1.11.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.11.0" + checksum: 310f2aad2a958b02ea2e661251e202989c662306e85a1c27bc99dec9c4d42fce5fc9b34bf21169276798acdae9d52af3b7ec19d8e5e9bb420fc145d0b901823c + languageName: node + linkType: hard + "@opentelemetry/semantic-conventions@npm:1.14.0": version: 1.14.0 resolution: "@opentelemetry/semantic-conventions@npm:1.14.0" @@ -5959,13 +6294,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.9.1": - version: 1.9.1 - resolution: "@opentelemetry/semantic-conventions@npm:1.9.1" - checksum: 6217ba14b8f0068a3400f054c1f9d918b2e22d1cf8d31112baa712b8d196e207aed7421c6df37ed1403f7109f51c7834c230cbe180313eee07db6f7e0a7797bc - languageName: node - linkType: hard - "@parcel/watcher@npm:2.0.4": version: 2.0.4 resolution: "@parcel/watcher@npm:2.0.4" @@ -6025,6 +6353,13 @@ __metadata: languageName: node linkType: hard +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f + languageName: node + linkType: hard + "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.10, @pmmmwh/react-refresh-webpack-plugin@npm:^0.5.5": version: 0.5.10 resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.10" @@ -11407,6 +11742,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + "ansicolor@npm:1.1.100": version: 1.1.100 resolution: "ansicolor@npm:1.1.100" @@ -13820,10 +14162,10 @@ __metadata: languageName: node linkType: hard -"core-js@npm:3.30.2": - version: 3.30.2 - resolution: "core-js@npm:3.30.2" - checksum: 73d47e2b9d9f502800973982d08e995bbf04832e20b04e04be31dd7607247158271315e9328788a2408190e291c7ffbefad141167b1e57dea9f983e1e723541e +"core-js@npm:3.31.0": + version: 3.31.0 + resolution: "core-js@npm:3.31.0" + checksum: f7cf9b3010f7ca99c026d95b61743baca1a85512742ed2b67e8f65a72ac4f4fe0b90b00057783e886bdd39d3a295f42f845d33e7cba3973ed263df978343ab79 languageName: node linkType: hard @@ -13991,21 +14333,21 @@ __metadata: languageName: node linkType: hard -"css-loader@npm:6.7.3": - version: 6.7.3 - resolution: "css-loader@npm:6.7.3" +"css-loader@npm:6.8.1": + version: 6.8.1 + resolution: "css-loader@npm:6.8.1" dependencies: icss-utils: ^5.1.0 - postcss: ^8.4.19 + postcss: ^8.4.21 postcss-modules-extract-imports: ^3.0.0 - postcss-modules-local-by-default: ^4.0.0 + postcss-modules-local-by-default: ^4.0.3 postcss-modules-scope: ^3.0.0 postcss-modules-values: ^4.0.0 postcss-value-parser: ^4.2.0 semver: ^7.3.8 peerDependencies: webpack: ^5.0.0 - checksum: 473cc32b6c837c2848e2051ad1ba331c1457449f47442e75a8c480d9891451434ada241f7e3de2347e57de17fcd84610b3bcfc4a9da41102cdaedd1e17902d31 + checksum: 7c1784247bdbe76dc5c55fb1ac84f1d4177a74c47259942c9cfdb7a8e6baef11967a0bc85ac285f26bd26d5059decb848af8154a03fdb4f4894f41212f45eef3 languageName: node linkType: hard @@ -14759,10 +15101,12 @@ __metadata: languageName: node linkType: hard -"date-fns@npm:2.29.3": - version: 2.29.3 - resolution: "date-fns@npm:2.29.3" - checksum: e01cf5b62af04e05dfff921bb9c9933310ed0e1ae9a81eb8653452e64dc841acf7f6e01e1a5ae5644d0337e9a7f936175fd2cb6819dc122fdd9c5e86c56be484 +"date-fns@npm:2.30.0": + version: 2.30.0 + resolution: "date-fns@npm:2.30.0" + dependencies: + "@babel/runtime": ^7.21.0 + checksum: f7be01523282e9bb06c0cd2693d34f245247a29098527d4420628966a2d9aad154bd0e90a6b1cf66d37adcb769cd108cf8a7bd49d76db0fb119af5cdd13644f4 languageName: node linkType: hard @@ -15464,6 +15808,13 @@ __metadata: languageName: node linkType: hard +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed + languageName: node + linkType: hard + "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -17296,6 +17647,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 139d270bc82dc9e6f8bc045fe2aae4001dc2472157044fdfad376d0a3457f77857fa883c1c8b21b491c6caade9a926a4bed3d3d2e8d3c9202b151a4cbbd0bcd5 + languageName: node + linkType: hard + "forever-agent@npm:~0.6.1": version: 0.6.1 resolution: "forever-agent@npm:0.6.1" @@ -18004,6 +18365,21 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.2.7": + version: 10.2.7 + resolution: "glob@npm:10.2.7" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 + path-scurry: ^1.7.0 + bin: + glob: dist/cjs/src/bin.js + checksum: 555205a74607d6f8d9874ba888924b305b5ea1abfaa2e9ccb11ac713d040aac7edbf7d8702a2f4a1cd81b2d7666412170ce7ef061d33cddde189dae8c1a1a054 + languageName: node + linkType: hard + "glob@npm:^7.0.0": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -18200,7 +18576,7 @@ __metadata: "@babel/plugin-transform-typescript": 7.22.3 "@babel/preset-env": 7.22.4 "@babel/preset-react": 7.22.3 - "@babel/preset-typescript": 7.21.5 + "@babel/preset-typescript": 7.22.5 "@babel/runtime": 7.22.3 "@betterer/betterer": 5.4.0 "@betterer/cli": 5.4.0 @@ -18218,13 +18594,13 @@ __metadata: "@grafana/eslint-config": 5.1.0 "@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules" "@grafana/experimental": 1.4.2 - "@grafana/faro-core": 1.0.2 - "@grafana/faro-web-sdk": 1.0.2 + "@grafana/faro-core": 1.1.0 + "@grafana/faro-web-sdk": 1.1.0 "@grafana/google-sdk": 0.1.1 "@grafana/lezer-logql": 0.1.5 "@grafana/monaco-logql": ^0.0.7 "@grafana/runtime": "workspace:*" - "@grafana/scenes": ^0.14.0 + "@grafana/scenes": ^0.15.0 "@grafana/schema": "workspace:*" "@grafana/toolkit": "workspace:*" "@grafana/tsconfig": ^1.2.0-rc1 @@ -18353,15 +18729,15 @@ __metadata: comlink: 4.4.1 common-tags: 1.8.2 copy-webpack-plugin: 11.0.0 - core-js: 3.30.2 - css-loader: 6.7.3 + core-js: 3.31.0 + css-loader: 6.8.1 css-minimizer-webpack-plugin: 4.2.2 cypress: 9.5.1 d3: 7.8.2 d3-force: 3.0.0 d3-scale-chromatic: 3.0.0 dangerously-set-html-content: 1.0.9 - date-fns: 2.29.3 + date-fns: 2.30.0 debounce-promise: 3.1.2 emotion: 11.0.0 esbuild: 0.17.19 @@ -20483,6 +20859,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^2.0.3": + version: 2.2.1 + resolution: "jackspeak@npm:2.2.1" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: e29291c0d0f280a063fa18fbd1e891ab8c2d7519fd34052c0ebde38538a15c603140d60c2c7f432375ff7ee4c5f1c10daa8b2ae19a97c3d4affe308c8360c1df + languageName: node + linkType: hard + "jake@npm:^10.8.5": version: 10.8.5 resolution: "jake@npm:10.8.5" @@ -22169,6 +22558,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^9.1.1": + version: 9.1.2 + resolution: "lru-cache@npm:9.1.2" + checksum: d3415634be3908909081fc4c56371a8d562d9081eba70543d86871b978702fffd0e9e362b83921b27a29ae2b37b90f55675aad770a54ac83bb3e4de5049d4b15 + languageName: node + linkType: hard + "lru-memoize@npm:^1.1.0": version: 1.1.0 resolution: "lru-memoize@npm:1.1.0" @@ -22669,6 +23065,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.1": + version: 9.0.1 + resolution: "minimatch@npm:9.0.1" + dependencies: + brace-expansion: ^2.0.1 + checksum: 97f5f5284bb57dc65b9415dec7f17a0f6531a33572193991c60ff18450dcfad5c2dad24ffeaf60b5261dccd63aae58cc3306e2209d57e7f88c51295a532d8ec3 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -22802,6 +23207,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0 || ^6.0.2": + version: 6.0.2 + resolution: "minipass@npm:6.0.2" + checksum: d140b91f4ab2e5ce5a9b6c468c0e82223504acc89114c1a120d4495188b81fedf8cade72a9f4793642b4e66672f990f1e0d902dd858485216a07cd3c8a62fac9 + languageName: node + linkType: hard + "minizlib@npm:^2.0.0, minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -23077,6 +23489,13 @@ __metadata: languageName: node linkType: hard +"murmurhash-js@npm:^1.0.0": + version: 1.0.0 + resolution: "murmurhash-js@npm:1.0.0" + checksum: 083cea92a11bc9eb25be1446fc92eded3f49731bc1ad34fa8023afd68c234d1dd59458d70eb20e667b1383bedeeb8dfb1a16c89913b6ffe3584fd22fb598739d + languageName: node + linkType: hard + "mutationobserver-shim@npm:0.3.7": version: 0.3.7 resolution: "mutationobserver-shim@npm:0.3.7" @@ -24457,6 +24876,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.7.0": + version: 1.9.2 + resolution: "path-scurry@npm:1.9.2" + dependencies: + lru-cache: ^9.1.1 + minipass: ^5.0.0 || ^6.0.2 + checksum: 92888dfb68e285043c6d3291c8e971d5d2bc2f5082f4d7b5392896f34be47024c9d0a8b688dd7ae6d125acc424699195474927cb4f00049a9b1ec7c4256fa8e0 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -24878,6 +25307,19 @@ __metadata: languageName: node linkType: hard +"postcss-modules-local-by-default@npm:^4.0.3": + version: 4.0.3 + resolution: "postcss-modules-local-by-default@npm:4.0.3" + dependencies: + icss-utils: ^5.0.0 + postcss-selector-parser: ^6.0.2 + postcss-value-parser: ^4.1.0 + peerDependencies: + postcss: ^8.1.0 + checksum: 2f8083687f3d6067885f8863dd32dbbb4f779cfcc7e52c17abede9311d84faf6d3ed8760e7c54c6380281732ae1f78e5e56a28baf3c271b33f450a11c9e30485 + languageName: node + linkType: hard + "postcss-modules-scope@npm:^3.0.0": version: 3.0.0 resolution: "postcss-modules-scope@npm:3.0.0" @@ -25156,7 +25598,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.21, postcss@npm:^8.4.19, postcss@npm:^8.4.21": +"postcss@npm:8.4.21, postcss@npm:^8.4.21": version: 8.4.21 resolution: "postcss@npm:8.4.21" dependencies: @@ -28524,6 +28966,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.0.2 + resolution: "signal-exit@npm:4.0.2" + checksum: 41f5928431cc6e91087bf0343db786a6313dd7c6fd7e551dbc141c95bb5fb26663444fd9df8ea47c5d7fc202f60aa7468c3162a9365cbb0615fc5e1b1328fe31 + languageName: node + linkType: hard + "simple-git@npm:^3.6.0": version: 3.16.0 resolution: "simple-git@npm:3.16.0" @@ -29343,6 +29792,17 @@ __metadata: languageName: node linkType: hard +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + "string-width@npm:^1.0.1": version: 1.0.2 resolution: "string-width@npm:1.0.2" @@ -29354,17 +29814,6 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.1 - checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - "string-width@npm:^5.0.0": version: 5.0.1 resolution: "string-width@npm:5.0.1" @@ -29376,6 +29825,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.7, string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -29481,6 +29941,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + "strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": version: 3.0.1 resolution: "strip-ansi@npm:3.0.1" @@ -29490,15 +29959,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c - languageName: node - linkType: hard - "strip-ansi@npm:^7.0.1": version: 7.0.1 resolution: "strip-ansi@npm:7.0.1" @@ -32075,6 +32535,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + "wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" @@ -32086,14 +32557,14 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 languageName: node linkType: hard
TypeDescriptionDescription
+ +