From 242ace772460f30f57c1239be931e2d39bf22f47 Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Fri, 26 Apr 2024 11:30:38 -0300 Subject: [PATCH 01/20] Switch to uv Python package installer/resolver (#4005) [uv](https://github.com/astral-sh/uv) is an extremely fast Python package installer and resolver, written in Rust, and designed as a drop-in replacement for pip and pip-tools workflows (see [post](https://astral.sh/blog/uv)) --- .drone.yml | 3 +- .github/workflows/linting-and-tests.yml | 24 +++++++------- Makefile | 12 +++---- engine/Dockerfile | 8 +++-- tools/pagerduty-migrator/requirements.in | 4 +++ tools/pagerduty-migrator/requirements.txt | 40 +++++++++++++++++++++-- 6 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 tools/pagerduty-migrator/requirements.in diff --git a/.drone.yml b/.drone.yml index 44339910..5c670f00 100644 --- a/.drone.yml +++ b/.drone.yml @@ -69,7 +69,8 @@ steps: commands: - apt-get update && apt-get install -y netcat-traditional - cd engine/ - - pip install -r requirements.txt -r requirements-dev.txt + - pip install uv + - uv pip install -r requirements.txt -r requirements-dev.txt - ./wait_for_test_mysql_start.sh && pytest depends_on: - rabbit_test diff --git a/.github/workflows/linting-and-tests.yml b/.github/workflows/linting-and-tests.yml index 2e2fd81e..4a5dc38e 100644 --- a/.github/workflows/linting-and-tests.yml +++ b/.github/workflows/linting-and-tests.yml @@ -127,8 +127,8 @@ jobs: # makemigrations --check = Exit with a non-zero status if model changes are missing migrations # and don't actually write them. run: | - pip install pip-tools - pip-sync requirements.txt requirements-dev.txt + pip install uv + uv pip sync --system requirements.txt requirements-dev.txt python manage.py makemigrations --check python manage.py lintmigrations @@ -185,8 +185,8 @@ jobs: working-directory: engine run: | apt-get update && apt-get install -y netcat-traditional - pip install pip-tools - pip-sync requirements.txt requirements-dev.txt + pip install uv + uv pip sync --system requirements.txt requirements-dev.txt ./wait_for_test_mysql_start.sh && pytest -x unit-test-backend-postgresql-rabbitmq: @@ -235,8 +235,8 @@ jobs: - name: Unit Test Backend working-directory: engine run: | - pip install pip-tools - pip-sync requirements.txt requirements-dev.txt + pip install uv + uv pip sync --system requirements.txt requirements-dev.txt pytest -x unit-test-backend-sqlite-redis: @@ -275,8 +275,8 @@ jobs: working-directory: engine run: | apt-get update && apt-get install -y netcat-traditional - pip install pip-tools - pip-sync requirements.txt requirements-dev.txt + pip install uv + uv pip sync --system requirements.txt requirements-dev.txt pytest -x unit-test-pd-migrator: @@ -292,8 +292,8 @@ jobs: - name: Unit Test PD Migrator working-directory: tools/pagerduty-migrator run: | - pip install pip-tools - pip-sync requirements.txt + pip install uv + uv pip sync --system requirements.txt pytest -x mypy: @@ -311,8 +311,8 @@ jobs: - name: mypy Static Type Checking working-directory: engine run: | - pip install pip-tools - pip-sync requirements.txt requirements-dev.txt + pip install uv + uv pip sync --system requirements.txt requirements-dev.txt mypy . end-to-end-tests: diff --git a/Makefile b/Makefile index 4ae1adb1..46ee7d89 100644 --- a/Makefile +++ b/Makefile @@ -248,21 +248,21 @@ endef backend-bootstrap: python3.11 -m venv $(VENV_DIR) - $(VENV_DIR)/bin/pip install -U pip wheel pip-tools - $(VENV_DIR)/bin/pip-sync $(REQUIREMENTS_TXT) $(REQUIREMENTS_DEV_TXT) + $(VENV_DIR)/bin/pip install -U pip wheel uv + $(VENV_DIR)/bin/uv pip sync $(REQUIREMENTS_TXT) $(REQUIREMENTS_DEV_TXT) @if [ -f $(REQUIREMENTS_ENTERPRISE_TXT) ]; then \ - $(VENV_DIR)/bin/pip install -r $(REQUIREMENTS_ENTERPRISE_TXT); \ + $(VENV_DIR)/bin/uv pip install -r $(REQUIREMENTS_ENTERPRISE_TXT); \ fi backend-migrate: $(call backend_command,python manage.py migrate) backend-compile-deps: - pip-compile --strip-extras $(REQUIREMENTS_IN) - pip-compile --strip-extras $(REQUIREMENTS_DEV_IN) + uv pip compile --strip-extras $(REQUIREMENTS_IN) + uv pip compile --strip-extras $(REQUIREMENTS_DEV_IN) backend-upgrade-deps: - pip-compile --strip-extras --upgrade $(REQUIREMENTS_IN) + uv pip compile --strip-extras --upgrade $(REQUIREMENTS_IN) run-backend-server: $(call backend_command,python manage.py runserver 0.0.0.0:8080) diff --git a/engine/Dockerfile b/engine/Dockerfile index 0c1083d1..0d8f255b 100644 --- a/engine/Dockerfile +++ b/engine/Dockerfile @@ -27,10 +27,12 @@ RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ && rm grpcio-1.57.0-cp311-cp311-linux_aarch64.whl; \ fi +RUN pip install uv + # TODO: figure out how to get this to work.. see comment in .github/workflows/e2e-tests.yml # https://stackoverflow.com/a/71846527 # RUN --mount=type=cache,target=/root/.cache/pip,from=pip_cache pip install -r requirements.txt -RUN pip install -r requirements.txt +RUN uv pip install --system -r requirements.txt # we intentionally have two COPY commands, this is to have the requirements.txt in a separate build step # which only invalidates when the requirements.txt actually changes. This avoids having to unneccasrily reinstall deps (which is time-consuming) @@ -63,13 +65,13 @@ RUN apk add sqlite mysql-client postgresql-client # TODO: figure out how to get this to work.. see comment in .github/workflows/e2e-tests.yml # https://stackoverflow.com/a/71846527 # RUN --mount=type=cache,target=/root/.cache/pip,from=pip_cache pip install -r requirements-dev.txt -RUN pip install -r requirements-dev.txt +RUN uv pip install --system -r requirements-dev.txt FROM dev AS dev-enterprise # TODO: figure out how to get this to work.. see comment in .github/workflows/e2e-tests.yml # https://stackoverflow.com/a/71846527 # RUN --mount=type=cache,target=/root/.cache/pip,from=pip_cache pip install -r requirements-enterprise-docker.txt -RUN pip install -r requirements-enterprise-docker.txt +RUN uv pip install --system -r requirements-enterprise-docker.txt FROM base AS prod diff --git a/tools/pagerduty-migrator/requirements.in b/tools/pagerduty-migrator/requirements.in new file mode 100644 index 00000000..36e35a92 --- /dev/null +++ b/tools/pagerduty-migrator/requirements.in @@ -0,0 +1,4 @@ +requests==2.31.0 +pdpyras==4.5.0 +pytest==7.1.2 +pytest-env==0.6.2 \ No newline at end of file diff --git a/tools/pagerduty-migrator/requirements.txt b/tools/pagerduty-migrator/requirements.txt index 36e35a92..3f71b48c 100644 --- a/tools/pagerduty-migrator/requirements.txt +++ b/tools/pagerduty-migrator/requirements.txt @@ -1,4 +1,40 @@ -requests==2.31.0 +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile requirements.in +# +attrs==23.2.0 + # via pytest +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +idna==3.6 + # via requests +iniconfig==2.0.0 + # via pytest +packaging==23.2 + # via pytest pdpyras==4.5.0 + # via -r requirements.in +pluggy==1.4.0 + # via pytest +py==1.11.0 + # via pytest pytest==7.1.2 -pytest-env==0.6.2 \ No newline at end of file + # via + # -r requirements.in + # pytest-env +pytest-env==0.6.2 + # via -r requirements.in +requests==2.31.0 + # via + # -r requirements.in + # pdpyras +tomli==2.0.1 + # via pytest +urllib3==2.2.1 + # via + # pdpyras + # requests From 01f2a1c0e3f3a4ec838ad2bd2e81200630a0cdef Mon Sep 17 00:00:00 2001 From: Alyssa Wada <101596687+alyssawada@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:36:33 -0600 Subject: [PATCH 02/20] Docs review on SNOW integration doc (#4275) # What this PR does Provides an editorial review of the SNOW doc for style, format, and overall Writers Toolkit adherence. --------- Co-authored-by: Vadim Stepanov --- docs/sources/integrations/servicenow/index.md | 117 +++++++++++------- .../ServiceNowTokenSection.tsx | 6 +- 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/docs/sources/integrations/servicenow/index.md b/docs/sources/integrations/servicenow/index.md index 094af476..c12c1811 100644 --- a/docs/sources/integrations/servicenow/index.md +++ b/docs/sources/integrations/servicenow/index.md @@ -1,8 +1,8 @@ --- -aliases: - - servicenow/ - - /docs/oncall/latest/integrations/available-integrations/configure-servicenow/ -canonical: https://grafana.com/docs/oncall/latest/integrations/available-integrations/configure-servicenow/ +title: ServiceNow integration for Grafana OnCall +menuTitle: ServiceNow +description: Learn how to configure the ServiceNow integration for Grafana OnCall +weight: 500 keywords: - Grafana Cloud - Alerts @@ -10,62 +10,83 @@ keywords: - on-call - webhooks - ServiceNow +canonical: https://grafana.com/docs/oncall/latest/integrations/servicenow +aliases: + - /docs/grafana-cloud/alerting-and-irm/oncall/integrations/servicenow + - /docs/oncall/latest/integrations/servicenow/ + - /servicenow/ labels: products: - cloud -title: ServiceNow -weight: 500 --- -# Integrate Grafana OnCall with ServiceNow +# ServiceNow integration for Grafana OnCall -> This integration is not available in OSS version +{{< admonition type="note" >}} +This integration is available exclusively on Grafana Cloud. +{{< /admonition >}} -The bi-directional ServiceNow integration can create and update incidents in ServiceNow based on Grafana OnCall alert -groups, and vice-versa. This integration supports alerts originating from ServiceNow or other integrations such as -Alertmanager, Grafana Alerting, and others. +Integrate ServiceNow with Grafana OnCall for bidirectional functionality that automatically creates and updates incidents in ServiceNow based on Grafana OnCall alert +groups, and vice versa. Whether your alerts originate from ServiceNow or another integration like +Alertmanager or Grafana Alerting, you can manage updates and status changes directly from ServiceNow. -The integration can automatically: +Use this integration to automate the following processes: * Create an incident in ServiceNow when an alert group is created in OnCall. * Update the incident state in ServiceNow when the alert group status changes in OnCall. * Create an alert group in OnCall when an incident is created in ServiceNow. * Update the alert group status in OnCall when the incident state changes in ServiceNow. -## Prerequisites +## Before you begin -1. Create a new ServiceNow user to be used by Grafana OnCall. On your ServiceNow instance, -navigate to **User Administration** > **Users** and click **New**. Fill in the following details: +Before configuring the integration, ensure you or your ServiceNow Admin have created a Service account specifically for Grafana OnCall integration. + +Follow these steps to create a ServiceNow user for Grafana OnCall: + +1. In ServiceNow,navigate to **User Administration** > **Users** and click **New**. +1. Fill in the following details: * Username: `grafana-oncall` * First name: `Grafana OnCall` * Active: ✔ * Web service access only: ✔ +1. After creating the user, generate a password using the **Set Password** button. Securely store the password for later use. +1. Navigate to the **Roles** tab and grant the following roles to the user: + * `itil` (for incident creation and updates) + * `personalize_choices` (to fetch the list of available incident states) - After creating the user, generate and save a password using the **Set Password** button for later use. -2. Grant the following roles to the user (use the **Roles** tab): - * `itil` (allows creating and updating incidents) - * `personalize_choices` (allows fetching the list of available incident states) +## Configure ServiceNow integration -## Create integration +### Create integration -1. On the **Integrations** tab, click **+ New integration**. -2. Select **ServiceNow** from the list of available integrations. -3. Enter a name and description for the integration. -4. Enter ServiceNow credentials (instance URL, username, and password of the [Grafana OnCall user](#prerequisites)) and verify the connection. -5. Make sure **Create default outgoing webhooks** is enabled. This will create the necessary webhooks in Grafana OnCall -to send alerts to ServiceNow. -6. Click **Create integration**. -7. Map ServiceNow incident states to OnCall alert group statuses. Example: - * `Firing -> New` - * `Acknowledged -> In Progress` - * `Resolved -> Resolved` - * `Silenced -> Not Selected` -8. Generate a ServiceNow Business Rule script and copy it to your clipboard. This script will allow your ServiceNow -instance to send updates to Grafana OnCall. You won't be able to see the script again after closing the -dialog, but you can regenerate it at any time in integration settings. See the next step for more details on how to -create a Business Rule in ServiceNow using the generated script. -9. On your ServiceNow instance, navigate to **System Definition** > **Business Rules** and click **New**. -Fill in the following details: +1. On the **Integrations** tab in Grafana OnCall, click **+ New integration**. +1. Select **ServiceNow** from the list of available integrations. +1. Enter a name and description for the integration. +1. Enter ServiceNow credentials (instance URL, username, and password of the [Grafana OnCall user](#before-you-begin)) and verify the connection. +1. Ensure **Create default outgoing webhooks** is enabled to create necessary webhooks in Grafana OnCall for sending alerts to ServiceNow. +1. Click **Create integration**. + +### Map incident states + +Map ServiceNow incident states to OnCall alert group statuses. + +Example: + +* `Firing -> New` +* `Acknowledged -> In Progress` +* `Resolved -> Resolved` +* `Silenced -> Not Selected` + +### Generate Business Rule script + +Generate a ServiceNow Business Rule script to enable your ServiceNow instance to send updates to Grafana OnCall. + +{{< admonition type="note" >}} +You can't view the script again after closing the dialog, but you can regenerate it at any time in integration settings. +{{< /admonition >}} + +1. Generate a new ServiceNow Business Rule script and copy it to your clipboard. +1. In ServiceNow, navigate to **System Definition** > **Business Rules** and click **New**. +1. Fill in the following details: * Name: `grafana-oncall` * Table: `incident` * Active: ✔ @@ -74,9 +95,9 @@ Fill in the following details: * When to run > Insert: ✔ * When to run > Update: ✔ * Advanced > Script: Paste the generated script +1. Click **Submit** to save the Business Rule. - Click **Submit** to save the Business Rule. -10. In Grafana OnCall, click **Proceed** to complete the integration setup. +In Grafana OnCall, click **Proceed** to complete the integration setup. ## Test the integration @@ -87,31 +108,31 @@ Fill in the following details: ## Connect other integrations -You can connect other integrations such as Alertmanager, Grafana Alerting, and others to an existing ServiceNow -integration. When connected, Grafana OnCall will send alerts from the connected integrations to ServiceNow, and update -alert groups on the connected integrations based on incident state changes in ServiceNow. Connected integrations will -use the same ServiceNow credentials and outgoing webhooks as the ServiceNow integration they are connected to. +You can connect other integrations such as Alertmanager, Grafana Alerting, and others to your ServiceNow integration for a consolidated workflow. +When connected, Grafana OnCall sends alerts from the connected integrations to ServiceNow and update alert groups on the connected integrations based on incident +state changes in ServiceNow. +Connected integrations utilize the same ServiceNow credentials and outgoing webhooks as the ServiceNow integration they are connected to. To connect other integrations: 1. Navigate to the **Outgoing** tab of an existing ServiceNow integration. 2. Use the **Send data from other integrations** section to connect other integrations. 3. Enable the **backsync** option if you want alert groups from connected integrations to be updated from ServiceNow. - If disabled, Grafana OnCall will only send alerts to ServiceNow, but not receive updates back. +If disabled, Grafana OnCall will only send alerts to ServiceNow, but not receive updates back. 4. Test the connection by creating a demo alert for the connected integration. * Verify that an incident is created in ServiceNow. * Verify that incident state changes in ServiceNow are reflected in Grafana OnCall, and vice-versa. ## Advanced usage -You can customize the integration behaviour by editing the outgoing webhooks on the **Outgoing** tab of the integration. +Customize the integration behavior according to your needs by editing the outgoing webhooks on the **Outgoing** tab of the integration. ### Custom incident fields You can set custom fields on ServiceNow incidents. To do so, edit the **Alert group created** webhook on the **Outgoing** tab of the integration. -Example: to set the "urgency" field based on alert group labels, add the following to **Data template**: +Example: Set the "urgency" field based on alert group labels, add the provided JSON to **Data template**: ```json { @@ -120,7 +141,7 @@ Example: to set the "urgency" field based on alert group labels, add the followi } ``` - Refer to [Outgoing webhook templates] and [Alert group labels] for more info. +For more information, refer to [Outgoing webhook templates] and [Alert group labels]. {{% docs/reference %}} [Alert group labels]: "/docs/oncall/ -> /docs/oncall//integrations#alert-group-labels" diff --git a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx index c24c6d2d..9f48f489 100644 --- a/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx +++ b/grafana-plugin/src/containers/ServiceNowConfigDrawer/ServiceNowTokenSection.tsx @@ -49,7 +49,11 @@ export const ServiceNowTokenSection: React.FC = obs Copy and paste the following script to ServiceNow to allow communication between ServiceNow and OnCall{' '} - + Read more From 364a0632a5f3279992e55b60bb8494a8fb1086bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:40:39 -0400 Subject: [PATCH 03/20] Bump idna from 3.6 to 3.7 in /tools/pagerduty-migrator (#4291) Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
Release notes

Sourced from idna's releases.

v3.7

What's Changed

  • Fix issue where specially crafted inputs to encode() could take exceptionally long amount of time to process. [CVE-2024-3651]

Thanks to Guido Vranken for reporting the issue.

Full Changelog: https://github.com/kjd/idna/compare/v3.6...v3.7

Changelog

Sourced from idna's changelog.

3.7 (2024-04-11) ++++++++++++++++

  • Fix issue where specially crafted inputs to encode() could take exceptionally long amount of time to process. [CVE-2024-3651]

Thanks to Guido Vranken for reporting the issue.

Commits
  • 1d365e1 Release v3.7
  • c1b3154 Merge pull request #172 from kjd/optimize-contextj
  • 0394ec7 Merge branch 'master' into optimize-contextj
  • cd58a23 Merge pull request #152 from elliotwutingfeng/dev
  • 5beb28b More efficient resolution of joiner contexts
  • 1b12148 Update ossf/scorecard-action to v2.3.1
  • d516b87 Update Github actions/checkout to v4
  • c095c75 Merge branch 'master' into dev
  • 60a0a4c Fix typo in GitHub Actions workflow key
  • 5918a0e Merge branch 'master' into dev
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=idna&package-manager=pip&previous-version=3.6&new-version=3.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/grafana/oncall/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/pagerduty-migrator/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pagerduty-migrator/requirements.txt b/tools/pagerduty-migrator/requirements.txt index 3f71b48c..807b369a 100644 --- a/tools/pagerduty-migrator/requirements.txt +++ b/tools/pagerduty-migrator/requirements.txt @@ -10,7 +10,7 @@ certifi==2024.2.2 # via requests charset-normalizer==3.3.2 # via requests -idna==3.6 +idna==3.7 # via requests iniconfig==2.0.0 # via pytest From 2a8240ab55ee2eddd70acadcaee6337330403628 Mon Sep 17 00:00:00 2001 From: Dominik Broj Date: Fri, 26 Apr 2024 23:41:08 +0200 Subject: [PATCH 04/20] Use ops-devenv only in expensive tests (#4285) # What this PR does - Use cross-plugin e2e tests setup (cloning ops-devenv, gops-labels) only on a daily runs and on dev/main branch pipelines (exclude it from PRs so that community PRs don't rely on secrets) - Rename "Daily e2e tests" to "Expensive e2e tests" and run them both daily and when PRs are merged to dev/main - Post Slack message only if e2e tests fail ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --------- Co-authored-by: Joey Orlando --- .github/workflows/e2e-tests.yml | 26 +++++++++++-------- ...-e2e-tests.yml => expensive-e2e-tests.yml} | 9 ++++--- .github/workflows/linting-and-tests.yml | 4 +-- .../createNewLabelKeysAndValues.test.ts | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) rename .github/workflows/{daily-e2e-tests.yml => expensive-e2e-tests.yml} (95%) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index ea2405f4..adccedcf 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -122,16 +122,19 @@ jobs: - name: Install Playwright deps uses: docker://mcr.microsoft.com/playwright:next-jammy - # Go and Mage are required to run gops-labels + # ---------- Expensive e2e tests steps start ----------- - name: Install Go + if: inputs.run-expensive-tests uses: actions/setup-go@v4 with: go-version: "1.21.5" - name: Install Mage + if: inputs.run-expensive-tests run: go install github.com/magefile/mage@v1.15.0 - name: Get Vault secrets + if: inputs.run-expensive-tests id: get-secrets uses: grafana/shared-workflows/actions/get-vault-secrets@main with: @@ -141,6 +144,7 @@ jobs: GH_APP_PRIVATE_KEY=github-app:private-key - name: Generate Github App token + if: inputs.run-expensive-tests id: generate-token uses: actions/create-github-app-token@v1 with: @@ -150,22 +154,13 @@ jobs: repositories: "ops-devenv,gops-labels" - name: Clone other repos needed for cross-plugin e2e tests + if: inputs.run-expensive-tests shell: bash run: | cd .. git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/ops-devenv.git git clone https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com/grafana/gops-labels.git - - name: Tilt CI - standard E2E tests - shell: bash - if: ${{ inputs.run-expensive-tests == false }} - env: - GRAFANA_VERSION: ${{ inputs.grafana_version }} - GRAFANA_ADMIN_USERNAME: "irm" - GRAFANA_ADMIN_PASSWORD: "irm" - BROWSERS: ${{ inputs.browsers }} - run: cd ../ops-devenv && tilt ci gops-labels oncall - - name: Tilt CI - standard and expensive E2E tests if: inputs.run-expensive-tests shell: bash @@ -182,6 +177,15 @@ jobs: TWILIO_PHONE_NUMBER: '"${{ secrets.TWILIO_PHONE_NUMBER }}"' TWILIO_VERIFY_SID: ${{ secrets.TWILIO_VERIFY_SID }} run: cd ../ops-devenv && tilt ci gops-labels oncall + # ---------- Expensive e2e tests steps end ----------- + + - name: Tilt CI - standard E2E tests + shell: bash + if: ${{ inputs.run-expensive-tests == false }} + env: + GRAFANA_VERSION: ${{ inputs.grafana_version }} + BROWSERS: ${{ inputs.browsers }} + run: tilt ci - name: Setup Pages if: failure() diff --git a/.github/workflows/daily-e2e-tests.yml b/.github/workflows/expensive-e2e-tests.yml similarity index 95% rename from .github/workflows/daily-e2e-tests.yml rename to .github/workflows/expensive-e2e-tests.yml index 8f03cf2c..5bda45e3 100644 --- a/.github/workflows/daily-e2e-tests.yml +++ b/.github/workflows/expensive-e2e-tests.yml @@ -1,5 +1,8 @@ -name: Daily e2e tests +name: Expensive e2e tests on: + push: + branches: + - main # allows manual run on github actions workflow_dispatch: schedule: @@ -7,7 +10,7 @@ on: jobs: end-to-end-tests: - name: End to end tests + name: Expensive e2e tests strategy: matrix: grafana_version: @@ -41,7 +44,7 @@ jobs: post-status-to-slack: runs-on: ubuntu-latest needs: end-to-end-tests - if: always() + if: failure steps: # Useful references # https://stackoverflow.com/questions/59073850/github-actions-get-url-of-test-build diff --git a/.github/workflows/linting-and-tests.yml b/.github/workflows/linting-and-tests.yml index 4a5dc38e..795ac982 100644 --- a/.github/workflows/linting-and-tests.yml +++ b/.github/workflows/linting-and-tests.yml @@ -1,4 +1,4 @@ -name: Linting and Unit/e2e Tests +name: Linting and Tests "on": push: @@ -316,7 +316,7 @@ jobs: mypy . end-to-end-tests: - name: End to end tests + name: Standard e2e tests uses: ./.github/workflows/e2e-tests.yml with: # TODO: fix issues with running e2e tests against Grafana v10.2.x and v10.3.x diff --git a/grafana-plugin/e2e-tests/labels/createNewLabelKeysAndValues.test.ts b/grafana-plugin/e2e-tests/labels/createNewLabelKeysAndValues.test.ts index 6daeac61..96226620 100644 --- a/grafana-plugin/e2e-tests/labels/createNewLabelKeysAndValues.test.ts +++ b/grafana-plugin/e2e-tests/labels/createNewLabelKeysAndValues.test.ts @@ -3,7 +3,7 @@ import { clickButton, generateRandomValidLabel, openDropdown } from '../utils/fo import { openCreateIntegrationModal } from '../utils/integrations'; import { goToOnCallPage } from '../utils/navigation'; -test('New label keys and labels can be created', async ({ adminRolePage }) => { +test('New label keys and labels can be created @expensive', async ({ adminRolePage }) => { const { page } = adminRolePage; await goToOnCallPage(page, 'integrations'); await openCreateIntegrationModal(page); From 0e59fadf38b96ada323a8f775b285a42f6912d61 Mon Sep 17 00:00:00 2001 From: Ravishankar Date: Sat, 27 Apr 2024 03:20:08 +0530 Subject: [PATCH 05/20] Add acknowledged, resolved user information on webhook payload (#4176) # What this PR does Adds acknowledged and resolved user information to the web hook payload ## Which issue(s) this PR closes Closes #4126 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --------- Co-authored-by: Joey Orlando --- .../configure/outgoing-webhooks/index.md | 26 ++++ .../webhooks/tests/test_trigger_webhook.py | 125 +++++++++++++++++- engine/apps/webhooks/utils.py | 2 + 3 files changed, 152 insertions(+), 1 deletion(-) diff --git a/docs/sources/configure/outgoing-webhooks/index.md b/docs/sources/configure/outgoing-webhooks/index.md index a1e24d6e..f01e04a6 100644 --- a/docs/sources/configure/outgoing-webhooks/index.md +++ b/docs/sources/configure/outgoing-webhooks/index.md @@ -243,6 +243,16 @@ must match the structure of how the fields are nested in the data. "urgency": "3" } }, + "alert_group_acknowledged_by": { + "id": "UVMX6YI9VY9PV", + "username": "admin", + "email": "admin@localhost" + }, + "alert_group_resolved_by": { + "id": "UVMX6YI9VY9PV", + "username": "admin", + "email": "admin@localhost" + }, "notified_users": [], "users_to_be_notified": [], "responses": { @@ -335,6 +345,22 @@ first element being the user that will be notified next. Like `notified_users` d a user in this array may have already been notified by the time this data is being processed. Access as `{{ users_to_notify[0].username }}` for example. +#### `alert_group_acknowledged_by` + +Information about the user who acknowledged the alert group + +- `{{ user.id }}` - [UID](#uid) of the user within Grafana OnCall +- `{{ user.username }}` - Username in Grafana +- `{{ user.email }}` - Email associated with user's Grafana account + +#### `alert_group_resolved_by` + +Information about the user who resolved the alert group + +- `{{ user.id }}` - [UID](#uid) of the user within Grafana OnCall +- `{{ user.username }}` - Username in Grafana +- `{{ user.email }}` - Email associated with user's Grafana account + #### `responses` The responses field is used to access the response data of other webhooks that are associated with this alert group. diff --git a/engine/apps/webhooks/tests/test_trigger_webhook.py b/engine/apps/webhooks/tests/test_trigger_webhook.py index 7a17350c..3ef949a9 100644 --- a/engine/apps/webhooks/tests/test_trigger_webhook.py +++ b/engine/apps/webhooks/tests/test_trigger_webhook.py @@ -1,4 +1,5 @@ import json +from datetime import timedelta from unittest.mock import call, patch import httpretty @@ -348,7 +349,11 @@ def test_execute_webhook_ok_forward_all( other_user = make_user_for_organization(organization) alert_receive_channel = make_alert_receive_channel(organization) alert_group = make_alert_group( - alert_receive_channel, acknowledged_at=timezone.now(), acknowledged=True, acknowledged_by=user.pk + alert_receive_channel, + acknowledged_at=timezone.now(), + acknowledged=True, + acknowledged_by=user.pk, + acknowledged_by_user=user, ) for _ in range(3): make_user_notification_policy_log_record( @@ -410,6 +415,12 @@ def test_execute_webhook_ok_forward_all( "name": webhook.name, "labels": {}, }, + "alert_group_acknowledged_by": { + "id": user.public_primary_key, + "username": user.username, + "email": user.email, + }, + "alert_group_resolved_by": None, } expected_call = call( "https://something/{}/".format(alert_group.public_primary_key), @@ -427,6 +438,118 @@ def test_execute_webhook_ok_forward_all( assert log.url == "https://something/{}/".format(alert_group.public_primary_key) +@pytest.mark.django_db +def test_execute_webhook_ok_forward_all_resolved( + make_organization, + make_user_for_organization, + make_alert_receive_channel, + make_alert_group, + make_user_notification_policy_log_record, + make_custom_webhook, +): + organization = make_organization() + user = make_user_for_organization(organization) + notified_user = make_user_for_organization(organization) + other_user = make_user_for_organization(organization) + alert_receive_channel = make_alert_receive_channel(organization) + alert_group = make_alert_group( + alert_receive_channel, + acknowledged_at=timezone.now(), + acknowledged=True, + acknowledged_by=user.pk, + acknowledged_by_user=user, + resolved=True, + resolved_at=timezone.now() + timedelta(hours=2), + resolved_by=user.pk, + resolved_by_user=user, + ) + for _ in range(3): + make_user_notification_policy_log_record( + author=notified_user, + alert_group=alert_group, + type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS, + ) + make_user_notification_policy_log_record( + author=other_user, + alert_group=alert_group, + type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED, + ) + webhook = make_custom_webhook( + organization=organization, + url="https://something/{{ alert_group_id }}/", + http_method="POST", + trigger_type=Webhook.TRIGGER_RESOLVE, + forward_all=True, + ) + + mock_response = MockResponse() + with patch("apps.webhooks.utils.socket.gethostbyname") as mock_gethostbyname: + mock_gethostbyname.return_value = "8.8.8.8" + with patch("apps.webhooks.models.webhook.requests") as mock_requests: + mock_requests.post.return_value = mock_response + execute_webhook(webhook.pk, alert_group.pk, user.pk, None, trigger_type=Webhook.TRIGGER_RESOLVE) + + assert mock_requests.post.called + expected_data = { + "event": { + "type": "resolve", + "time": alert_group.resolved_at.isoformat(), + }, + "user": { + "id": user.public_primary_key, + "username": user.username, + "email": user.email, + }, + "integration": { + "id": alert_receive_channel.public_primary_key, + "type": alert_receive_channel.integration, + "name": alert_receive_channel.short_name, + "team": None, + "labels": {}, + }, + "notified_users": [ + { + "id": notified_user.public_primary_key, + "username": notified_user.username, + "email": notified_user.email, + } + ], + "alert_group": {**IncidentSerializer(alert_group).data, "labels": {}}, + "alert_group_id": alert_group.public_primary_key, + "alert_payload": "", + "users_to_be_notified": [], + "webhook": { + "id": webhook.public_primary_key, + "name": webhook.name, + "labels": {}, + }, + "alert_group_acknowledged_by": { + "id": user.public_primary_key, + "username": user.username, + "email": user.email, + }, + "alert_group_resolved_by": { + "id": user.public_primary_key, + "username": user.username, + "email": user.email, + }, + } + expected_call = call( + "https://something/{}/".format(alert_group.public_primary_key), + timeout=TIMEOUT, + headers={}, + json=expected_data, + ) + assert mock_requests.post.call_args == expected_call + # check logs + log = webhook.responses.all()[0] + assert log.trigger_type == Webhook.TRIGGER_RESOLVE + assert log.status_code == 200 + assert log.content == json.dumps(mock_response.json()) + assert json.loads(log.request_data) == expected_data + assert log.url == "https://something/{}/".format(alert_group.public_primary_key) + + @pytest.mark.django_db def test_execute_webhook_using_responses_data( make_organization, diff --git a/engine/apps/webhooks/utils.py b/engine/apps/webhooks/utils.py index faadf169..93ee42f2 100644 --- a/engine/apps/webhooks/utils.py +++ b/engine/apps/webhooks/utils.py @@ -175,6 +175,8 @@ def serialize_event(event, alert_group, user, webhook, responses=None): for user in set(notification.author for notification in alert_group.sent_notifications) ], "users_to_be_notified": _extract_users_from_escalation_snapshot(alert_group.escalation_snapshot), + "alert_group_acknowledged_by": _serialize_event_user(alert_group.acknowledged_by_user), + "alert_group_resolved_by": _serialize_event_user(alert_group.resolved_by_user), } if responses: data["responses"] = responses From 6ed7a1e3b8393b7dc434e4da1dd2f6cdc8f62759 Mon Sep 17 00:00:00 2001 From: Rares Mardare Date: Mon, 29 Apr 2024 11:13:26 +0300 Subject: [PATCH 06/20] Tag component tweaks to get rid of hardcoded tag variables (#4280) # What this PR does - Removed the usage of `var(--` within the Tag component to help get rid of the vars file once we fully migrate to emotion - Added few other display tweaks and migrated a few stylesheets to emotion --- grafana-plugin/package.json | 1 + .../CardButton/CardButton.styles.ts | 20 ++-- .../Integrations/IntegrationTag.tsx | 9 +- .../components/Policy/EscalationPolicy.tsx | 6 +- .../ScheduleQuality/ScheduleQuality.styles.ts | 21 ---- .../ScheduleQuality/ScheduleQuality.tsx | 12 +-- grafana-plugin/src/components/Tag/Tag.tsx | 102 +++++++++++++----- .../src/components/Text/Text.styles.ts | 4 +- grafana-plugin/src/components/Text/Text.tsx | 6 +- .../TooltipBadge/TooltipBadge.styles.ts | 42 +++----- .../components/TooltipBadge/TooltipBadge.tsx | 7 +- .../__snapshots__/Unauthorized.test.tsx.snap | 20 ++-- .../__snapshots__/AddResponders.test.tsx.snap | 22 ++-- .../__snapshots__/TeamResponder.test.tsx.snap | 2 +- .../__snapshots__/UserResponder.test.tsx.snap | 2 +- .../src/containers/AlertRules/AlertRules.tsx | 6 +- .../EscalationChainSteps.tsx | 10 +- .../IntegrationFormContainer.tsx | 10 +- .../MobileAppConnection.test.tsx.snap | 46 ++++---- .../__snapshots__/DownloadIcons.test.tsx.snap | 8 +- .../LinkLoginButton.test.tsx.snap | 4 +- .../PluginConfigPage.test.tsx.snap | 24 ++--- .../ConfigurationForm.test.tsx.snap | 22 ++-- .../StatusMessageBlock.test.tsx.snap | 2 +- .../RotationForm/ScheduleOverrideForm.tsx | 8 +- .../RotationForm/parts/UserItem.tsx | 14 ++- .../parts/connectors/PhoneConnector.tsx | 8 +- .../src/navbar/Header/Header.module.scss | 69 ------------ .../src/navbar/Header/Header.styles.ts | 76 +++++++++++++ grafana-plugin/src/navbar/Header/Header.tsx | 36 +++---- .../src/pages/incident/Incident.helpers.tsx | 41 ------- .../parts/IncidentDropdown.module.scss | 51 --------- .../parts/IncidentDropdown.styles.ts | 49 +++++++++ .../incidents/parts/IncidentDropdown.tsx | 74 ++++++------- grafana-plugin/src/styles/utils.styles.ts | 38 ++++++- grafana-plugin/src/utils/DOM.ts | 4 - grafana-plugin/yarn.lock | 2 +- 37 files changed, 445 insertions(+), 433 deletions(-) delete mode 100644 grafana-plugin/src/navbar/Header/Header.module.scss create mode 100644 grafana-plugin/src/navbar/Header/Header.styles.ts delete mode 100644 grafana-plugin/src/pages/incidents/parts/IncidentDropdown.module.scss create mode 100644 grafana-plugin/src/pages/incidents/parts/IncidentDropdown.styles.ts diff --git a/grafana-plugin/package.json b/grafana-plugin/package.json index 928b23e8..8f86c341 100644 --- a/grafana-plugin/package.json +++ b/grafana-plugin/package.json @@ -174,6 +174,7 @@ "stylelint": "^13.13.1", "stylelint-config-standard": "^22.0.0", "throttle-debounce": "^2.1.0", + "tinycolor2": "^1.6.0", "tslib": "2.5.3" }, "packageManager": "yarn@1.22.21" diff --git a/grafana-plugin/src/components/CardButton/CardButton.styles.ts b/grafana-plugin/src/components/CardButton/CardButton.styles.ts index ec773895..e2584fd5 100644 --- a/grafana-plugin/src/components/CardButton/CardButton.styles.ts +++ b/grafana-plugin/src/components/CardButton/CardButton.styles.ts @@ -23,17 +23,15 @@ export const getCardButtonStyles = (theme: GrafanaTheme2) => { `, rootSelected: css` - { - &::before { - display: block; - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 4px; - background-image: linear-gradient(270deg, #f55f3e 0%, #f83 100%); - } + &::before { + display: block; + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background-image: ${theme.colors.gradients.brandVertical}; } `, }; diff --git a/grafana-plugin/src/components/Integrations/IntegrationTag.tsx b/grafana-plugin/src/components/Integrations/IntegrationTag.tsx index 105014d3..795edab6 100644 --- a/grafana-plugin/src/components/Integrations/IntegrationTag.tsx +++ b/grafana-plugin/src/components/Integrations/IntegrationTag.tsx @@ -1,10 +1,9 @@ import React, { FC } from 'react'; import { css } from '@emotion/css'; -import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; -import { Tag } from 'components/Tag/Tag'; +import { Tag, TagColor } from 'components/Tag/Tag'; import { Text } from 'components/Text/Text'; interface IntegrationTagProps { @@ -15,7 +14,7 @@ export const IntegrationTag: FC = ({ children }) => { const styles = useStyles2(getStyles); return ( - + {children} @@ -23,11 +22,9 @@ export const IntegrationTag: FC = ({ children }) => { ); }; -export const getStyles = (theme: GrafanaTheme2) => ({ +export const getStyles = () => ({ tag: css({ height: '25px', - background: theme.colors.background.secondary, - border: `1px solid ${theme.colors.border.weak}`, }), radius: css({ borderRadius: '4px', diff --git a/grafana-plugin/src/components/Policy/EscalationPolicy.tsx b/grafana-plugin/src/components/Policy/EscalationPolicy.tsx index 0b1b4f95..53967b6b 100644 --- a/grafana-plugin/src/components/Policy/EscalationPolicy.tsx +++ b/grafana-plugin/src/components/Policy/EscalationPolicy.tsx @@ -7,6 +7,7 @@ import { observer } from 'mobx-react'; import moment from 'moment-timezone'; import { SortableElement } from 'react-sortable-hoc'; import reactStringReplace from 'react-string-replace'; +import { getLabelBackgroundTextColorObject } from 'styles/utils.styles'; import { PluginLink } from 'components/PluginLink/PluginLink'; import { Text } from 'components/Text/Text'; @@ -28,7 +29,6 @@ import { UserGroup } from 'models/user_group/user_group.types'; import { ApiSchemas } from 'network/oncall-api/api.types'; import { SelectOption, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; -import { getVar } from 'utils/DOM'; import { UserActions } from 'utils/authorization/authorization'; import { DragHandle } from './DragHandle'; @@ -80,12 +80,14 @@ class _EscalationPolicy extends React.Component { (escalationOption: EscalationPolicyOption) => escalationOption.value === step ); + const { textColor: itemTextColor } = getLabelBackgroundTextColorObject('green', this.props.theme); + return ( diff --git a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.styles.ts b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.styles.ts index 8ca90009..bc8cfc51 100644 --- a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.styles.ts +++ b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.styles.ts @@ -17,27 +17,6 @@ export const getScheduleQualityStyles = (_theme: GrafanaTheme2) => { tag: css` font-size: 12px; padding: 5px 10px; - - &--danger { - // TODO: emotionjs - background-color: var(--tag-background-danger); - color: var(--tag-text-danger); - border: 1px solid var(--tag-border-danger); - } - - &--warning { - // TODO: emotionjs - background-color: var(--tag-background-warning); - color: var(--tag-text-warning); - border: 1px solid var(--tag-border-warning); - } - - &--primary { - // TODO: emotionjs - background-color: var(--tag-background-success); - color: var(--tag-text-success); - border: 1px solid var(--tag-border-success); - } `, }; }; diff --git a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx index 72e88d2c..e8896651 100644 --- a/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx +++ b/grafana-plugin/src/components/ScheduleQuality/ScheduleQuality.tsx @@ -3,11 +3,11 @@ import React, { FC, useEffect } from 'react'; import { cx } from '@emotion/css'; import { Tooltip, VerticalGroup, useStyles2 } from '@grafana/ui'; import { observer } from 'mobx-react'; -import { bem, getUtilStyles } from 'styles/utils.styles'; +import { getUtilStyles } from 'styles/utils.styles'; import { PluginLink } from 'components/PluginLink/PluginLink'; import { ScheduleQualityDetails } from 'components/ScheduleQualityDetails/ScheduleQualityDetails'; -import { Tag } from 'components/Tag/Tag'; +import { Tag, TagColor } from 'components/Tag/Tag'; import { Text } from 'components/Text/Text'; import { TooltipBadge } from 'components/TooltipBadge/TooltipBadge'; import { Schedule, ScheduleScoreQualityResult } from 'models/schedule/schedule.types'; @@ -88,7 +88,7 @@ export const ScheduleQuality: FC = observer(({ schedule }) content={} >
- + Quality: {getScheduleQualityString(quality.total_score)}
@@ -115,11 +115,11 @@ export const ScheduleQuality: FC = observer(({ schedule }) function getTagSeverity() { if (quality?.total_score < 20) { - return 'danger'; + return TagColor.ERROR_LABEL; } if (quality?.total_score < 60) { - return 'warning'; + return TagColor.WARNING_LABEL; } - return 'primary'; + return TagColor.SUCCESS_LABEL; } }); diff --git a/grafana-plugin/src/components/Tag/Tag.tsx b/grafana-plugin/src/components/Tag/Tag.tsx index 7c072d69..3dc9feb4 100644 --- a/grafana-plugin/src/components/Tag/Tag.tsx +++ b/grafana-plugin/src/components/Tag/Tag.tsx @@ -3,10 +3,10 @@ import React, { FC } from 'react'; import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; -import { bem } from 'styles/utils.styles'; +import { bem, getLabelCss } from 'styles/utils.styles'; interface TagProps { - color?: string; + color?: string | TagColor; className?: string; border?: string; text?: string; @@ -16,43 +16,89 @@ interface TagProps { size?: 'small' | 'medium'; } +export enum TagColor { + SUCCESS = 'success', + WARNING = 'warning', + ERROR = 'error', + SECONDARY = 'secondary', + INFO = 'info', + + SUCCESS_LABEL = 'successLabel', + WARNING_LABEL = 'warningLabel', + ERROR_LABEL = 'errorLabel', +} + export const Tag: FC = (props) => { + const { color, children, className, onClick, size = 'medium' } = props; + const styles = useStyles2(getStyles); - const { children, color, text, className, border, onClick, size = 'medium' } = props; - const style: React.CSSProperties = { - backgroundColor: color, - color: text, - border, - }; return ( {children} ); -}; -const getStyles = (_theme: GrafanaTheme2) => { - return { - root: css` - border-radius: 2px; - line-height: 100%; - padding: 5px 8px; - color: #fff; - display: inline-block; - white-space: nowrap; - `, + function getMatchingClass() { + return styles[color] + ? // Either pick a defined style already or create one on the spot with the passed bgColor + styles[color] + : css` + background-color: ${color}; + color: text; + `; + } - size: css` - &--small { - font-size: 12px; - height: 24px; - } - `, - }; + function getStyles(theme: GrafanaTheme2) { + return { + root: css` + border-radius: 2px; + line-height: 100%; + padding: 5px 8px; + display: inline-block; + white-space: nowrap; + `, + + size: css` + &--small { + font-size: 12px; + height: 24px; + } + `, + + success: css` + background-color: ${theme.colors.success.main}; + border: solid 1px ${theme.colors.success.main}; + color: ${theme.isDark ? '#fff' : theme.colors.success.contrastText}; + `, + warning: css` + background-color: ${theme.colors.warning.main}; + border: solid 1px ${theme.colors.warning.main}; + color: #fff; + `, + error: css` + background-color: ${theme.colors.error.main}; + border: solid 1px ${theme.colors.error.main}; + color: ${theme.isDark ? '#fff' : theme.colors.error.contrastText}; + `, + secondary: css` + background-color: ${theme.colors.secondary.main}; + border: solid 1px ${theme.colors.secondary.main}; + color: ${theme.isDark ? '#fff' : theme.colors.secondary.contrastText}; + `, + info: css` + background-color: ${theme.colors.primary.main}; + border: solid 1px ${theme.colors.primary.main}; + color: ${theme.isDark ? '#fff' : theme.colors.info.contrastText}; + `, + + successLabel: getLabelCss('green', theme), + warningLabel: getLabelCss('orange', theme), + errorLabel: getLabelCss('red', theme), + }; + } }; diff --git a/grafana-plugin/src/components/Text/Text.styles.ts b/grafana-plugin/src/components/Text/Text.styles.ts index 0f8e20fe..901f30af 100644 --- a/grafana-plugin/src/components/Text/Text.styles.ts +++ b/grafana-plugin/src/components/Text/Text.styles.ts @@ -7,8 +7,8 @@ export const getTextStyles = (theme: GrafanaTheme2) => { root: css` display: inline; - &:hover .icon-button { - display: inline-block; + &:hover [data-emotion='iconButton'] { + display: inline-flex; } `, diff --git a/grafana-plugin/src/components/Text/Text.tsx b/grafana-plugin/src/components/Text/Text.tsx index 5877e4c4..493d9e0b 100644 --- a/grafana-plugin/src/components/Text/Text.tsx +++ b/grafana-plugin/src/components/Text/Text.tsx @@ -91,8 +91,8 @@ export const Text: TextInterface = (props) => { styles.root, styles.text, { [styles.maxWidth]: Boolean(maxWidth) }, - { [bem(styles.text, `${type}`)]: true }, - { [bem(styles.text, `${size}`)]: true }, + { [bem(styles.text, type)]: true }, + { [bem(styles.text, size)]: true }, { [bem(styles.text, `strong`)]: strong }, { [bem(styles.text, `underline`)]: underline }, { [bem(styles.text, 'clickable')]: clickable }, @@ -109,6 +109,7 @@ export const Text: TextInterface = (props) => { className={styles.iconButton} tooltip="Edit" tooltipPlacement="top" + data-emotion="iconButton" name="pen" /> )} @@ -124,6 +125,7 @@ export const Text: TextInterface = (props) => { className={styles.iconButton} tooltip="Copy to clipboard" tooltipPlacement="top" + data-emotion="iconButton" name="copy" /> diff --git a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.styles.ts b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.styles.ts index 455eb4d2..86a25ab6 100644 --- a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.styles.ts +++ b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.styles.ts @@ -1,8 +1,20 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { getLabelCss } from 'styles/utils.styles'; export const getTooltipBadgeStyles = (theme: GrafanaTheme2) => { return { + primary: getLabelCss('blue', theme), + warning: getLabelCss('orange', theme), + success: getLabelCss('green', theme), + danger: getLabelCss('red', theme), + + secondary: css` + background: ${theme.colors.background.secondary}; + border: 1px solid ${theme.colors.border.weak}; + color: ${theme.colors.text.primary}; + `, + element: css` font-size: 12px; line-height: 16px; @@ -10,36 +22,6 @@ export const getTooltipBadgeStyles = (theme: GrafanaTheme2) => { border-radius: 2px; display: inline-block; - &--primary { - background: var(--tag-background-primary); - border: 1px solid var(--tag-border-primary); - color: var(--tag-text-primary); - } - - &--secondary { - background: ${theme.colors.background.secondary}; - border: 1px solid ${theme.colors.border.weak}; - color: ${theme.colors.text.primary}; - } - - &--warning { - background: var(--tag-background-warning); - border: 1px solid var(--tag-border-warning); - color: var(--tag-text-warning); - } - - &--success { - background: var(--tag-background-success); - border: 1px solid var(--tag-border-success); - color: var(--tag-text-success); - } - - &--danger { - background: var(--tag-background-danger); - border: 1px solid var(--tag-border-danger); - color: var(--tag-text-danger); - } - &--padding { padding: 3px 10px; } diff --git a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx index 1c40cf7f..858af3dc 100644 --- a/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx +++ b/grafana-plugin/src/components/TooltipBadge/TooltipBadge.tsx @@ -55,12 +55,7 @@ export const TooltipBadge: FC = (props) => { } >
diff --git a/grafana-plugin/src/components/Unauthorized/__snapshots__/Unauthorized.test.tsx.snap b/grafana-plugin/src/components/Unauthorized/__snapshots__/Unauthorized.test.tsx.snap index 83950867..59b30dfb 100644 --- a/grafana-plugin/src/components/Unauthorized/__snapshots__/Unauthorized.test.tsx.snap +++ b/grafana-plugin/src/components/Unauthorized/__snapshots__/Unauthorized.test.tsx.snap @@ -20,7 +20,7 @@ exports[`Unauthorized renders properly - access control enabled: false 1`] = ` className="css-1fmhfo9" > Participants @@ -72,7 +72,7 @@ exports[`AddResponders should properly display the add responders button when hi class="css-u023fv" > Participants @@ -106,7 +106,7 @@ exports[`AddResponders should render properly in create mode 1`] = ` class="css-u023fv" > Participants @@ -159,7 +159,7 @@ exports[`AddResponders should render properly in update mode 1`] = ` class="css-u023fv" > Participants @@ -212,7 +212,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-u023fv" > Participants @@ -270,7 +270,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-18qv8yz-layoutChildrenWrapper" > my test team @@ -323,7 +323,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-18qv8yz-layoutChildrenWrapper" > my test user3 @@ -438,7 +438,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-18qv8yz-layoutChildrenWrapper" > my test user @@ -552,7 +552,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-18qv8yz-layoutChildrenWrapper" > my test user2 @@ -664,7 +664,7 @@ exports[`AddResponders should render selected team and users properly 1`] = ` class="css-9om60p" >
my test team diff --git a/grafana-plugin/src/containers/AddResponders/parts/UserResponder/__snapshots__/UserResponder.test.tsx.snap b/grafana-plugin/src/containers/AddResponders/parts/UserResponder/__snapshots__/UserResponder.test.tsx.snap index b2f33321..722f56a7 100644 --- a/grafana-plugin/src/containers/AddResponders/parts/UserResponder/__snapshots__/UserResponder.test.tsx.snap +++ b/grafana-plugin/src/containers/AddResponders/parts/UserResponder/__snapshots__/UserResponder.test.tsx.snap @@ -31,7 +31,7 @@ exports[`UserResponder it renders data properly 1`] = ` class="css-18qv8yz-layoutChildrenWrapper" > johnsmith diff --git a/grafana-plugin/src/containers/AlertRules/AlertRules.tsx b/grafana-plugin/src/containers/AlertRules/AlertRules.tsx index c3b9cf57..82c2941c 100644 --- a/grafana-plugin/src/containers/AlertRules/AlertRules.tsx +++ b/grafana-plugin/src/containers/AlertRules/AlertRules.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; -import { VerticalGroup } from '@grafana/ui'; +import { VerticalGroup, useTheme2 } from '@grafana/ui'; import { Timeline } from 'components/Timeline/Timeline'; import { MSTeamsConnector } from 'containers/AlertRules/parts/connectors/MSTeamsConnector'; @@ -9,7 +9,6 @@ import { TelegramConnector } from 'containers/AlertRules/parts/connectors/Telegr import { ChannelFilter } from 'models/channel_filter/channel_filter.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { getVar } from 'utils/DOM'; interface ChatOpsConnectorsProps { channelFilterId: ChannelFilter['id']; @@ -20,6 +19,7 @@ export const ChatOpsConnectors = (props: ChatOpsConnectorsProps) => { const { channelFilterId, showLineNumber = true } = props; const store = useStore(); + const theme = useTheme2(); const { organizationStore, telegramChannelStore, msteamsChannelStore } = store; const isSlackInstalled = Boolean(organizationStore.currentOrganization?.slack_team_identity); @@ -37,7 +37,7 @@ export const ChatOpsConnectors = (props: ChatOpsConnectorsProps) => { } return ( - + {isSlackInstalled && } {isTelegramInstalled && } diff --git a/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx b/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx index 9faffc51..97ae2cd4 100644 --- a/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx +++ b/grafana-plugin/src/containers/EscalationChainSteps/EscalationChainSteps.tsx @@ -2,10 +2,11 @@ import React, { ReactElement, useCallback, useEffect } from 'react'; import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; -import { LoadingPlaceholder, Select, useStyles2 } from '@grafana/ui'; +import { LoadingPlaceholder, Select, useStyles2, useTheme2 } from '@grafana/ui'; import cn from 'classnames/bind'; import { get } from 'lodash-es'; import { observer } from 'mobx-react'; +import { getLabelBackgroundTextColorObject } from 'styles/utils.styles'; import { EscalationPolicy, EscalationPolicyProps } from 'components/Policy/EscalationPolicy'; import { SortableList } from 'components/SortableList/SortableList'; @@ -14,7 +15,6 @@ import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/W import { EscalationChain } from 'models/escalation_chain/escalation_chain.types'; import { EscalationPolicyOption } from 'models/escalation_policy/escalation_policy.types'; import { useStore } from 'state/useStore'; -import { getVar } from 'utils/DOM'; import { UserActions } from 'utils/authorization/authorization'; import styles from './EscalationChainSteps.module.css'; @@ -41,6 +41,7 @@ export const EscalationChainSteps = observer((props: EscalationChainStepsProps) const store = useStore(); const styles = useStyles2(getStyles); + const theme = useTheme2(); const { escalationPolicyStore } = store; @@ -74,6 +75,7 @@ export const EscalationChainSteps = observer((props: EscalationChainStepsProps) const escalationPolicyIds = escalationPolicyStore.escalationChainToEscalationPolicy[id]; const isSlackInstalled = Boolean(store.organizationStore.currentOrganization?.slack_team_identity); + const { bgColor: successBgColor, textColor: successTextColor } = getLabelBackgroundTextColorObject('green', theme); return ( // @ts-ignore @@ -124,8 +126,8 @@ export const EscalationChainSteps = observer((props: EscalationChainStepsProps) {!isDisabled && (