From 5e30e3ef12a217e8b6fe893ee1658ed09996feb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=A9o=20M=C3=A9vollon?= <38255502+matmut7@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:07:48 +0100 Subject: [PATCH] feat: disabling feature for Github, OVH and Mattermost services (#565) * feat: disabling feature for Github service * mattermost * change api route * remove old file * import * ovh * return in disabled endpoint * enable users for which we receive an account * dead code comment * refactor: disable accounts not users * frontend * tests * webhook leave disabled user * disable github webhook * error response * config talisman * merge lockfile * add react-toastify * use toasts * migrate to vitest * handle some services * chore: improve sentry setup (#603) * lock version * lock version * remove debug log * remove email * revert file formatting * fix: unused var * fix: refresh data after request submit * fix: sync onboarding request and form hooks * chore: fix nextjs build * chore: fix buttons width * chore: add modal max width * chore: remove comment * chore: remove useless async --------- Co-authored-by: Gary van Woerkens --- .gitignore | 3 + .../dev/templates/sentry.sealed-secret.yaml | 15 + .../templates/sentry.sealed-secret.yaml | 15 + .../prod/templates/sentry.sealed-secret.yaml | 16 + .kontinuous/env/prod/values.yaml | 1 - .kontinuous/values.yaml | 10 +- .talismanrc | 82 +- Dockerfile | 26 +- ...ts.test.js.snap => accounts.test.jsx.snap} | 6 +- ...index.test.js.snap => index.test.jsx.snap} | 6 +- ....test.js.snap => onboarding.test.jsx.snap} | 2 +- __tests__/accounts.test.js | 25 - __tests__/accounts.test.jsx | 19 + __tests__/api/onboarding/confirm.test.js | 35 +- __tests__/api/onboarding/request.test.js | 27 +- __tests__/api/onboarding/review.test.js | 41 +- __tests__/api/revoke.test.js | 67 - __tests__/api/sync.test.js | 7 +- ...oter.test.js.snap => footer.test.jsx.snap} | 2 +- ...ader.test.js.snap => header.test.jsx.snap} | 6 +- ...yout.test.js.snap => layout.test.jsx.snap} | 2 +- ...login.test.js.snap => login.test.jsx.snap} | 2 +- ...arch.test.js.snap => search.test.jsx.snap} | 2 +- .../__snapshots__/user-profile.test.js.snap | 213 - .../__snapshots__/user-profile.test.jsx.snap | 414 ++ ...users.test.js.snap => users.test.jsx.snap} | 224 +- ...alert.test.js.snap => alert.test.jsx.snap} | 2 +- ...badge.test.js.snap => badge.test.jsx.snap} | 2 +- ...ader.test.js.snap => loader.test.jsx.snap} | 2 +- ...est.js.snap => service-logo.test.jsx.snap} | 2 +- ...o.test.js.snap => site-logo.test.jsx.snap} | 2 +- .../common/{alert.test.js => alert.test.jsx} | 2 + .../common/{badge.test.js => badge.test.jsx} | 2 + .../{loader.test.js => loader.test.jsx} | 2 + ...ice-logo.test.js => service-logo.test.jsx} | 2 + .../{site-logo.test.js => site-logo.test.jsx} | 2 + .../{footer.test.js => footer.test.jsx} | 1 + .../{header.test.js => header.test.jsx} | 8 +- .../{layout.test.js => layout.test.jsx} | 14 +- .../{login.test.js => login.test.jsx} | 2 + .../{search.test.js => search.test.jsx} | 2 + ...-profile.test.js => user-profile.test.jsx} | 4 +- .../{users.test.js => users.test.jsx} | 2 + __tests__/{index.test.js => index.test.jsx} | 13 +- ...onboarding.test.js => onboarding.test.jsx} | 23 +- __tests__/services/fetchers/github.test.js | 5 +- __tests__/services/fetchers/matomo.test.js | 3 +- .../services/fetchers/mattermost.test.js | 3 +- __tests__/services/fetchers/nextcloud.test.js | 3 +- __tests__/services/fetchers/ovh.test.js | 5 +- __tests__/services/fetchers/sentry.test.js | 3 +- __tests__/services/fetchers/zammad.test.js | 3 +- __tests__/services/onboard.test.js | 5 +- __tests__/services/revoke.test.js | 124 - __tests__/services/sync.test.js | 59 +- __tests__/utils/SERVICES.test.js | 1 + __tests__/utils/log-action.test.js | 3 +- __tests__/utils/ovh.test.js | 35 +- __tests__/utils/rest-fetcher.test.js | 1 + jest.config.js | 30 - jest.setup.js | 45 - next.config.js | 25 +- package.json | 12 +- packages/hasura/metadata/actions.graphql | 32 +- packages/hasura/metadata/actions.yaml | 39 +- .../default/tables/public_services.yaml | 3 +- .../default/tables/public_users.yaml | 2 + .../down.sql | 4 + .../up.sql | 2 + sentry.client.config.js | 18 +- sentry.edge.config.js | 18 +- sentry.server.config.js | 18 +- src/components/common/toast-provider.tsx | 15 + src/components/common/wizard/index.tsx | 7 +- src/components/onboarding/index.tsx | 1 + src/components/users/delete-account-modal.tsx | 85 - src/components/users/index.tsx | 41 +- src/components/users/toggle-account-modal.tsx | 75 + src/components/users/user-profile.tsx | 8 +- src/components/users/user-selected.tsx | 6 +- src/components/users/user-service-info.tsx | 49 +- src/components/users/user-services.tsx | 15 +- src/hooks/use-onboarding.ts | 11 +- src/hooks/use-users.ts | 53 +- src/mocks/handlers.js | 197 +- src/pages/_app.tsx | 2 + src/pages/api/accounts/disable.ts | 101 + src/pages/api/accounts/enable.ts | 111 + src/pages/api/revoke.ts | 39 - src/pages/api/webhooks/github.ts | 137 +- src/queries/index.ts | 65 +- src/services/disablers/github.ts | 15 + src/services/disablers/mattermost.ts | 13 + src/services/disablers/ovh.ts | 11 + src/services/enablers/github.ts | 18 + src/services/enablers/mattermost.ts | 14 + src/services/enablers/ovh.ts | 20 + src/services/revoke.ts | 189 - src/services/send-email.ts | 13 + src/services/sync.ts | 16 +- src/styles/components/modal.scss | 1 + src/types/index.d.ts | 8 + src/utils/SERVICES.ts | 2 + tsconfig.json | 16 +- vitest.config.mjs | 17 + vitest.setup.js | 38 + yarn.lock | 4408 ++++++++++------- 107 files changed, 4207 insertions(+), 3468 deletions(-) create mode 100644 .kontinuous/env/dev/templates/sentry.sealed-secret.yaml create mode 100644 .kontinuous/env/preprod/templates/sentry.sealed-secret.yaml create mode 100644 .kontinuous/env/prod/templates/sentry.sealed-secret.yaml rename __tests__/__snapshots__/{accounts.test.js.snap => accounts.test.jsx.snap} (98%) rename __tests__/__snapshots__/{index.test.js.snap => index.test.jsx.snap} (86%) rename __tests__/__snapshots__/{onboarding.test.js.snap => onboarding.test.jsx.snap} (98%) delete mode 100644 __tests__/accounts.test.js create mode 100644 __tests__/accounts.test.jsx delete mode 100644 __tests__/api/revoke.test.js rename __tests__/components/__snapshots__/{footer.test.js.snap => footer.test.jsx.snap} (95%) rename __tests__/components/__snapshots__/{header.test.js.snap => header.test.jsx.snap} (93%) rename __tests__/components/__snapshots__/{layout.test.js.snap => layout.test.jsx.snap} (97%) rename __tests__/components/__snapshots__/{login.test.js.snap => login.test.jsx.snap} (80%) rename __tests__/components/__snapshots__/{search.test.js.snap => search.test.jsx.snap} (99%) delete mode 100644 __tests__/components/__snapshots__/user-profile.test.js.snap create mode 100644 __tests__/components/__snapshots__/user-profile.test.jsx.snap rename __tests__/components/__snapshots__/{users.test.js.snap => users.test.jsx.snap} (86%) rename __tests__/components/common/__snapshots__/{alert.test.js.snap => alert.test.jsx.snap} (94%) rename __tests__/components/common/__snapshots__/{badge.test.js.snap => badge.test.jsx.snap} (89%) rename __tests__/components/common/__snapshots__/{loader.test.js.snap => loader.test.jsx.snap} (84%) rename __tests__/components/common/__snapshots__/{service-logo.test.js.snap => service-logo.test.jsx.snap} (98%) rename __tests__/components/common/__snapshots__/{site-logo.test.js.snap => site-logo.test.jsx.snap} (81%) rename __tests__/components/common/{alert.test.js => alert.test.jsx} (95%) rename __tests__/components/common/{badge.test.js => badge.test.jsx} (95%) rename __tests__/components/common/{loader.test.js => loader.test.jsx} (93%) rename __tests__/components/common/{service-logo.test.js => service-logo.test.jsx} (93%) rename __tests__/components/common/{site-logo.test.js => site-logo.test.jsx} (90%) rename __tests__/components/{footer.test.js => footer.test.jsx} (85%) rename __tests__/components/{header.test.js => header.test.jsx} (81%) rename __tests__/components/{layout.test.js => layout.test.jsx} (67%) rename __tests__/components/{login.test.js => login.test.jsx} (84%) rename __tests__/components/{search.test.js => search.test.jsx} (85%) rename __tests__/components/{user-profile.test.js => user-profile.test.jsx} (90%) rename __tests__/components/{users.test.js => users.test.jsx} (94%) rename __tests__/{index.test.js => index.test.jsx} (59%) rename __tests__/{onboarding.test.js => onboarding.test.jsx} (59%) delete mode 100644 __tests__/services/revoke.test.js delete mode 100644 jest.config.js delete mode 100644 jest.setup.js create mode 100644 packages/hasura/migrations/default/1691156416829_alter_table_public_users_add_column_disabled/down.sql create mode 100644 packages/hasura/migrations/default/1691156416829_alter_table_public_users_add_column_disabled/up.sql create mode 100644 src/components/common/toast-provider.tsx delete mode 100644 src/components/users/delete-account-modal.tsx create mode 100644 src/components/users/toggle-account-modal.tsx create mode 100644 src/pages/api/accounts/disable.ts create mode 100644 src/pages/api/accounts/enable.ts delete mode 100644 src/pages/api/revoke.ts create mode 100644 src/services/disablers/github.ts create mode 100644 src/services/disablers/mattermost.ts create mode 100644 src/services/disablers/ovh.ts create mode 100644 src/services/enablers/github.ts create mode 100644 src/services/enablers/mattermost.ts create mode 100644 src/services/enablers/ovh.ts delete mode 100644 src/services/revoke.ts create mode 100644 vitest.config.mjs create mode 100644 vitest.setup.js diff --git a/.gitignore b/.gitignore index bb8740498..ac00e599d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ coverage !.yarn/releases !.yarn/sdks !.yarn/versions + +# Sentry Config File +.sentryclirc diff --git a/.kontinuous/env/dev/templates/sentry.sealed-secret.yaml b/.kontinuous/env/dev/templates/sentry.sealed-secret.yaml new file mode 100644 index 000000000..d5c5cabbd --- /dev/null +++ b/.kontinuous/env/dev/templates/sentry.sealed-secret.yaml @@ -0,0 +1,15 @@ +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + annotations: + sealedsecrets.bitnami.com/cluster-wide: 'true' + name: sentry +spec: + encryptedData: + SENTRY_AUTH_TOKEN: AgB0+vei/m71QPjqmuo6v9pfgHRy8U9LCuVK2PHzkH5ul8tmgs04r2afwRs877iu+EP4NB6OtjXKG6AFCFmPusi6p35xl/IpqjdA/GlBC9Vwx914YDd46Hn+QoVqqDMmtvNxfuMxuxs5Zwro/5tMJMb6sU31Qyl/8p4Rne18EA2gje/WVK1a91HCb75o2sB0t/M5HgP+oP8Fj1yw1sqbIbnxHaIn4arf7NTEB3DSGrL4NpLtNN1ToQfL8UXH9vCmbLUjinHVSDm5Fkd67hPBqen0HkIdBIheU+LU7ygiiA5Bb470IuKxV9BuCey2CxSKDVNZBDfLuQAZ3uqF8plBoEtPLCeZ4Tda6KOvTnQ72Maj1YE3CLdOaLMtKQTi/Lt3ZcoBJvUEbGck+hmpEQq3QjwZMhmba8ZZX4Y8ymVpQd/LoJYhBM0ftel4kpK4oBlYEC612phPa+d+LywVkv+Vb3QQX4VOynAWhiuS+larg27H3dZ95kHZlzEgKdnUJ0WxfupoZEnyE7EXQ9aA1b+6Jg8GvCFoSCRuBG3WM7VmTCCzI7n/yjbKHTaPRqcLKA0pgvYNqyKiBemuKCRb+i/gzK8LqsdO8bJb3ZYl/H7ysqdWTvz0JLblg6lIMd/dDDjTJ5CWjF1q/RZWKiWigarirlNgnYdkK2iRaQ666KBRb3DoiexpAiBxx2iAsUDLWiVdHsZ8g9HB5xIbZTbyEuJZ56cKywwZvoTZNHXCKY2Ki74ArsKu9H9NiOwf9shgoDghXQc5AZskxzL7wkOyqhSvpjKC + template: + metadata: + annotations: + sealedsecrets.bitnami.com/cluster-wide: 'true' + name: sentry + type: Opaque diff --git a/.kontinuous/env/preprod/templates/sentry.sealed-secret.yaml b/.kontinuous/env/preprod/templates/sentry.sealed-secret.yaml new file mode 100644 index 000000000..d5c5cabbd --- /dev/null +++ b/.kontinuous/env/preprod/templates/sentry.sealed-secret.yaml @@ -0,0 +1,15 @@ +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + annotations: + sealedsecrets.bitnami.com/cluster-wide: 'true' + name: sentry +spec: + encryptedData: + SENTRY_AUTH_TOKEN: AgB0+vei/m71QPjqmuo6v9pfgHRy8U9LCuVK2PHzkH5ul8tmgs04r2afwRs877iu+EP4NB6OtjXKG6AFCFmPusi6p35xl/IpqjdA/GlBC9Vwx914YDd46Hn+QoVqqDMmtvNxfuMxuxs5Zwro/5tMJMb6sU31Qyl/8p4Rne18EA2gje/WVK1a91HCb75o2sB0t/M5HgP+oP8Fj1yw1sqbIbnxHaIn4arf7NTEB3DSGrL4NpLtNN1ToQfL8UXH9vCmbLUjinHVSDm5Fkd67hPBqen0HkIdBIheU+LU7ygiiA5Bb470IuKxV9BuCey2CxSKDVNZBDfLuQAZ3uqF8plBoEtPLCeZ4Tda6KOvTnQ72Maj1YE3CLdOaLMtKQTi/Lt3ZcoBJvUEbGck+hmpEQq3QjwZMhmba8ZZX4Y8ymVpQd/LoJYhBM0ftel4kpK4oBlYEC612phPa+d+LywVkv+Vb3QQX4VOynAWhiuS+larg27H3dZ95kHZlzEgKdnUJ0WxfupoZEnyE7EXQ9aA1b+6Jg8GvCFoSCRuBG3WM7VmTCCzI7n/yjbKHTaPRqcLKA0pgvYNqyKiBemuKCRb+i/gzK8LqsdO8bJb3ZYl/H7ysqdWTvz0JLblg6lIMd/dDDjTJ5CWjF1q/RZWKiWigarirlNgnYdkK2iRaQ666KBRb3DoiexpAiBxx2iAsUDLWiVdHsZ8g9HB5xIbZTbyEuJZ56cKywwZvoTZNHXCKY2Ki74ArsKu9H9NiOwf9shgoDghXQc5AZskxzL7wkOyqhSvpjKC + template: + metadata: + annotations: + sealedsecrets.bitnami.com/cluster-wide: 'true' + name: sentry + type: Opaque diff --git a/.kontinuous/env/prod/templates/sentry.sealed-secret.yaml b/.kontinuous/env/prod/templates/sentry.sealed-secret.yaml new file mode 100644 index 000000000..c3a7ce207 --- /dev/null +++ b/.kontinuous/env/prod/templates/sentry.sealed-secret.yaml @@ -0,0 +1,16 @@ +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + annotations: + sealedsecrets.bitnami.com/namespace-wide: 'true' + name: sentry + namespace: secretariat +spec: + encryptedData: + SENTRY_AUTH_TOKEN: AgCg6EpuN96oZQ1zbCfciq9w63htn3GtqMwQB5COwLocovcJTk42Z3c1aDp7zkZpDS0rVgUt5zcQuEVoWoZExQOyfmQPxRbFGQnXqbFGYTcekc9mZ1qRTr9Vzl26YQ7OsVegEv+tCX5Njzgm/vrGPHQCQBZQKtVNSzH5ihhu79UNS1G5U6gXhlYzOFEAjQAzKP9UfHcd1lkXfVx9bm0dpnJPyWPjMyJvC7RfAd9SGNJwDuGsGeUH/zbn5XuLHblqV6XvUD4YX/9cBxMvCzcHAwQCrruTxeogoLRXAzWXZye/RqSV/AQ4fPRP0fK+rxzF6sOumP3T7Zmb31/Rb0zv0kozbavdhcsX71nZbMUXJVFNuTqpT24JoMSJnsc/U9vNyya65IDjs18BVsJl2+x01Oc143W/irLzilh7lNriP+YqUcpjoZK9RHtNfyNTJ8ffFd/M3wlvE55WAmTvSzcTivV8oubvM3nREUDihaMGwRSsW24d84Gr3VBM8KlZlm7XEIazmeS7Xt62w/Xgj88b7UWP0+YLH5PlIZnMnoQPVDhnyAOIMAGmp97VVw4AogfuaqZqgQkn+LCTC/DTz68u7vpWSsNRxw835CxaEKB55GT4WpgtGbAvttufeDfvMMrPNswFv8uoI4Ae0a/lb5kKjZOlZIF/dYxu4vJScUFkic6df78NBMLIoX9aB5U/PQPOBLl92vlGDrPzckq+oQMDZmdT5rHPeogjFHHi3AQQOd3gqacq1TV8RGPGryK5vZu4c4Ddop2FK2roqqRabIwlzSI/ + template: + metadata: + annotations: + sealedsecrets.bitnami.com/namespace-wide: 'true' + name: sentry + type: Opaque diff --git a/.kontinuous/env/prod/values.yaml b/.kontinuous/env/prod/values.yaml index 5fc4e244b..5a96a8865 100644 --- a/.kontinuous/env/prod/values.yaml +++ b/.kontinuous/env/prod/values.yaml @@ -31,6 +31,5 @@ jobs: with: imagePackage: app buildArgs: - NEXT_PUBLIC_HASURA_URL: "https://hasura-{{ .Values.global.host }}/v1/graphql" NEXT_PUBLIC_MATOMO_SITE_ID: "82" NEXT_PUBLIC_MATOMO_URL: "https://matomo.fabrique.social.gouv.fr/" diff --git a/.kontinuous/values.yaml b/.kontinuous/values.yaml index b5c3a2481..99b391bc6 100644 --- a/.kontinuous/values.yaml +++ b/.kontinuous/values.yaml @@ -28,7 +28,15 @@ jobs: with: imagePackage: app buildArgs: - NEXT_PUBLIC_HASURA_URL: "https://hasura-{{ .Values.global.host }}/v1/graphql" + NEXT_PUBLIC_HASURA_URL: + "https://hasura-{{ .Values.global.host }}/v1/graphql" + NEXT_PUBLIC_SENTRY_DSN: "https://1fc6ed945722444583d4238af34346a9@sentry.fabrique.social.gouv.fr/85" + NEXT_PUBLIC_SENTRY_ENVIRONMENT: "{{ .Values.global.env }}" + NEXT_PUBLIC_SENTRY_RELEASE: "{{ .Values.global.imageTag }}" + secrets: + sentry_auth_token: + secretName: sentry + secretKey: SENTRY_AUTH_TOKEN build-hasura: use: build diff --git a/.talismanrc b/.talismanrc index b8fca83bb..10655c8bd 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,66 +1,28 @@ fileignoreconfig: -- filename: .github/workflows/deactivate.yaml - checksum: e0f0138068d1f9a11721ad38ff8b892f8dfbf19854f741fe60ae1d7f291fff22 -- filename: .github/workflows/preproduction.yaml - checksum: 852a71100a6bf940df9c77fb911b35e59ab280a4febfec67811f9d0a7f8f4676 -- filename: .github/workflows/production.yaml - checksum: 7ae9af137671722308557d0881493f6c0302f6c44a9753696129403930d6169b -- filename: .github/workflows/review-auto.yaml - checksum: 3e14e4a3020b4f6e749ab6e607e08b1f8ea3c7cb445907e843d99909fc1dbb65 -- filename: .github/workflows/review.yaml - checksum: 3209ade741c7176f01de5a3a3cdd3d8b852bd68c45755c785dcac68845b30405 -- filename: .kontinuous/env/dev/templates/app.sealed-secret.yaml - checksum: fa80c5622c002c66ac217ec37a6f6b33935d42e98fcef60a008a234609c13344 -- filename: .kontinuous/env/dev/values.yaml - checksum: 8e57dfc5e7e7fa5e4d6ca8a2174e2ea88ec66b80e165b705a442bc756e8c1a76 -- filename: .kontinuous/env/preprod/templates/app.sealed-secret.yaml - checksum: 45302a0bf5121715c7abab561bdcaab85e67f57f4deb932aeeed383292c0cc4b -- filename: .kontinuous/env/preprod/templates/charon.sealed-secret.yaml - checksum: ed2e8250214389ddda47d74a8e27defd392245c4d64966240d6e8a6cb9bbe573 -- filename: .kontinuous/env/prod/templates/cnpg-backup-credentials.yaml - checksum: 7eb76edfc4548e0973aa159fb11ca2a1887a114d00c32f498ad50c2f508b91f5 -- filename: .kontinuous/env/prod/values.yaml - checksum: 832641aa7d11d88e5f5fb3b94fdf82587bc086ba77433a20b670caf67c8945ea -- filename: .yarn/plugins/@yarnpkg/plugin-fetch.cjs - checksum: d5c4610f916094d84cfcd7a372f3a9fb4cf5af733488bbe04684c384d846f53a -- filename: __tests__/components/__snapshots__/users.test.js.snap - checksum: 690d463633eb908fa7ed3eade9597d8aa9ded3c5d892206e3837596e35d88cd4 -- filename: next.config.js - checksum: 855b09fc06f8388ba51dccbaaf7c76375f45c36a89ace9b51cd665d290f1d9f4 -- filename: packages/hasura/migrations/default/1696938677969_alter_table_public_users_add_column_email/down.sql - checksum: 1e47fbc2dccbf99c13549c8d6e669c54815be24d7e42f0edd78219377fdc63f4 -- filename: packages/hasura/migrations/default/1696938677969_alter_table_public_users_add_column_email/up.sql - checksum: c97a7b91dc05e94fca2ba43f8bc4bb743450cdbb552175bb0c10fee01fe86882 -- filename: packages/hasura/migrations/default/1696938751031_alter_table_public_users_add_column_onboardind_request_id/down.sql - checksum: 3f27790c9d3f5c08440f56b3d238f90014352ecf5967b80a93a910459aa6e7b8 -- filename: packages/hasura/migrations/default/1696938751031_alter_table_public_users_add_column_onboardind_request_id/up.sql - checksum: 3dbfd37ba006e639f12a4c375fa44626a04c27e2bb5d4356a1782576fff9b199 -- filename: packages/hasura/migrations/default/1696938764795_alter_table_public_users_alter_column_email/down.sql - checksum: cc4f4de24bd74f2be73cbdc45f562a3d485826bcc2189b1becce143cde11ccb2 -- filename: packages/hasura/migrations/default/1696938764795_alter_table_public_users_alter_column_email/up.sql - checksum: 7fc715964fdbc2fdd70a5316e0ec68dfc0f8255869682ec6abf31b2b71ce4948 -- filename: packages/hasura/migrations/default/1696938864227_alter_table_public_users_alter_column_onboardind_request_id/down.sql - checksum: d4a7960f08d3af47c48d30cb3436fdb1739b38078bce92e18f394e2ea4bf9a86 -- filename: packages/hasura/migrations/default/1696938864227_alter_table_public_users_alter_column_onboardind_request_id/up.sql - checksum: 623d5d32a6aabc5790d7ae05656b340f8a6c8a55e2d99b767194b98f4bf080f2 -- filename: sentry.client.config.js - checksum: c099a2c12302135ea90b784b7f17827d985de03d2b667750551cb9f8cde0ea81 -- filename: sentry.edge.config.js - checksum: 1944a74030b8a786b01906e52ee2d9c1304d3867aebe394df7828e98f057bce4 -- filename: sentry.server.config.js - checksum: c45afaf7fe0822f1bf966627a0e5e166468d27f6d5151229990f293b37d4302b -- filename: src/components/onboarding/steps/services-accounts-statuses.tsx - checksum: b118715f8269bd92790bbe4019159567893d2517cad7ef2a639e456d924b88ca +- filename: .kontinuous/env/dev/templates/sentry.sealed-secret.yaml + checksum: 6b3eb493305a8c3d24402c99d7a20b01b6650e7fedb2782648c539a70adc4088 +- filename: .kontinuous/env/preprod/templates/sentry.sealed-secret.yaml + checksum: fae95a0f658c5920845516eb04659cac883418d135a4bce54615236e47da8df1 +- filename: .kontinuous/env/prod/templates/sentry.sealed-secret.yaml + checksum: ef7c23320a02ca66f8a10755bccabc2a487973e9581a4408ed3f4c8685f921f7 +- filename: .kontinuous/values.yaml + checksum: 1d67b52046f6e850ff38f806000aa8bb1329cb1f7e6314fbec89bea2009709e3 +- filename: Dockerfile + checksum: 14c30aedc55e0f61364c26cf84c28596973f6951f4e0322f2f2693ec84ff513c +- filename: packages/hasura/migrations/default/1691156416829_alter_table_public_users_add_column_disabled/down.sql + checksum: cac3e070c06c7c7fbd6821009fef546e452a38184ac6539ccca609be4a82bd8b +- filename: packages/hasura/migrations/default/1691156416829_alter_table_public_users_add_column_disabled/up.sql + checksum: 390c88e78b3e8bd3153e028403a3ffea2b5da636eeab22c307788cbddb86e096 +- filename: src/components/users/toggle-account-modal.tsx + checksum: 2d0bd58b461814a6d2d934d96441ed7962a74f2d184e2581b38b4990562dde70 - filename: src/hooks/use-onboarding.ts - checksum: 84ace93b2abe1dbb750a311a6cee984e9b0392be6176871e7e49d7cdf948cef0 + checksum: 004c4c33f375813231a10a7b6399cb7a1c03128ae6c84c6af5fefabff9cd8a81 +- filename: src/services/disablers/ovh.ts + checksum: 390c9e51cf01b5db14a4af94a4182c3d55ccddeb7905462b21b0a5f23b8d24e9 +- filename: src/services/enablers/ovh.ts + checksum: 5e2adf5acd6e8f769a465b573cd9149226526bbce8402abf3c0cc8d6f5520a3a - filename: src/services/send-email.ts - checksum: f8e6673f08300673626ad8ce310cfce4037f5feec086d4ce5808e40c09766992 -- filename: src/services/sync.ts - checksum: ae6aef4a72d66a3ad64e55a6a9bdd96dea9cdcb55809d1ac1b8bc7adb3d99b07 -- filename: src/utils/env.ts - checksum: d7acba9f2c4fb7fc16b2d3b35440e9f0105d299a3b3b499f912b580e7096d25d -- filename: yarn.lock - checksum: b9128943e69440b534f82c27472f838bfea5567d0af222e4e681746eb74a9486 + checksum: 0562c88a33c18be479847c12d3c05dfc00b6edea18ffd61b97bc389536d41cb1 scopeconfig: - scope: node version: "1.0" diff --git a/Dockerfile b/Dockerfile index 020dd58fe..eec38d401 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,22 +4,32 @@ WORKDIR /app # Rebuild the source code only when needed FROM base AS builder + +# install deps +COPY yarn.lock .yarnrc.yml ./ +COPY .yarn .yarn +RUN yarn fetch --immutable + +COPY . . + +# build time args ARG NEXT_PUBLIC_HASURA_URL ENV NEXT_PUBLIC_HASURA_URL $NEXT_PUBLIC_HASURA_URL ARG NEXT_PUBLIC_MATOMO_URL ENV NEXT_PUBLIC_MATOMO_URL $NEXT_PUBLIC_MATOMO_URL ARG NEXT_PUBLIC_MATOMO_SITE_ID ENV NEXT_PUBLIC_MATOMO_SITE_ID $NEXT_PUBLIC_MATOMO_SITE_ID +ARG NEXT_PUBLIC_SENTRY_ENVIRONMENT +ENV NEXT_PUBLIC_SENTRY_ENVIRONMENT $NEXT_PUBLIC_SENTRY_ENVIRONMENT +ARG NEXT_PUBLIC_SENTRY_RELEASE +ENV NEXT_PUBLIC_SENTRY_RELEASE $NEXT_PUBLIC_SENTRY_RELEASE +ARG NEXT_PUBLIC_SENTRY_DSN +ENV NEXT_PUBLIC_SENTRY_DSN $NEXT_PUBLIC_SENTRY_DSN ENV NEXT_TELEMETRY_DISABLED 1 -# install deps -COPY yarn.lock .yarnrc.yml ./ -COPY .yarn .yarn -RUN yarn fetch --immutable - # build -COPY . . -RUN yarn build +RUN --mount=type=secret,id=sentry_auth_token export SENTRY_AUTH_TOKEN="$(cat /run/secrets/sentry_auth_token)"; \ + yarn build # Production image, copy all the files and run next FROM base AS runner @@ -30,11 +40,9 @@ ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs -# You only need to copy next.config.js if you are NOT using the default configuration COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public -# Automatically leverage output traces to reduce image size COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static diff --git a/__tests__/__snapshots__/accounts.test.js.snap b/__tests__/__snapshots__/accounts.test.jsx.snap similarity index 98% rename from __tests__/__snapshots__/accounts.test.js.snap rename to __tests__/__snapshots__/accounts.test.jsx.snap index 66dd14dc5..95214bc90 100644 --- a/__tests__/__snapshots__/accounts.test.js.snap +++ b/__tests__/__snapshots__/accounts.test.jsx.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`when accounts page renders displays login form if session is invalid 1`] = ` +exports[`displays login form if session is invalid 1`] = `
`; -exports[`when accounts page renders displays users list if session is valid 1`] = ` +exports[`displays users list if session is valid 1`] = `
@@ -60,8 +60,8 @@ exports[`renders home page 1`] = ` data-nimg="fill" decoding="async" sizes="100vw" - src="/_next/image?url=%2Fimg.jpg&w=3840&q=75" - srcset="/_next/image?url=%2Fimg.jpg&w=640&q=75 640w, /_next/image?url=%2Fimg.jpg&w=750&q=75 750w, /_next/image?url=%2Fimg.jpg&w=828&q=75 828w, /_next/image?url=%2Fimg.jpg&w=1080&q=75 1080w, /_next/image?url=%2Fimg.jpg&w=1200&q=75 1200w, /_next/image?url=%2Fimg.jpg&w=1920&q=75 1920w, /_next/image?url=%2Fimg.jpg&w=2048&q=75 2048w, /_next/image?url=%2Fimg.jpg&w=3840&q=75 3840w" + src="/public/images/home.svg" + srcset="/public/images/home.svg 640w, /public/images/home.svg 750w, /public/images/home.svg 828w, /public/images/home.svg 1080w, /public/images/home.svg 1200w, /public/images/home.svg 1920w, /public/images/home.svg 2048w, /public/images/home.svg 3840w" style="position: absolute; top: 0px; left: 0px; bottom: 0px; right: 0px; box-sizing: border-box; padding: 0px; margin: auto; display: block; width: 0px; height: 0px; min-width: 100%; max-width: 100%; min-height: 100%; max-height: 100%;" /> diff --git a/__tests__/__snapshots__/onboarding.test.js.snap b/__tests__/__snapshots__/onboarding.test.jsx.snap similarity index 98% rename from __tests__/__snapshots__/onboarding.test.js.snap rename to __tests__/__snapshots__/onboarding.test.jsx.snap index c8b0ca58a..fa27a587c 100644 --- a/__tests__/__snapshots__/onboarding.test.js.snap +++ b/__tests__/__snapshots__/onboarding.test.jsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`renders onboarding page 1`] = `
diff --git a/__tests__/accounts.test.js b/__tests__/accounts.test.js deleted file mode 100644 index 5640c0a7e..000000000 --- a/__tests__/accounts.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { render } from "@testing-library/react" -import { useSession } from "next-auth/react" - -import Accounts from "../src/pages/accounts" -import { session } from "../src/mocks/data" - -describe("when accounts page renders", () => { - it("displays login form if session is invalid", () => { - useSession.mockImplementation(() => ({ - data: null, - status: "unauthenticated", - })) - const { container } = render() - expect(container).toMatchSnapshot() - }) - - it("displays users list if session is valid", () => { - useSession.mockImplementation(() => ({ - data: session, - status: "authenticated", - })) - const { container } = render() - expect(container).toMatchSnapshot() - }) -}) diff --git a/__tests__/accounts.test.jsx b/__tests__/accounts.test.jsx new file mode 100644 index 000000000..57149b65e --- /dev/null +++ b/__tests__/accounts.test.jsx @@ -0,0 +1,19 @@ +import { render } from "@testing-library/react" +import Accounts from "../src/pages/accounts" +import { vi, it, expect } from "vitest" +import { session } from "../src/mocks/data" + +const useSession = vi.hoisted(() => vi.fn()) +vi.mock("next-auth/react", () => ({ useSession })) + +it("displays login form if session is invalid", () => { + useSession.mockReturnValue({ data: null, status: "unauthenticated" }) + const { container } = render() + expect(container).toMatchSnapshot() +}) + +it("displays users list if session is valid", () => { + useSession.mockReturnValue({ data: session, status: "authenticated" }) + const { container } = render() + expect(container).toMatchSnapshot() +}) diff --git a/__tests__/api/onboarding/confirm.test.js b/__tests__/api/onboarding/confirm.test.js index faf37b341..ae4ab1063 100644 --- a/__tests__/api/onboarding/confirm.test.js +++ b/__tests__/api/onboarding/confirm.test.js @@ -1,17 +1,22 @@ import { createMocks } from "node-mocks-http" import handleConfirm from "@/pages/api/onboarding/confirm" -import { graphql } from "msw" +import { graphql, HttpResponse } from "msw" import { sendConfirmMail } from "@/services/send-email" import { server } from "@/mocks/server" +import { vi, it, expect } from "vitest" -jest.mock("@/utils/log-action", () => jest.fn()) -jest.mock("@/utils/jwt", () => ({ getJwt: jest.fn() })) -jest.mock("@/services/send-email", () => ({ sendConfirmMail: jest.fn() })) -jest.mock("@/utils/env", () => ({ - ONBOARDING_NOTIFICATION_EMAILS: "mail", - NEXTAUTH_URL: "http://fake.fr", - NEXT_PUBLIC_HASURA_URL: "http://fake.fr", -})) +vi.mock("@/utils/log-action", () => ({ default: vi.fn() })) +vi.mock("@/utils/jwt", () => ({ getJwt: vi.fn() })) +vi.mock("@/services/send-email", () => ({ sendConfirmMail: vi.fn() })) +vi.mock("@/utils/env", async () => { + const actual = await vi.importActual("@/utils/env") + return { + ...actual, + ONBOARDING_NOTIFICATION_EMAILS: "mail", + NEXTAUTH_URL: "http://fake.fr", + NEXT_PUBLIC_HASURA_URL: "http://fake.fr", + } +}) it("should confirm request and send email", async () => { const { req, res } = createMocks({ @@ -29,15 +34,15 @@ it("should not confirm multiple times", async () => { query: { id: "fakeId" }, }) server.use( - graphql.mutation("confirmOnboardingRequest", (_req, res, ctx) => { - return res( - ctx.data({ + graphql.mutation("confirmOnboardingRequest", () => + HttpResponse.json({ + data: { update_onboarding_requests: { affected_rows: 0, }, - }) - ) - }) + }, + }) + ) ) await handleConfirm(req, res) expect(res._getStatusCode(200)) diff --git a/__tests__/api/onboarding/request.test.js b/__tests__/api/onboarding/request.test.js index 456ccc771..5341d06be 100644 --- a/__tests__/api/onboarding/request.test.js +++ b/__tests__/api/onboarding/request.test.js @@ -1,18 +1,19 @@ -import handleRequest from "../../../src/pages/api/onboarding/request" +import handleRequest from "@/pages/api/onboarding/request" import { createMocks } from "node-mocks-http" -import { graphql } from "msw" +import { graphql, HttpResponse } from "msw" import { sendRequestMail } from "@/services/send-email" import { server } from "@/mocks/server" +import { vi, it, expect } from "vitest" -jest.mock("@/utils/log-action", () => jest.fn()) -jest.mock("@/utils/jwt", () => ({ getJwt: jest.fn() })) -jest.mock("@/services/send-email", () => ({ - sendRequestMail: jest.fn(() => ({ +vi.mock("@/utils/log-action", () => ({ default: vi.fn() })) +vi.mock("@/utils/jwt", () => ({ getJwt: vi.fn() })) +vi.mock("@/services/send-email", () => ({ + sendRequestMail: vi.fn(() => ({ status: 200, text: () => Promise.resolve("response"), })), })) -jest.mock("@/utils/env", () => ({ +vi.mock("@/utils/env", () => ({ NEXTAUTH_URL: "http://fake.fr", NEXT_PUBLIC_HASURA_URL: "http://fake.fr", })) @@ -37,13 +38,13 @@ it("should not create duplicate request", async () => { body: { input: { data: "fakeData" } }, }) server.use( - graphql.query("getOnboardingRequestContaining", (_req, res, ctx) => { - return res( - ctx.data({ + graphql.query("getOnboardingRequestContaining", () => + HttpResponse.json({ + data: { onboarding_requests: ["fakeExistingRequest"], - }) - ) - }) + }, + }) + ) ) await handleRequest(req, res) expect(res._getStatusCode(200)) diff --git a/__tests__/api/onboarding/review.test.js b/__tests__/api/onboarding/review.test.js index 0216e2319..31d2d02b5 100644 --- a/__tests__/api/onboarding/review.test.js +++ b/__tests__/api/onboarding/review.test.js @@ -1,23 +1,28 @@ import onboard from "@/services/onboard" import { createMocks } from "node-mocks-http" import { getToken } from "next-auth/jwt" -import handleReview from "../../../src/pages/api/onboarding/review" -import { graphql } from "msw" +import handleReview from "@/pages/api/onboarding/review" +import { graphql, HttpResponse } from "msw" import { sendReviewMail } from "@/services/send-email" import { server } from "@/mocks/server" +import { vi, it, expect } from "vitest" -jest.mock("next-auth/jwt", () => ({ - getToken: jest.fn(() => ({ user: { login: "testUser" } })), +vi.mock("next-auth/jwt", () => ({ + getToken: vi.fn(() => ({ user: { login: "testUser" } })), })) -jest.mock("@/services/onboard", () => - jest.fn(() => ({ github: { status: 250, body: "fake body" } })) -) -jest.mock("@/utils/log-action", () => jest.fn()) -jest.mock("@/utils/jwt", () => ({ - getJwt: jest.fn(), +vi.mock("@/services/onboard", () => ({ + default: vi.fn(() => ({ github: { status: 250, body: "fake body" } })), })) -jest.mock("@/services/send-email", () => ({ - sendReviewMail: jest.fn(), +vi.mock("@/utils/log-action", () => ({ default: vi.fn() })) +vi.mock("@/utils/jwt", async () => { + const actual = await vi.importActual("@/utils/jwt") + return { + ...actual, + getJwt: vi.fn(), + } +}) +vi.mock("@/services/send-email", () => ({ + sendReviewMail: vi.fn(), })) it("should onboard user and send email", async () => { @@ -40,9 +45,9 @@ it("should return 400 if request is already reviewed", async () => { body: { input: { data: "fakeData" } }, }) server.use( - graphql.query("getOnboardingRequest", (_req, res, ctx) => { - return res(ctx.data({ onboarding_requests: [{ reviewed: {} }] })) - }) + graphql.query("getOnboardingRequest", () => + HttpResponse.json({ data: { onboarding_requests: [{ reviewed: {} }] } }) + ) ) await handleReview(req, res) expect(res._getStatusCode()).toBe(400) @@ -59,9 +64,9 @@ it("should return 500 if request does not exist", async () => { body: { input: { data: "fakeData" } }, }) server.use( - graphql.query("getOnboardingRequest", (_req, res, ctx) => { - return res(ctx.data({ onboarding_requests: [] })) - }) + graphql.query("getOnboardingRequest", () => + HttpResponse.json({ data: { onboarding_requests: [] } }) + ) ) await handleReview(req, res) expect(res._getStatusCode()).toBe(500) diff --git a/__tests__/api/revoke.test.js b/__tests__/api/revoke.test.js deleted file mode 100644 index e74c60384..000000000 --- a/__tests__/api/revoke.test.js +++ /dev/null @@ -1,67 +0,0 @@ -import revoke from "@/services/revoke" -import { createMocks } from "node-mocks-http" -import { getToken } from "next-auth/jwt" -import handleRevoke from "../../src/pages/api/revoke" - -jest.mock("next-auth/jwt", () => ({ - getToken: jest.fn(() => ({ user: { login: "testUser" } })), -})) -jest.mock("@/services/revoke", () => - jest.fn(() => ({ status: 250, body: "fake body" })) -) -jest.mock("@/utils/log-action", () => jest.fn()) -jest.mock("@/utils/jwt", () => ({ - getJwt: jest.fn(), -})) - -let req, res -beforeEach(() => { - ;({ req, res } = createMocks({ - method: "DELETE", - body: { - input: { - data: { - accountServiceID: "fake accountServiceID", - accountID: "fake accountID", - serviceName: "github", - }, - }, - }, - })) -}) - -it("should call the revoke service and return its return value", async () => { - await handleRevoke(req, res) - expect(res._getStatusCode()).toEqual(200) - expect(res._getData()).toEqual('{"status":250,"body":"fake body"}') - expect(revoke).toHaveBeenCalledWith( - "fake accountServiceID", - "fake accountID", - "github" - ) -}) - -it("should return 400 if service name is incorrect", async () => { - req.body.input.data.serviceName = "fake serviceName" - await handleRevoke(req, res) - expect(res._getStatusCode()).toEqual(400) - expect(res._getJSONData()).toEqual({ message: "unknown service name" }) - expect(revoke).not.toHaveBeenCalled() -}) - -it("should return 403 if no next-auth session", async () => { - getToken.mockImplementation(() => Promise.resolve(false)) - await handleRevoke(req, res) - expect(res._getStatusCode()).toEqual(403) - expect(res._getJSONData()).toStrictEqual({ message: "Unauthorized" }) - expect(revoke).not.toHaveBeenCalled() -}) - -it("should return 405 if wrong method", async () => { - ;({ req, res } = createMocks({ method: "GET" })) - await handleRevoke(req, res) - expect(res._getStatusCode()).toEqual(405) - expect(res._getJSONData()).toStrictEqual({ message: "Method Not Allowed" }) - expect(res._getHeaders().allow).toStrictEqual("DELETE") - expect(revoke).toHaveBeenCalledTimes(0) -}) diff --git a/__tests__/api/sync.test.js b/__tests__/api/sync.test.js index 5e5cda566..595a18157 100644 --- a/__tests__/api/sync.test.js +++ b/__tests__/api/sync.test.js @@ -2,10 +2,11 @@ import { sync } from "@/services/sync" import SERVICES from "@/utils/SERVICES" import { createMocks } from "node-mocks-http" import handleSync from "../../src/pages/api/sync" +import { vi, it, expect } from "vitest" -jest.mock("@/services/sync", () => ({ sync: jest.fn() })) -jest.mock("@/utils/log-action", () => jest.fn()) -jest.mock("@/utils/jwt", () => ({ getJwt: jest.fn() })) +vi.mock("@/services/sync", () => ({ sync: vi.fn() })) +vi.mock("@/utils/log-action", () => ({ default: vi.fn() })) +vi.mock("@/utils/jwt", () => ({ getJwt: vi.fn() })) it("should call the sync service with given services", async () => { const { req, res } = createMocks({ diff --git a/__tests__/components/__snapshots__/footer.test.js.snap b/__tests__/components/__snapshots__/footer.test.jsx.snap similarity index 95% rename from __tests__/components/__snapshots__/footer.test.js.snap rename to __tests__/components/__snapshots__/footer.test.jsx.snap index 212217a7e..3f7889fa4 100644 --- a/__tests__/components/__snapshots__/footer.test.js.snap +++ b/__tests__/components/__snapshots__/footer.test.jsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`renders footer 1`] = `
diff --git a/__tests__/components/__snapshots__/header.test.js.snap b/__tests__/components/__snapshots__/header.test.jsx.snap similarity index 93% rename from __tests__/components/__snapshots__/header.test.js.snap rename to __tests__/components/__snapshots__/header.test.jsx.snap index e936fd95a..ea0579bc0 100644 --- a/__tests__/components/__snapshots__/header.test.js.snap +++ b/__tests__/components/__snapshots__/header.test.jsx.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`when header renders does not show authenticated user if session is not valid 1`] = ` +exports[`when header renders > does not show authenticated user if session is not valid 1`] = `
`; -exports[`when header renders does show authenticated user if session is valid 1`] = ` +exports[`when header renders > does show authenticated user if session is valid 1`] = `
diff --git a/__tests__/components/__snapshots__/login.test.js.snap b/__tests__/components/__snapshots__/login.test.jsx.snap similarity index 80% rename from __tests__/components/__snapshots__/login.test.js.snap rename to __tests__/components/__snapshots__/login.test.jsx.snap index 3420a7412..6f884d356 100644 --- a/__tests__/components/__snapshots__/login.test.js.snap +++ b/__tests__/components/__snapshots__/login.test.jsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`renders login 1`] = `
diff --git a/__tests__/components/__snapshots__/search.test.js.snap b/__tests__/components/__snapshots__/search.test.jsx.snap similarity index 99% rename from __tests__/components/__snapshots__/search.test.js.snap rename to __tests__/components/__snapshots__/search.test.jsx.snap index 037728808..33592b0f6 100644 --- a/__tests__/components/__snapshots__/search.test.js.snap +++ b/__tests__/components/__snapshots__/search.test.jsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`renders search 1`] = `
diff --git a/__tests__/components/__snapshots__/user-profile.test.js.snap b/__tests__/components/__snapshots__/user-profile.test.js.snap deleted file mode 100644 index 468f202da..000000000 --- a/__tests__/components/__snapshots__/user-profile.test.js.snap +++ /dev/null @@ -1,213 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders user profile 1`] = ` -
-