diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 1cfe2c74e99..a14f33bf72f 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -138,6 +138,8 @@ jobs: with: slim: 'true' - uses: mansagroup/nrwl-nx-action@v3 + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: lint,build,test parallel: 5 @@ -168,6 +170,7 @@ jobs: uses: mansagroup/nrwl-nx-action@v3 env: LOG_LEVEL: 'info' + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: build projects: '@novu/api' @@ -176,6 +179,7 @@ jobs: uses: mansagroup/nrwl-nx-action@v3 env: LOG_LEVEL: 'info' + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: lint,build,test projects: ${{join(fromJson(needs.get-affected.outputs.test-packages), ',')}} @@ -195,6 +199,8 @@ jobs: - name: Run Lint, Build, Test uses: mansagroup/nrwl-nx-action@v3 + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: lint,build,test projects: ${{join(fromJson(needs.get-affected.outputs.test-libs), ',')}} @@ -230,6 +236,8 @@ jobs: - uses: ./.github/actions/setup-redis-cluster - uses: mansagroup/nrwl-nx-action@v3 name: Lint and build and test + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: build projects: '@novu/api' @@ -237,6 +245,8 @@ jobs: - uses: mansagroup/nrwl-nx-action@v3 name: Lint and build and test + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: lint,build,test projects: ${{matrix.projectName}} diff --git a/.github/workflows/preview-packages.yml b/.github/workflows/preview-packages.yml index d5810b8000d..f9b2c863706 100644 --- a/.github/workflows/preview-packages.yml +++ b/.github/workflows/preview-packages.yml @@ -41,6 +41,8 @@ jobs: run: pnpm run packages:set-latest - name: Build + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: pnpm run preview:pkg:build - name: Release package previews to pkg.pr.new diff --git a/.github/workflows/prod-deploy-inbound-mail.yml b/.github/workflows/prod-deploy-inbound-mail.yml index 28fc6a441a2..ab59a7f9130 100644 --- a/.github/workflows/prod-deploy-inbound-mail.yml +++ b/.github/workflows/prod-deploy-inbound-mail.yml @@ -36,6 +36,8 @@ jobs: - uses: ./.github/actions/setup-project - name: build api + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: pnpm build:inbound-mail - uses: crazy-max/ghaction-setup-docker@v2 diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 1bac0bb8f33..10360d41232 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -120,6 +120,8 @@ jobs: fi - name: Build packages + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: | if [ "${{ github.event.inputs.release_type }}" != "stable" ]; then pnpm run build:packages @@ -236,6 +238,8 @@ jobs: pnpm install --frozen-lockfile - name: Build packages + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: pnpm run build:packages - name: Publish packages to NPM diff --git a/.github/workflows/reusable-api-e2e.yml b/.github/workflows/reusable-api-e2e.yml index 47a0ec441f0..9caebd96925 100644 --- a/.github/workflows/reusable-api-e2e.yml +++ b/.github/workflows/reusable-api-e2e.yml @@ -45,6 +45,8 @@ jobs: name: Start localstack - name: Build API & Worker + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm nx run-many --target=build --all --projects=@novu/api-service,@novu/worker - name: Start Worker diff --git a/.github/workflows/reusable-dashboard-deploy.yml b/.github/workflows/reusable-dashboard-deploy.yml index 38393ccff7c..2cfe1e4513a 100644 --- a/.github/workflows/reusable-dashboard-deploy.yml +++ b/.github/workflows/reusable-dashboard-deploy.yml @@ -54,6 +54,8 @@ jobs: submodules: true - name: Build + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm build:dashboard --skip-nx-cache - name: Deploy Dashboard to Netlify diff --git a/.github/workflows/reusable-dashboard-e2e.yml b/.github/workflows/reusable-dashboard-e2e.yml index f31fbd0e161..154ab6a1d7a 100644 --- a/.github/workflows/reusable-dashboard-e2e.yml +++ b/.github/workflows/reusable-dashboard-e2e.yml @@ -93,6 +93,8 @@ jobs: echo NODE_ENV=test >> .env.playwright - uses: mansagroup/nrwl-nx-action@v3 + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} with: targets: build projects: '@novu/dashboard,@novu/api-service,@novu/worker' diff --git a/.github/workflows/reusable-inbound-mail-e2e.yml b/.github/workflows/reusable-inbound-mail-e2e.yml index e7a39c1e26b..e7c2b42a10b 100644 --- a/.github/workflows/reusable-inbound-mail-e2e.yml +++ b/.github/workflows/reusable-inbound-mail-e2e.yml @@ -50,6 +50,8 @@ jobs: # Runs a single command using the runners shell - name: Build Inbound Mail + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm build:inbound-mail - name: Run unit tests diff --git a/.github/workflows/reusable-webhook-e2e.yml b/.github/workflows/reusable-webhook-e2e.yml index 9aeafec725b..4976920cb82 100644 --- a/.github/workflows/reusable-webhook-e2e.yml +++ b/.github/workflows/reusable-webhook-e2e.yml @@ -25,6 +25,8 @@ jobs: # Runs a single command using the runners shell - name: Build Webhook + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm build:webhook # Runs a set of commands using the runners shell diff --git a/.github/workflows/reusable-worker-e2e.yml b/.github/workflows/reusable-worker-e2e.yml index f0ea87b54a6..703ee6ac150 100644 --- a/.github/workflows/reusable-worker-e2e.yml +++ b/.github/workflows/reusable-worker-e2e.yml @@ -54,6 +54,8 @@ jobs: # Runs a single command using the runners shell - name: Build worker + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm build:worker # Runs a set of commands using the runners shell diff --git a/.github/workflows/reusable-ws-e2e.yml b/.github/workflows/reusable-ws-e2e.yml index fe6c1190157..be0276b35da 100644 --- a/.github/workflows/reusable-ws-e2e.yml +++ b/.github/workflows/reusable-ws-e2e.yml @@ -49,6 +49,8 @@ jobs: # Runs a single command using the runners shell - name: Build WS + env: + NX_NO_CLOUD: ${{ secrets.NX_CLOUD_ACCESS_TOKEN == '' && 'true' || 'false' }} run: CI='' pnpm build:ws - name: Run unit tests diff --git a/apps/api/src/app/subscriptions/usecases/create-subscription-preferences/create-subscription-preferences.usecase.ts b/apps/api/src/app/subscriptions/usecases/create-subscription-preferences/create-subscription-preferences.usecase.ts index 75dbf27a25d..4d622101b8e 100644 --- a/apps/api/src/app/subscriptions/usecases/create-subscription-preferences/create-subscription-preferences.usecase.ts +++ b/apps/api/src/app/subscriptions/usecases/create-subscription-preferences/create-subscription-preferences.usecase.ts @@ -48,16 +48,35 @@ export class CreateSubscriptionPreferencesUsecase { continue; } - const createdPreference = await this.preferencesRepository.create({ - _environmentId: command.environmentId, - _organizationId: command.organizationId, - _subscriberId: command._subscriberId, - _templateId: workflow._id, - _topicSubscriptionId: command._topicSubscriptionId, - type: PreferencesTypeEnum.SUBSCRIPTION_SUBSCRIBER_WORKFLOW, - preferences: workflowPreferences, - contextKeys: command.contextKeys, - }); + let createdPreference; + try { + createdPreference = await this.preferencesRepository.create({ + _environmentId: command.environmentId, + _organizationId: command.organizationId, + _subscriberId: command._subscriberId, + _templateId: workflow._id, + _topicSubscriptionId: command._topicSubscriptionId, + type: PreferencesTypeEnum.SUBSCRIPTION_SUBSCRIBER_WORKFLOW, + preferences: workflowPreferences, + contextKeys: command.contextKeys, + }); + } catch (error) { + const isDuplicateKeyError = error && typeof error === 'object' && 'code' in error && error.code === 11000; + + if (isDuplicateKeyError) { + createdPreference = await this.preferencesRepository.findOne({ + _environmentId: command.environmentId, + _subscriberId: command._subscriberId, + _templateId: workflow._id, + _topicSubscriptionId: command._topicSubscriptionId, + type: PreferencesTypeEnum.SUBSCRIPTION_SUBSCRIBER_WORKFLOW, + }); + } + + if (!isDuplicateKeyError || !createdPreference) { + throw error; + } + } if (createdPreference) { preferencesResult.push({ diff --git a/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts b/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts index aac13d2c01d..10051b91701 100644 --- a/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts +++ b/libs/application-generic/src/usecases/upsert-preferences/upsert-preferences.usecase.ts @@ -180,18 +180,31 @@ export class UpsertPreferences { PreferencesTypeEnum.SUBSCRIBER_GLOBAL, ].includes(command.type); - return await this.preferencesRepository.create({ - _subscriberId: command._subscriberId, - _userId: command.userId, - _environmentId: command.environmentId, - _organizationId: command.organizationId, - _templateId: command.templateId, - _topicSubscriptionId: command.topicSubscriptionId, - preferences: command.preferences, - type: command.type, - schedule: command.schedule, - contextKeys: useContextFiltering && isContextScoped ? (command.contextKeys ?? []) : undefined, - }); + try { + return await this.preferencesRepository.create({ + _subscriberId: command._subscriberId, + _userId: command.userId, + _environmentId: command.environmentId, + _organizationId: command.organizationId, + _templateId: command.templateId, + _topicSubscriptionId: command.topicSubscriptionId, + preferences: command.preferences, + type: command.type, + schedule: command.schedule, + contextKeys: useContextFiltering && isContextScoped ? (command.contextKeys ?? []) : undefined, + }); + } catch (error) { + const isDuplicateKeyError = error && typeof error === 'object' && 'code' in error && error.code === 11000; + + if (isDuplicateKeyError) { + const existingPreference = await this.getPreference(command); + if (existingPreference) { + return existingPreference; + } + } + + throw error; + } } private async updatePreferences(