diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 0717e96a78..a6778251c0 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -105,7 +105,9 @@ jobs: run: make cluster/up - name: Install Playwright deps - uses: docker://mcr.microsoft.com/playwright:next-jammy + shell: bash + working-directory: grafana-plugin + run: yarn playwright install # ---------- Expensive e2e tests steps start ----------- - name: Install Go diff --git a/Tiltfile b/Tiltfile index 8077d59526..7fb40ca1b1 100644 --- a/Tiltfile +++ b/Tiltfile @@ -61,9 +61,8 @@ docker_build_sub( def load_oncall_helm(): - helm_oncall_values = ["./dev/helm-local.yml", "./dev/helm-local.dev.yml"] - if is_ci: - helm_oncall_values = helm_oncall_values + ["./.github/helm-ci.yml"] + helm_oncall_values = ["./dev/helm-local.yml"] + helm_oncall_values += ["./.github/helm-ci.yml"] if is_ci else ["./dev/helm-local.dev.yml"] yaml = helm("helm/oncall", name=HELM_PREFIX, values=helm_oncall_values, set=twilio_values, namespace="default") k8s_yaml(yaml) diff --git a/engine/apps/alerts/move_to_migrations_folder_soon_0057_remove_alertgroup_slack_log_message_db.py b/engine/apps/alerts/migrations/0057_remove_alertgroup_slack_log_message_db.py similarity index 100% rename from engine/apps/alerts/move_to_migrations_folder_soon_0057_remove_alertgroup_slack_log_message_db.py rename to engine/apps/alerts/migrations/0057_remove_alertgroup_slack_log_message_db.py diff --git a/engine/apps/chatops_proxy/client.py b/engine/apps/chatops_proxy/client.py index 1c78a21312..8f638d12b1 100644 --- a/engine/apps/chatops_proxy/client.py +++ b/engine/apps/chatops_proxy/client.py @@ -29,6 +29,8 @@ class Tenant: service_tenant_id: str service_type: str cluster_slug: str + stack_id: int + stack_slug: str slack_links: List[SlackLink] = field(default_factory=list) msteams_links: List[MSTeamsLink] = field(default_factory=list) diff --git a/engine/apps/mobile_app/tests/tasks/test_new_shift_swap_request.py b/engine/apps/mobile_app/tests/tasks/test_new_shift_swap_request.py index 888a653fd7..3ef7599244 100644 --- a/engine/apps/mobile_app/tests/tasks/test_new_shift_swap_request.py +++ b/engine/apps/mobile_app/tests/tasks/test_new_shift_swap_request.py @@ -91,6 +91,24 @@ def _timeout(**kwargs): assert _get_shift_swap_requests_to_notify(swap_start) == [] +@pytest.mark.django_db +def test_get_shift_swap_requests_to_notify_deleted_organization( + make_organization, make_user, make_schedule, make_shift_swap_request +): + organization = make_organization() + user = make_user(organization=organization) + schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb) + + swap_start = timezone.now() + swap_end = swap_start + timezone.timedelta(days=1) + make_shift_swap_request( + schedule, user, swap_start=swap_start, swap_end=swap_end, created_at=swap_start - timezone.timedelta(days=27) + ) + + organization.delete() + assert _get_shift_swap_requests_to_notify(swap_start - timezone.timedelta(days=28)) == [] + + @pytest.mark.django_db def test_notify_shift_swap_requests(make_organization, make_user, make_schedule, make_shift_swap_request): organization = make_organization() diff --git a/engine/apps/schedules/models/shift_swap_request.py b/engine/apps/schedules/models/shift_swap_request.py index dde99a7f73..a8d5429bcb 100644 --- a/engine/apps/schedules/models/shift_swap_request.py +++ b/engine/apps/schedules/models/shift_swap_request.py @@ -48,7 +48,9 @@ def hard_delete(self): return self.get_queryset().hard_delete() def get_open_requests(self, now): - return self.get_queryset().filter(benefactor__isnull=True, swap_start__gt=now) + return self.get_queryset().filter( + schedule__organization__deleted_at__isnull=True, benefactor__isnull=True, swap_start__gt=now + ) class ShiftSwapRequest(models.Model): diff --git a/grafana-plugin/e2e-tests/utils/navigation.ts b/grafana-plugin/e2e-tests/utils/navigation.ts index 2c53125ad0..3d8cdbefd9 100644 --- a/grafana-plugin/e2e-tests/utils/navigation.ts +++ b/grafana-plugin/e2e-tests/utils/navigation.ts @@ -2,6 +2,8 @@ import { KeyValue } from '@grafana/data'; import type { Page } from '@playwright/test'; import qs from 'query-string'; +import { getPluginId } from 'utils/consts'; + import { BASE_URL } from './constants'; type OnCallPage = @@ -21,7 +23,7 @@ export const goToGrafanaPage = async (page: Page, url = '') => _goToPage(page, u export const goToOnCallPage = async (page: Page, onCallPage: OnCallPage, queryParams?: KeyValue) => { const queryParamsString = queryParams ? `?${qs.stringify(queryParams)}` : ''; - await _goToPage(page, `/a/grafana-oncall-app/${onCallPage}${queryParamsString}`); + await _goToPage(page, `/a/${getPluginId()}/${onCallPage}${queryParamsString}`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); }; diff --git a/grafana-plugin/package.json b/grafana-plugin/package.json index 7b37bc1f08..a125be49dd 100644 --- a/grafana-plugin/package.json +++ b/grafana-plugin/package.json @@ -13,12 +13,12 @@ "labels:unlink": "yarn --cwd ../../gops-labels/frontend unlink", "test-utc": "TZ=UTC jest --verbose --testNamePattern '^((?!@london-tz).)*$'", "test-london-tz": "TZ=Europe/London jest --verbose --testNamePattern '@london-tz'", - "test": "yarn test-utc && yarn test-london-tz", - "test:ci": "yarn test", + "test": "PLUGIN_ID=grafana-oncall-app yarn test-utc && yarn test-london-tz", + "test:ci": "PLUGIN_ID=grafana-oncall-app pnpm test-utc && pnpm test-london-tz", "test:report": "HTML_REPORT_ENABLED=true yarn test", "test:silent": "yarn test --silent", - "test:e2e": "yarn playwright test --grep-invert @expensive", - "test:e2e-expensive": "yarn playwright test --grep @expensive", + "test:e2e": "PLUGIN_ID=grafana-oncall-app yarn playwright test --grep-invert @expensive", + "test:e2e-expensive": "PLUGIN_ID=grafana-oncall-app yarn playwright test --grep @expensive", "test:e2e:watch": "yarn test:e2e --ui", "test:e2e-expensive:watch": "yarn test:e2e-expensive --ui", "test:e2e:gen": "yarn playwright codegen http://localhost:3000", @@ -56,7 +56,7 @@ "@grafana/eslint-config": "^6.0.0", "@grafana/tsconfig": "^1.2.0-rc1", "@jest/globals": "^27.5.1", - "@playwright/test": "^1.41.0", + "@playwright/test": "1.46.0", "@swc/core": "^1.3.90", "@swc/helpers": "^0.5.0", "@swc/jest": "^0.2.26", @@ -82,7 +82,7 @@ "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.3", "dompurify": "^2.3.12", - "dotenv": "^16.0.3", + "dotenv": "^16.4.0", "eslint": "^8.25.0", "eslint-plugin-deprecation": "^2.0.0", "eslint-plugin-jsdoc": "^44.2.4", diff --git a/grafana-plugin/playwright.config.ts b/grafana-plugin/playwright.config.ts index 42a37f2524..21b75543da 100644 --- a/grafana-plugin/playwright.config.ts +++ b/grafana-plugin/playwright.config.ts @@ -7,9 +7,9 @@ import path from 'path'; */ require('dotenv').config({ path: path.resolve(process.cwd(), 'e2e-tests/.env') }); -export const VIEWER_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/viewer.json'); -export const EDITOR_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/editor.json'); -export const ADMIN_USER_STORAGE_STATE = path.join(__dirname, 'e2e-tests/.auth/admin.json'); +export const VIEWER_USER_STORAGE_STATE = path.join(process.cwd(), 'e2e-tests/.auth/viewer.json'); +export const EDITOR_USER_STORAGE_STATE = path.join(process.cwd(), 'e2e-tests/.auth/editor.json'); +export const ADMIN_USER_STORAGE_STATE = path.join(process.cwd(), 'e2e-tests/.auth/admin.json'); const IS_CI = !!process.env.CI; const BROWSERS = process.env.BROWSERS || 'chromium'; diff --git a/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx b/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx index dee6d5f65b..6a28e61d2c 100644 --- a/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx +++ b/grafana-plugin/src/containers/RotationForm/ShiftSwapForm.tsx @@ -40,6 +40,7 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => { const { scheduleStore, userStore: { currentUserPk }, + timezoneStore: { selectedTimezoneOffset }, } = store; useEffect(() => { @@ -87,7 +88,7 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => { ...shiftSwap, }); } - }, [shiftSwap, store.timezoneStore.calendarStartDate, store.timezoneStore.selectedTimezoneOffset]); + }, [shiftSwap, store.timezoneStore.calendarStartDate, selectedTimezoneOffset]); const handleDescriptionChange = useCallback( (event) => { @@ -171,6 +172,7 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => { @@ -178,6 +180,7 @@ export const ShiftSwapForm = (props: ShiftSwapFormProps) => { diff --git a/grafana-plugin/src/pages/schedule/Schedule.tsx b/grafana-plugin/src/pages/schedule/Schedule.tsx index a5a17f2317..396144de6a 100644 --- a/grafana-plugin/src/pages/schedule/Schedule.tsx +++ b/grafana-plugin/src/pages/schedule/Schedule.tsx @@ -47,7 +47,12 @@ import { isUserActionAllowed, UserActions } from 'utils/authorization/authorizat import { PLUGIN_ROOT } from 'utils/consts'; import { PropsWithRouter, withRouter } from 'utils/hoc'; -import { getCalendarStartDate, getNewCalendarStartDate, getUTCString } from './Schedule.helpers'; +import { + getCalendarStartDate, + getNewCalendarStartDate, + getUTCString, + toDateWithTimezoneOffset, +} from './Schedule.helpers'; import { getScheduleStyles } from './Schedule.styles'; interface RouteProps { @@ -647,7 +652,7 @@ class _SchedulePage extends React.Component - `${includePrefix ? `${getPluginId()}.` : ''}${resource}:${action}`; + `${includePrefix ? `${PluginId.OnCall}.` : ''}${resource}:${action}`; const constructAction = ( resource: Resource, diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 2924c388ac..d21d683824 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -19,7 +19,7 @@ export const getIsDevelopmentEnv = () => { export const getPluginId = (): PluginId => { try { - return (process.env.PLUGIN_ID as PluginId) || PluginId.OnCall; + return (process.env.PLUGIN_ID as PluginId) || PluginId.Irm; } catch (error) { return PluginId.Irm; } diff --git a/grafana-plugin/yarn.lock b/grafana-plugin/yarn.lock index 551fae5921..5e8850968f 100644 --- a/grafana-plugin/yarn.lock +++ b/grafana-plugin/yarn.lock @@ -2397,12 +2397,12 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@playwright/test@^1.41.0": - version "1.41.2" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.41.2.tgz#bd9db40177f8fd442e16e14e0389d23751cdfc54" - integrity sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg== +"@playwright/test@1.46.0": + version "1.46.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.0.tgz#ccea6d22c40ee7fa567e4192fafbdf2a907e2714" + integrity sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w== dependencies: - playwright "1.41.2" + playwright "1.46.0" "@pnpm/constants@7.1.1": version "7.1.1" @@ -6456,10 +6456,10 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@^16.0.3: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== +dotenv@^16.4.0: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== duplexer@^0.1.2: version "0.1.2" @@ -11527,17 +11527,17 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -playwright-core@1.41.2: - version "1.41.2" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.2.tgz#db22372c708926c697acc261f0ef8406606802d9" - integrity sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA== +playwright-core@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.0.tgz#2336ac453a943abf0dc95a76c117f9d3ebd390eb" + integrity sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A== -playwright@1.41.2: - version "1.41.2" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.2.tgz#4e760b1c79f33d9129a8c65cc27953be6dd35042" - integrity sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A== +playwright@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.0.tgz#c7ff490deae41fc1e814bf2cb62109dd9351164d" + integrity sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw== dependencies: - playwright-core "1.41.2" + playwright-core "1.46.0" optionalDependencies: fsevents "2.3.2" @@ -13967,16 +13967,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14094,7 +14085,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -14115,13 +14106,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -15477,7 +15461,8 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -15495,15 +15480,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"