diff --git a/.github/workflows/CI-CD.yml b/.github/workflows/CI-CD.yml index 794a525..7637577 100644 --- a/.github/workflows/CI-CD.yml +++ b/.github/workflows/CI-CD.yml @@ -36,7 +36,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v1 with: - node-version: 16.14.2 + node-version: 18.14.0 - name: Install dependencies run: npm ci - name: Run linter diff --git a/.nvmrc b/.nvmrc index f274881..49991d3 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.16.0 +v18.14.0 diff --git a/Dockerfile b/Dockerfile index a1468bb..91f7b78 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ #https://hub.docker.com/_/node?tab=tags&page=1 -FROM node:16.14.2-alpine3.15 +FROM node:18.14.0 WORKDIR /usr/src/app diff --git a/migrations/1681206838965-changeCancelProjectNotificationCopies.ts b/migrations/1681206838965-changeCancelProjectNotificationCopies.ts new file mode 100644 index 0000000..fce5cd9 --- /dev/null +++ b/migrations/1681206838965-changeCancelProjectNotificationCopies.ts @@ -0,0 +1,90 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { getNotificationTypeByEventName } from '../src/repositories/notificationTypeRepository'; +import { NOTIFICATION_TYPE_NAMES } from '../src/types/general'; + +export class changeCancelProjectNotificationCopies1681206838965 + implements MigrationInterface +{ + private async updateNotificationType( + eventName: string, + data: { + htmlTemplate?: any; + content?: string; + }, + ) { + const notificationType = await getNotificationTypeByEventName(eventName); + if (!notificationType) { + return; + } + notificationType.htmlTemplate = data.htmlTemplate; + notificationType.content = data.content; + await notificationType.save(); + } + + public async up(queryRunner: QueryRunner): Promise { + if ( + process.env.NODE_ENV === 'test' || + process.env.NODE_ENV === 'development' + ) { + // Running these migrations in test and development environments would make the tests fail + // because the notification types are not created in the test database + // In future we should use raw SQL queries to update the notification types + return; + } + await this.updateNotificationType( + NOTIFICATION_TYPE_NAMES.PROJECT_CANCELLED_OWNER, + { + htmlTemplate: [ + { + type: 'p', + content: 'Your project ', + }, + { + type: 'a', + content: '$projectTitle', + href: '$projectLink', + }, + { + type: 'p', + content: + ' has been canceled by an admin. More information can be found in our ', + }, + { + type: 'a', + content: 'terms of service', + href: 'https://giveth.io/tos', + }, + { + type: 'p', + content: '.', + }, + ], + content: + 'Your project {project name} has been canceled by an admin. More information can be found in our {terms of service}.', + }, + ); + + await this.updateNotificationType( + NOTIFICATION_TYPE_NAMES.PROJECT_CANCELLED_SUPPORTED, + { + htmlTemplate: [ + { + type: 'a', + content: '$projectTitle', + href: '$projectLink', + }, + { + type: 'p', + content: ' which you supported, has been canceled by an admin.', + }, + ], + content: + '{project name} which you supported, has been canceled by an admin.', + }, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + // + } +} diff --git a/migrations/1682242459196-seedNotificationTypeForProjectGetNewRank.ts b/migrations/1682242459196-seedNotificationTypeForProjectGetNewRank.ts new file mode 100644 index 0000000..6474da9 --- /dev/null +++ b/migrations/1682242459196-seedNotificationTypeForProjectGetNewRank.ts @@ -0,0 +1,161 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { + NotificationType, + SCHEMA_VALIDATORS_NAMES, +} from '../src/entities/notificationType'; +import { MICRO_SERVICES, THIRD_PARTY_EMAIL_SERVICES } from '../src/utils/utils'; +import { + NOTIFICATION_CATEGORY, + NOTIFICATION_TYPE_NAMES, +} from '../src/types/general'; +import { NOTIFICATION_CATEGORY_GROUPS } from '../src/entities/notificationSetting'; +import { SegmentEvents } from '../src/services/segment/segmentAnalyticsSingleton'; + +// https://github.com/Giveth/notification-center/issues/6 , https://gist.github.com/MohammadPCh/24434d50bc9ccd9b74905c271ee05482 +// icons https://gist.github.com/MohammadPCh/31e2b750dd9aa54edb21dcc6e7332efb +export const GivethNotificationTypes = [ + { + name: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_A_NEW_RANK, + description: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_A_NEW_RANK, + microService: MICRO_SERVICES.givethio, + category: NOTIFICATION_CATEGORY.PROJECT_RELATED, + icon: '', + categoryGroup: NOTIFICATION_CATEGORY_GROUPS.PROJECT_NEW_RANK, + schemaValidator: SCHEMA_VALIDATORS_NAMES.PROJECT_HAS_A_NEW_RANK, + emailNotifierService: null, + emailNotificationId: null, + pushNotifierService: null, + title: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_A_NEW_RANK, + htmlTemplate: [ + { + type: 'p', + content: 'Your project ', + }, + { + type: 'a', + content: '$projectTitle', + href: '$projectLink', + }, + { + type: 'p', + content: ' has a new rank.', + }, + ], + content: 'Your project {project name} has a new rank.', + }, + { + name: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + description: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + microService: MICRO_SERVICES.givethio, + category: NOTIFICATION_CATEGORY.PROJECT_RELATED, + icon: '', + categoryGroup: NOTIFICATION_CATEGORY_GROUPS.PROJECT_NEW_RANK, + schemaValidator: SCHEMA_VALIDATORS_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + emailNotifierService: null, + emailNotificationId: null, + pushNotifierService: null, + title: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + htmlTemplate: [ + { + type: 'p', + content: 'Your project ', + }, + { + type: 'a', + content: '$projectTitle', + href: '$projectLink', + }, + { + type: 'p', + content: ' has risen in the rank.', + }, + ], + content: 'Your project {project name} has has risen in the rank.', + }, + { + name: NOTIFICATION_TYPE_NAMES.YOUR_PROJECT_GOT_A_RANK, + description: NOTIFICATION_TYPE_NAMES.YOUR_PROJECT_GOT_A_RANK, + microService: MICRO_SERVICES.givethio, + category: NOTIFICATION_CATEGORY.PROJECT_RELATED, + icon: '', + categoryGroup: NOTIFICATION_CATEGORY_GROUPS.PROJECT_NEW_RANK, + schemaValidator: SCHEMA_VALIDATORS_NAMES.YOUR_PROJECT_GOT_A_RANK, + emailNotifierService: null, + emailNotificationId: null, + pushNotifierService: null, + title: NOTIFICATION_TYPE_NAMES.YOUR_PROJECT_GOT_A_RANK, + htmlTemplate: [ + { + type: 'p', + content: 'Your project ', + }, + { + type: 'a', + content: '$projectTitle', + href: '$projectLink', + }, + { + type: 'p', + content: ' got a rank.', + }, + ], + content: 'Your project {project name} got a rank.', + }, + + // This just for grouping and showing on setting page + { + name: 'New Rank', + title: 'New Rank', + description: 'Notify me when my project has a new rank.', + showOnSettingPage: true, + emailDefaultValue: false, + webDefaultValue: true, + isEmailEditable: false, + categoryGroup: NOTIFICATION_CATEGORY_GROUPS.PROJECT_NEW_RANK, + isGroupParent: true, + microService: MICRO_SERVICES.givethio, + category: NOTIFICATION_CATEGORY.PROJECT_RELATED, + }, +]; + +export class seedNotificationTypeForProjectGetNewRank1682242459196 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + // Giveth IO Notifications + await queryRunner.manager.save(NotificationType, GivethNotificationTypes); + + // Fetch the notificationType id for 'New Rank' + const result = await queryRunner.query( + `SELECT id FROM "notification_type" WHERE "categoryGroup" = 'projectNewRank';`, + ); + for (const row of result) { + await queryRunner.query(` + INSERT INTO "notification_setting" ( + "allowNotifications", + "allowEmailNotification", + "allowDappPushNotification", + "notificationTypeId", + "userAddressId", + "createdAt", + "updatedAt" + ) + SELECT + true, -- For these notificationTypes, allowNotifications is true + false, -- For these notificationTypes, allowNotifications is true + true, -- For these notificationTypes, allowNotifications is true + ${row.id}, -- New Rank notificationType id + "user_address"."id", -- UserAddress id + NOW(), -- Current timestamp for createdAt + NOW() -- Current timestamp for updatedAt + FROM user_address; + `); + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DELETE FROM notification_type WHERE "categoryGroup" = 'projectNewRank';`, + ); + } +} diff --git a/package-lock.json b/package-lock.json index 7a0f564..361fbbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "notification-venter", - "version": "2.0.2", + "version": "2.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "notification-venter", - "version": "2.0.2", + "version": "2.1.0", "license": "ISC", "dependencies": { "@adminjs/express": "4.1.0", @@ -58,8 +58,8 @@ "mocha": "9.2.2", "pgtools": "^0.3.2", "prettier": "2.5.1", - "ts-node": "10.7.0", - "ts-node-dev": "1.1.8", + "ts-node": "10.9.1", + "ts-node-dev": "2.0.0", "typescript": "^4.5.2" } }, @@ -1954,27 +1954,28 @@ "carbon-telemetry": "bin/carbon-telemetry.js" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "devOptional": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "devOptional": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -9338,12 +9339,12 @@ } }, "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "devOptional": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -9354,7 +9355,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { @@ -9381,20 +9382,20 @@ } }, "node_modules/ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, "dependencies": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", + "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, "bin": { @@ -9414,15 +9415,6 @@ } } }, - "node_modules/ts-node-dev/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/ts-node-dev/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -9447,32 +9439,6 @@ "rimraf": "bin.js" } }, - "node_modules/ts-node-dev/node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -11481,19 +11447,25 @@ "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==" }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "devOptional": true - }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "devOptional": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "@emotion/cache": { @@ -17086,12 +17058,12 @@ "dev": true }, "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "devOptional": true, "requires": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -17102,7 +17074,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "dependencies": { @@ -17115,29 +17087,23 @@ } }, "ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, "requires": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", + "minimist": "^1.2.6", "mkdirp": "^1.0.4", "resolve": "^1.0.0", "rimraf": "^2.6.1", "source-map-support": "^0.5.12", "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", + "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -17152,20 +17118,6 @@ "requires": { "glob": "^7.1.3" } - }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } } } }, diff --git a/package.json b/package.json index ef77022..2fea8f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notification-venter", - "version": "2.0.2", + "version": "2.1.0", "description": "", "main": "index.js", "scripts": { @@ -82,8 +82,8 @@ "mocha": "9.2.2", "pgtools": "^0.3.2", "prettier": "2.5.1", - "ts-node": "10.7.0", - "ts-node-dev": "1.1.8", + "ts-node": "10.9.1", + "ts-node-dev": "2.0.0", "typescript": "^4.5.2" } } diff --git a/src/entities/notificationSetting.ts b/src/entities/notificationSetting.ts index d471209..dacea8e 100644 --- a/src/entities/notificationSetting.ts +++ b/src/entities/notificationSetting.ts @@ -22,6 +22,7 @@ export const NOTIFICATION_CATEGORY_GROUPS = { 'supportedByYouProjectsHasNewUpdate', YOUR_PROJECT_UPDATE: 'yourProjectUpdate', PROJECT_LIKES: 'projectLikes', + PROJECT_NEW_RANK: 'projectNewRank', PROJECT_STATUS: 'projectStatus', DONATIONS_MADE: 'donationsMade', DONATIONS_RECEIVED: 'donationsReceived', diff --git a/src/entities/notificationType.ts b/src/entities/notificationType.ts index b92ef67..d5c6b8d 100644 --- a/src/entities/notificationType.ts +++ b/src/entities/notificationType.ts @@ -63,6 +63,9 @@ export const SCHEMA_VALIDATORS_NAMES = { USER_GIVPOWER_RELOCKED_AUTOMATICALLY: 'givPowerUserGivPowerRelockedAutoMatically', GIV_BACK_IS_READY_TO_CLAIM: 'givBackReadyToClaim', + PROJECT_HAS_A_NEW_RANK: 'projectHasANewRank', + PROJECT_HAS_RISEN_IN_THE_RANK: 'projectHasRisenInTheRank', + YOUR_PROJECT_GOT_A_RANK: 'yourProjectGotARank', }; export type HtmlTemplate = { type: string; content: string; href?: string }[]; diff --git a/src/routes/v1/notificationRouter.test.ts b/src/routes/v1/notificationRouter.test.ts index 4a74494..aaf8b07 100644 --- a/src/routes/v1/notificationRouter.test.ts +++ b/src/routes/v1/notificationRouter.test.ts @@ -1774,6 +1774,104 @@ function sendNotificationTestCases() { } }); + it('should create *Project has new lower rank* notification, success', async () => { + const data = { + eventName: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_A_NEW_RANK, + sendEmail: false, + sendSegment: false, + userWalletAddress: generateRandomEthereumAddress(), + metadata: { + projectTitle, + projectLink, + }, + }; + + const result = await axios.post(sendNotificationUrl, data, { + headers: { + authorization: getGivethIoBasicAuth(), + }, + }); + assert.equal(result.status, 200); + assert.isOk(result.data); + assert.isTrue(result.data.success); + }); + it('should create *Project has new lower rank* notification, failed invalid metadata', async () => { + try { + const data = { + eventName: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + sendEmail: false, + sendSegment: false, + userWalletAddress: generateRandomEthereumAddress(), + metadata: { + projectLink, + }, + }; + + await axios.post(sendNotificationUrl, data, { + headers: { + authorization: getGivethIoBasicAuth(), + }, + }); + // If request doesn't fail, it means this test failed + assert.isTrue(false); + } catch (e: any) { + assert.equal( + e.response.data.message, + errorMessagesEnum.IMPACT_GRAPH_VALIDATION_ERROR.message, + ); + assert.equal(e.response.data.description, '"projectTitle" is required'); + } + }); + + it('should create *Project has new higher rank* notification, success', async () => { + const data = { + eventName: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + sendEmail: false, + sendSegment: false, + userWalletAddress: generateRandomEthereumAddress(), + metadata: { + projectTitle, + projectLink, + }, + }; + + const result = await axios.post(sendNotificationUrl, data, { + headers: { + authorization: getGivethIoBasicAuth(), + }, + }); + assert.equal(result.status, 200); + assert.isOk(result.data); + assert.isTrue(result.data.success); + }); + it('should create *Project has new higher rank* notification, failed invalid metadata', async () => { + try { + const data = { + eventName: NOTIFICATION_TYPE_NAMES.PROJECT_HAS_RISEN_IN_THE_RANK, + sendEmail: false, + sendSegment: false, + userWalletAddress: generateRandomEthereumAddress(), + metadata: { + projectLink, + }, + }; + + await axios.post(sendNotificationUrl, data, { + headers: { + authorization: getGivethIoBasicAuth(), + }, + }); + // If request doesn't fail, it means this test failed + assert.isTrue(false); + } catch (e: any) { + assert.equal( + e.response.data.message, + errorMessagesEnum.IMPACT_GRAPH_VALIDATION_ERROR.message, + ); + assert.equal(e.response.data.description, '"projectTitle" is required'); + } + }); + it('should create notification successfully with passing trackId', async () => { const trackId = generateRandomTxHash(); const data = { diff --git a/src/types/general.ts b/src/types/general.ts index 564c65f..0489630 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -45,4 +45,9 @@ export enum NOTIFICATION_TYPE_NAMES { PROJECT_BADGE_UP_FOR_REVOKING = 'Project badge up for revoking', VERIFICATION_FORM_GOT_DRAFT_BY_ADMIN = 'Verification form got draft by admin', PROJECT_UPDATE_ADDED = 'Project update added - Users who supported', + + // https://github.com/Giveth/impact-graph/issues/774#issuecomment-1542337083 + PROJECT_HAS_A_NEW_RANK = 'Your project has a new rank', + PROJECT_HAS_RISEN_IN_THE_RANK = 'Your Project has risen in the rank', + YOUR_PROJECT_GOT_A_RANK = 'Your project got a rank', } diff --git a/src/utils/validators/segmentAndMetadataValidators.ts b/src/utils/validators/segmentAndMetadataValidators.ts index 56cfef4..bd59905 100644 --- a/src/utils/validators/segmentAndMetadataValidators.ts +++ b/src/utils/validators/segmentAndMetadataValidators.ts @@ -19,7 +19,7 @@ export const validateWithJoiSchema = (data: any, schema: ObjectSchema) => { // Define all joi schemas here const projectRelatedTrackerSchema = Joi.object({ // seems we have to different schemas, not good TODO CHANGE ON IMPACT GRAPH - email: Joi.string(), + email: Joi.string().allow(null).allow(''), title: Joi.string().required(), firstName: Joi.string().allow(null, ''), lastName: Joi.string().allow(null, ''), @@ -58,7 +58,7 @@ const givPowerUnLockedSchema = Joi.object({ }); const donationTrackerSchema = Joi.object({ - email: Joi.string().allow(null, ''), // if anonymous + email: Joi.string().allow(null).allow(''), // if anonymous title: Joi.string().required(), firstName: Joi.string().allow(null, ''), lastName: Joi.string().allow(null, ''), @@ -275,6 +275,18 @@ export const SEGMENT_METADATA_SCHEMA_VALIDATOR: { metadata: givBackReadyClaimSchema, segment: null, }, + projectHasRisenInTheRank: { + metadata: projectTitleProjectLinkSchema, + segment: null, + }, + projectHasANewRank: { + metadata: projectTitleProjectLinkSchema, + segment: null, + }, + yourProjectGotARank: { + metadata: projectTitleProjectLinkSchema, + segment: null, + }, }; function throwHttpErrorIfJoiValidatorFails( diff --git a/src/validators/schemaValidators.ts b/src/validators/schemaValidators.ts index 6237d48..f1089a9 100644 --- a/src/validators/schemaValidators.ts +++ b/src/validators/schemaValidators.ts @@ -47,10 +47,10 @@ export const sendNotificationValidator = Joi.object({ analyticsUserId: Joi.string(), payload: Joi.object({ // Common attributes - email: Joi.string(), + email: Joi.string().allow(null).allow(''), title: Joi.string(), slug: Joi.string(), - firstName: Joi.string(), + firstName: Joi.string().allow(null).allow(''), // Donation related attributes projectOwnerId: Joi.string(), @@ -60,15 +60,15 @@ export const sendNotificationValidator = Joi.object({ currency: Joi.string(), createdAt: Joi.string(), toWalletAddress: Joi.string(), - fromWalletAddress: Joi.string().allow(null), + fromWalletAddress: Joi.string().allow(null).allow(''), donationValueUsd: Joi.number(), donationValueEth: Joi.number(), verified: Joi.boolean(), - transakStatus: Joi.string().allow(null), + transakStatus: Joi.string().allow(null).allow(''), //Project related attributes - lastName: Joi.string(), - OwnerId: Joi.number(), + lastName: Joi.string().allow(null).allow(''), + OwnerId: Joi.number().allow(null).allow(''), // Project update update: Joi.string(),