From c3d04e138a394e0c84bdc2597ea8291bccb5a0c0 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:24:51 +0000 Subject: [PATCH 01/14] feat : handled timezone events --- package-lock.json | 557 +++++++++++------------------ package.json | 2 + src/common/utils/constants.util.ts | 1 + src/modules/event/event.service.ts | 328 +++++++++-------- 4 files changed, 382 insertions(+), 506 deletions(-) diff --git a/package-lock.json b/package-lock.json index b535d93..755ce40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "haqdarshak-customer-app", + "name": "event-management-app", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "haqdarshak-customer-app", + "name": "event-management-app", "version": "0.0.1", "license": "UNLICENSED", "dependencies": { @@ -17,7 +17,10 @@ "@nestjs/swagger": "^7.1.16", "@nestjs/typeorm": "^10.0.2", "axios": "^1.6.2", + "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "moment-timezone": "^0.5.45", + "pg": "^8.13.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, @@ -29,13 +32,14 @@ "@types/jest": "^29.5.2", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", - "eslint": "^8.42.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", "jest": "^29.5.0", - "prettier": "^3.0.0", + "prettier": "^3.3.3", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", @@ -928,22 +932,23 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -964,9 +969,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1884,19 +1890,11 @@ "node": ">=14" } }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -2208,6 +2206,12 @@ "@types/superagent": "*" } }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, "node_modules/@types/validator": { "version": "13.11.9", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz", @@ -2948,15 +2952,6 @@ } ] }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3027,18 +3022,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3143,21 +3126,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3315,6 +3283,11 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, "node_modules/class-validator": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", @@ -3767,156 +3740,6 @@ "node": ">=0.10.0" } }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -3942,18 +3765,6 @@ "node": ">= 0.4" } }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4163,16 +3974,16 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4230,23 +4041,24 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -5390,21 +5202,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5443,24 +5240,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -5512,33 +5291,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -6656,6 +6408,25 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6833,24 +6604,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -7083,6 +6836,87 @@ "node": ">=8" } }, + "node_modules/pg": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", + "integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==", + "dependencies": { + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7183,6 +7017,41 @@ "node": ">=4" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7193,9 +7062,9 @@ } }, "node_modules/prettier": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", - "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -7614,21 +7483,6 @@ "node": ">=8" } }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -7994,6 +7848,14 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8227,12 +8089,12 @@ } }, "node_modules/synckit": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz", - "integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "dependencies": { - "@pkgr/utils": "^2.4.2", + "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" }, "engines": { @@ -8405,18 +8267,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -8886,15 +8736,6 @@ "node": ">= 0.8" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 14c6c6c..92b75a6 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "axios": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "moment-timezone": "^0.5.45", + "pg": "^8.13.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, diff --git a/src/common/utils/constants.util.ts b/src/common/utils/constants.util.ts index 5f75df2..4729d9d 100644 --- a/src/common/utils/constants.util.ts +++ b/src/common/utils/constants.util.ts @@ -61,6 +61,7 @@ export const ERROR_MESSAGES = { RECURRENCE_PATTERN_MISSING: 'Recurring Pattern is missing for this event', CANNOT_PASS_MAIN_EVENT_FALSE: 'You can not pass isMainEvent false because event is non recurring', + TIMEZONE_NOT_PROVIDED: 'Timezone not provided', CREATION_LIMIT_UNAVAILABLE: 'Event creation limit unavailable', CREATION_COUNT_EXCEEDED: 'Event Creation Count exceeded', RECURRENCE_PERIOD_INSUFFICIENT: 'Event recurrence period insufficient', diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index f67d35c..16e1cd3 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -42,10 +42,11 @@ import { RecurringEndDateValidationPipe, } from 'src/common/pipes/event-validation.pipe'; import { compareArrays } from 'src/common/utils/functions.util'; - +import * as moment from 'moment-timezone'; @Injectable() export class EventService { private eventCreationLimit: number; + private timezone: string; constructor( @InjectRepository(Events) @@ -60,6 +61,7 @@ export class EventService { this.eventCreationLimit = this.configService.get( 'EVENT_CREATION_LIMIT', ); + this.timezone = this.configService.get('TIMEZONE'); } async createEvent( @@ -67,58 +69,60 @@ export class EventService { response: Response, ): Promise { const apiId = API_ID.CREATE_EVENT; - try { - // this.validateCreateEventDto(createEventDto); - // true for private, false for public - let createdEvent: any = {}; - if (createEventDto.isRestricted === true) { - // private event - createdEvent = await this.createOfflineOrOnlineEvent(createEventDto); - - // if event is private then invitees are required - // add invitees to attendees table - - // await this.attendeesService.createAttendeesForRecurringEvents( - // createEventDto.attendees, - // createdEvent.res.eventId, - // createdEvent.eventRepetitionIds, - // createEventDto.createdBy, - // ); - - // TODO: new approach of adding attendees - // await this.attendeesService.createAttendeesForEvents( - // createEventDto.attendees, - // createdEvent.res.eventId, - // createEventDto.createdBy, - // ); - } else { - throw new NotImplementedException(ERROR_MESSAGES.PUBLIC_EVENTS); - // if event is public then registrationDate is required - if (createEventDto.eventType === 'online') { - // create online event - // this.createOnlineEvent(createEventDto); - } else if (createEventDto.eventType === 'offline') { - // create offline event - // this.createOfflineEvent(createEventDto); - } - } + if (!this.timezone || !this.timezone.trim().length) { + throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); + } - return response - .status(HttpStatus.CREATED) - .json(APIResponse.success(apiId, createdEvent.res, 'Created')); - } catch (error) { - console.log(error, 'error create event'); - throw error; + // this.validateCreateEventDto(createEventDto); + // true for private, false for public + let createdEvent: any = {}; + if (createEventDto.isRestricted === true) { + // private event + createdEvent = await this.createOfflineOrOnlineEvent(createEventDto); + + // if event is private then invitees are required + // add invitees to attendees table + + // await this.attendeesService.createAttendeesForRecurringEvents( + // createEventDto.attendees, + // createdEvent.res.eventId, + // createdEvent.eventRepetitionIds, + // createEventDto.createdBy, + // ); + + // TODO: new approach of adding attendees + // await this.attendeesService.createAttendeesForEvents( + // createEventDto.attendees, + // createdEvent.res.eventId, + // createEventDto.createdBy, + // ); + } else { + throw new NotImplementedException(ERROR_MESSAGES.PUBLIC_EVENTS); + // if event is public then registrationDate is required + if (createEventDto.eventType === 'online') { + // create online event + // this.createOnlineEvent(createEventDto); + } else if (createEventDto.eventType === 'offline') { + // create offline event + // this.createOfflineEvent(createEventDto); + } } + + return response + .status(HttpStatus.CREATED) + .json(APIResponse.success(apiId, createdEvent.res, 'Created')); } async getEvents(response, requestBody) { const apiId = API_ID.GET_EVENTS; - try { - const { filters } = requestBody; - const today = new Date(); + if (!this.timezone || !this.timezone.trim().length) { + throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); + } - let finalquery = `SELECT + const { filters } = requestBody; + const today = new Date(); + + let finalquery = `SELECT er."eventDetailId" AS "eventRepetition_eventDetailId", er."createdBy" AS "eventRepetition_createdBy", er.*, @@ -132,52 +136,45 @@ export class EventService { LEFT JOIN "EventDetails" AS ed ON er."eventDetailId"=ed."eventDetailId" LEFT JOIN "Events" AS e ON er."eventId"=e."eventId"`; - //User not pass any things then it show today and upcoming event - if (!filters || Object.keys(filters).length === 0) { - finalquery += ` WHERE (er."startDateTime" >= CURRENT_TIMESTAMP + //User not pass any things then it show today and upcoming event + if (!filters || Object.keys(filters).length === 0) { + finalquery += ` WHERE (er."startDateTime" >= CURRENT_TIMESTAMP OR er."endDateTime" > CURRENT_TIMESTAMP) AND ed.status='live'`; - } + } - // if user pass somthing in filter then make query - if (filters && Object.keys(filters).length > 0) { - finalquery = await this.createSearchQuery(filters, finalquery); - } + // if user pass somthing in filter then make query + if (filters && Object.keys(filters).length > 0) { + finalquery = await this.createSearchQuery(filters, finalquery); + } - // Set default limit and offset if not provided - const limit = requestBody.limit ? requestBody.limit : 200; - const offset = requestBody.offset ? requestBody.offset : 0; + // Set default limit and offset if not provided + const limit = requestBody.limit ? requestBody.limit : 200; + const offset = requestBody.offset ? requestBody.offset : 0; - // Append LIMIT and OFFSET to the query - finalquery += ` LIMIT ${limit} OFFSET ${offset}`; + // Append LIMIT and OFFSET to the query + finalquery += ` LIMIT ${limit} OFFSET ${offset}`; - const result = await this.eventRepetitionRepository.query(finalquery); - const totalCount = result[0]?.total_count; + const result = await this.eventRepetitionRepository.query(finalquery); + const totalCount = result[0]?.total_count; - // Add isEnded key based on endDateTime - const finalResult = result.map((event) => { - delete event.total_count; + // Add isEnded key based on endDateTime + const finalResult = result.map((event) => { + delete event.total_count; - const endDateTime = new Date(event.endDateTime); - return { - ...event, - isEnded: endDateTime < today, - }; - }); - if (finalResult.length === 0) { - throw new NotFoundException(ERROR_MESSAGES.EVENT_NOT_FOUND); - } - return response - .status(HttpStatus.OK) - .json( - APIResponse.success( - apiId, - { totalCount, events: finalResult }, - 'OK`', - ), - ); - } catch (error) { - throw error; + const endDateTime = new Date(event.endDateTime); + return { + ...event, + isEnded: endDateTime < today, + }; + }); + if (finalResult.length === 0) { + throw new NotFoundException(ERROR_MESSAGES.EVENT_NOT_FOUND); } + return response + .status(HttpStatus.OK) + .json( + APIResponse.success(apiId, { totalCount, events: finalResult }, 'OK`'), + ); } async createSearchQuery(filters, finalquery) { @@ -277,6 +274,11 @@ export class EventService { response: Response, ) { const apiId = API_ID.UPDATE_EVENT; + + if (!this.timezone || !this.timezone.trim().length) { + throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); + } + // Event repetition record must not be of passed date const currentTimestamp = new Date(); // To do optimize both cases in one queries @@ -1325,60 +1327,55 @@ export class EventService { async createOfflineOrOnlineEvent(createEventDto: CreateEventDto) { // recurring & non-recurring - try { - if (createEventDto.eventType === EventTypes.offline) { - // create offline event - createEventDto.onlineProvider = null; - createEventDto.meetingDetails = null; - createEventDto.recordings = null; - } else if (createEventDto.eventType === EventTypes.online) { - createEventDto.meetingDetails.providerGenerated = false; - } - const createdEventDetailDB = - await this.createEventDetailDB(createEventDto); + if (createEventDto.eventType === EventTypes.offline) { + // create offline event + createEventDto.onlineProvider = null; + createEventDto.meetingDetails = null; + createEventDto.recordings = null; + } else if (createEventDto.eventType === EventTypes.online) { + createEventDto.meetingDetails.providerGenerated = false; + } - const createdEventDB = await this.createEventDB( - createEventDto, - createdEventDetailDB, - ); + const createdEventDetailDB = await this.createEventDetailDB(createEventDto); - let erep: EventRepetition | InsertResult; + const createdEventDB = await this.createEventDB( + createEventDto, + createdEventDetailDB, + ); - if (createEventDto.isRecurring) { - erep = await this.createRecurringEvents( - createEventDto, - createdEventDB.eventId, - createdEventDetailDB.eventDetailId, - ); - return { - res: this.generateEventResponse( - createdEventDB, - erep?.generatedMaps[0], - erep?.generatedMaps.length, - ), - eventRepetitionIds: erep.identifiers, - }; - } else { - // this.createNonRecurringEvent(createEventDto); - erep = await this.createEventRepetitionDB( - createEventDto, - createdEventDB, - createdEventDetailDB, - ); - const { event, eventDetail, ...repetitionDtl } = erep; + let erep: EventRepetition | InsertResult; - return { - res: this.generateEventResponse(event, repetitionDtl), - eventRepetitionIds: [{ eventRepetitionId: erep.eventRepetitionId }], - }; - } + if (createEventDto.isRecurring) { + erep = await this.createRecurringEvents( + createEventDto, + createdEventDB.eventId, + createdEventDetailDB.eventDetailId, + ); + return { + res: this.generateEventResponse( + createdEventDB, + erep?.generatedMaps[0], + erep?.generatedMaps.length, + ), + eventRepetitionIds: erep.identifiers, + }; + } else { + // this.createNonRecurringEvent(createEventDto); + erep = await this.createEventRepetitionDB( + createEventDto, + createdEventDB, + createdEventDetailDB, + ); + const { event, eventDetail, ...repetitionDtl } = erep; - // generate and return response body - } catch (error) { - console.log(error, 'error'); - throw error; + return { + res: this.generateEventResponse(event, repetitionDtl), + eventRepetitionIds: [{ eventRepetitionId: erep.eventRepetitionId }], + }; } + + // generate and return response body } async createRecurringEvents( @@ -1387,16 +1384,17 @@ export class EventService { eventDetailId: string, isEdit: boolean = false, ) { + if (!(this.eventCreationLimit > 0)) { + await this.removePartiallyCreatedData(eventId, eventDetailId); + throw new BadRequestException(ERROR_MESSAGES.CREATION_LIMIT_UNAVAILABLE); + } + const eventOccurences = this.generateEventOccurences( createEventDto, eventDetailId, eventId, isEdit, ); - if (!(this.eventCreationLimit > 0)) { - await this.removePartiallyCreatedData(eventId, eventDetailId); - throw new BadRequestException(ERROR_MESSAGES.CREATION_LIMIT_UNAVAILABLE); - } if (eventOccurences.length > this.eventCreationLimit) { await this.removePartiallyCreatedData(eventId, eventDetailId); @@ -1482,15 +1480,40 @@ export class EventService { ) { const config = createEventDto.recurrencePattern; const startDate = createEventDto.startDatetime; - + const endDate = createEventDto.endDatetime; const occurrences: EventRepetition[] = []; - const startTime = createEventDto.startDatetime.split('T')[1]; - const endTime = createEventDto.endDatetime.split('T')[1]; - let currentDate = new Date(startDate.split('T')[0] + 'T' + startTime); + // if we convert to local time and then genererate occurences + let currentDateUTC = new Date(startDate); + let currentDate = new Date( + currentDateUTC.toLocaleString('en-US', { timeZone: this.timezone }), + ); // Convert to given timezone + + const currentEnd = new Date(endDate); + + let endDateTimeZoned = new Date( + currentEnd.toLocaleString('en-US', { timeZone: this.timezone }), + ); + + const endTime = endDateTimeZoned.toISOString().split('T')[1]; let createFirst = true; + const getEndDate = (currentDate: Date): string => { + const endDate = currentDate.toISOString().split('T')[0] + 'T' + endTime; + return endDate; + }; + + const removeZChar = (date: string): string => { + return date.slice(0, -1); + }; + + const convertToUTC = (istTime: string): string => { + // Convert the IST time string to UTC using moment-timezone + const utcTime = moment.tz(istTime, this.timezone).utc().toISOString(); + return utcTime; + }; + const addDays = (date: Date, days: number): Date => { const result = new Date(date); result.setDate(result.getDate() + days); @@ -1546,18 +1569,24 @@ export class EventService { eventId, isEdit, ); - const endDtm = currentDate.toISOString().split('T')[0] + 'T' + endTime; + const endDtm = getEndDate(currentDate); + + eventRec.startDateTime = new Date( + convertToUTC(removeZChar(currentDate.toISOString())), + ); + eventRec.endDateTime = new Date(convertToUTC(removeZChar(endDtm))); - eventRec.startDateTime = new Date(currentDate); - eventRec.endDateTime = new Date(endDtm); occurrences.push(eventRec); } if (config.frequency === Frequency.daily) { - const endDtm = currentDate.toISOString().split('T')[0] + 'T' + endTime; + const endDtm = getEndDate(currentDate); + + eventRec.startDateTime = new Date( + convertToUTC(removeZChar(currentDate.toISOString())), + ); + eventRec.endDateTime = new Date(convertToUTC(removeZChar(endDtm))); - eventRec.startDateTime = new Date(currentDate); - eventRec.endDateTime = new Date(endDtm); occurrences.push(eventRec); currentDate = addDays(currentDate, config.interval); } else if (config.frequency === Frequency.weekly) { @@ -1568,10 +1597,13 @@ export class EventService { config.daysOfWeek, ); currentDate = addDays(currentDate, daysUntilNextOccurrence); - const endDtm = currentDate.toISOString().split('T')[0] + 'T' + endTime; + const endDtm = getEndDate(currentDate); + + eventRec.startDateTime = new Date( + convertToUTC(removeZChar(currentDate.toISOString())), + ); + eventRec.endDateTime = new Date(convertToUTC(removeZChar(endDtm))); - eventRec.startDateTime = new Date(currentDate); - eventRec.endDateTime = new Date(endDtm); occurrences.push(eventRec); if ( currentDate.getDay() === From 0b1daad0bd90e113f0ebe8919492e8e240b23a05 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:25:38 +0000 Subject: [PATCH 02/14] chore : added better error handling --- src/common/filters/exception.filter.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/common/filters/exception.filter.ts b/src/common/filters/exception.filter.ts index 365565d..708021f 100644 --- a/src/common/filters/exception.filter.ts +++ b/src/common/filters/exception.filter.ts @@ -14,18 +14,14 @@ import { ERROR_MESSAGES } from '../utils/constants.util'; export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly apiId?: string) {} - catch(exception: unknown, host: ArgumentsHost) { - console.log('exception', exception); + catch(exception: any, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const status = exception instanceof HttpException ? exception.getStatus() : 500; - const exceptionResponse = - exception instanceof HttpException ? exception.getResponse() : null; - const errorMessage = - exception instanceof HttpException - ? (exceptionResponse as any).message || exception.message - : ERROR_MESSAGES.INTERNAL_SERVER_ERROR; + + let errorMessage = + exception?.message || ERROR_MESSAGES.INTERNAL_SERVER_ERROR; if (exception instanceof QueryFailedError) { const statusCode = HttpStatus.UNPROCESSABLE_ENTITY; From f101a02ee9ad3eb7082034729b2978167982e53e Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:30:48 +0000 Subject: [PATCH 03/14] chore : sonarcloud suggestions --- src/modules/event/event.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index 16e1cd3..c85546b 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -69,7 +69,7 @@ export class EventService { response: Response, ): Promise { const apiId = API_ID.CREATE_EVENT; - if (!this.timezone || !this.timezone.trim().length) { + if (!this.timezone?.trim()?.length) { throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); } @@ -115,7 +115,7 @@ export class EventService { async getEvents(response, requestBody) { const apiId = API_ID.GET_EVENTS; - if (!this.timezone || !this.timezone.trim().length) { + if (!this.timezone?.trim()?.length) { throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); } @@ -275,7 +275,7 @@ export class EventService { ) { const apiId = API_ID.UPDATE_EVENT; - if (!this.timezone || !this.timezone.trim().length) { + if (!this.timezone?.trim()?.length) { throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); } From 66b45b9e240c2dfb2593ad35dfed2696dc71c135 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:27:51 +0530 Subject: [PATCH 04/14] chore : removed hasura code --- src/common/middleware/axios.middleware.ts | 80 -------------- src/services/hasura/hasura.service.ts | 128 ---------------------- 2 files changed, 208 deletions(-) delete mode 100644 src/common/middleware/axios.middleware.ts delete mode 100644 src/services/hasura/hasura.service.ts diff --git a/src/common/middleware/axios.middleware.ts b/src/common/middleware/axios.middleware.ts deleted file mode 100644 index eaeecd9..0000000 --- a/src/common/middleware/axios.middleware.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import axios from 'axios'; -import { hasUncaughtExceptionCaptureCallback } from 'process'; - - -@Injectable() -export class AxiosRequest { - constructor() {} - - async postAxiosRequest(body,url,key) { - const data = JSON.stringify(body); - const config = { - method: 'post', - maxBodyLength: Infinity, - url: url, - headers: { - Authorization: `key=${key}`, - 'Content-Type': 'application/json', - }, - data: data, - }; - const response = axios - .request(config) - .then((response) => { - return response.data; - }) - .catch((error) => { - throw error; - }); - return response; - } - - async getAxiosRequest(url,key) { - const config = { - method: 'get', - maxBodyLength: Infinity, - url: url, - headers: { - Authorization: `key=${key}`, - 'Content-Type': 'application/json', - }, - }; - const response = axios - .request(config) - .then((response) => { - return response.data; - }) - .catch((error) => { - throw error; - }); - return response; - } - - async queryDb(hasuraUrl,adminSecretKey,query: string, variables?: Record): Promise { - try { - const response = await axios.post( - hasuraUrl, - { - query, - variables, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-hasura-admin-secret': adminSecretKey - }, - } - ); - // if (response?.data?.errors) { - // throw new ErrorResponse({ - // errorCode: response.data.errors[0].extensions, - // errorMessage: response.data.errors[0].message, - // }); - // } - return response.data; - } catch (ErrorResponse) { - throw ErrorResponse; - } - } -} diff --git a/src/services/hasura/hasura.service.ts b/src/services/hasura/hasura.service.ts deleted file mode 100644 index c4097eb..0000000 --- a/src/services/hasura/hasura.service.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import {AxiosRequest} from '../../common/middleware/axios.middleware' - - -@Injectable() -export class HasuraService { - private url: string; - private key: string; - constructor(private configService: ConfigService,private axiosRequest: AxiosRequest){ - this.url = this.configService.get('EVENTMANAGEMENTHASURA'); - this.key = this.configService.get('EVENTMANAGEMENTHASURAADMINSECRET'); - } - - async getEventDetails():Promise{ - try { - let query = `query GetEvents { - Events { - isRestricted - createdBy - eventType - location - onlineProvider - shortDescription - status - title - updatedBy - latitude - longitude - maxAttendees - recordings - params - description - image - createdAt - endDatetime - registrationDeadline - startDatetime - updatedAt - eventID - } - }` ; - let response = await this.axiosRequest.queryDb(this.url,this.key,query) - return response; - } catch (error) { - console.log(error); - } - - } - - async createEventDetails(createEventDto){ - try { - let query = `mutation InsertEvents($eventData: Events_insert_input!) { - insert_Events(objects: ) { - affected_rows - returning { - eventID - title - shortDescription - description - image - eventType - isRestricted - startDatetime - endDatetime - location - longitude - latitude - onlineProvider - registrationDeadline - maxAttendees - params - recordings - status - createdBy - createdAt - updatedBy - updatedAt - } - } - } - `; - console.log(createEventDto); - let response = await this.axiosRequest.queryDb(this.url,this.key,query,{...createEventDto}); - console.log(response); - return response; - } catch (error) { - console.log(error); - } - - } - - async getEventDetailById(eventId){ - try { - let query = `query GetEvents { - Events(where: {eventID: {_eq: $eventID}}) { - eventID - title - shortDescription - description - image - eventType - isRestricted - startDatetime - endDatetime - location - longitude - latitude - onlineProvider - registrationDeadline - maxAttendees - params - recordings - status - createdBy - createdAt - updatedBy - updatedAt - } - }`; - let response = await this.axiosRequest.queryDb(this.url,this.key,query,{eventID:eventId}) - return response; - } catch (error) { - console.log(error); - } - } - -} From 4b4960463c96c9ffdb35d131df17e1a273ba31cd Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:42:13 +0530 Subject: [PATCH 05/14] refactor : sonar cloud issues --- src/common/pipes/event-validation.pipe.ts | 267 +++++++++++++-------- src/common/utils/validation.util.ts | 2 +- src/modules/event/dto/create-event.dto.ts | 1 - src/modules/event/dto/update-event.dto.ts | 8 +- src/modules/event/entities/event.entity.ts | 1 - src/modules/event/event.module.ts | 12 +- 6 files changed, 176 insertions(+), 115 deletions(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index 5044625..5d575b0 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -54,8 +54,37 @@ export class RegistrationDateValidationPipe implements PipeTransform { ? new Date(createEventDto.registrationEndDate) : null; - // Ensure registration dates are not provided for restricted events + this.validateRestrictedEventDates( + createEventDto, + registrationStartDate, + registrationEndDate, + ); + this.validateRegistrationDatesNotInPast( + registrationStartDate, + registrationEndDate, + currentDate, + isRestricted, + ); + this.validateRegistrationDatesOrder( + registrationStartDate, + registrationEndDate, + isRestricted, + ); + this.validateRegistrationPeriodWithinEventPeriod( + registrationStartDate, + registrationEndDate, + startDate, + isRestricted, + ); + return createEventDto; + } + + private validateRestrictedEventDates( + createEventDto: CreateEventDto, + registrationStartDate: Date | null, + registrationEndDate: Date | null, + ) { if ( (createEventDto.isRestricted && registrationStartDate) || (createEventDto.isRestricted && registrationEndDate) @@ -64,8 +93,14 @@ export class RegistrationDateValidationPipe implements PipeTransform { ERROR_MESSAGES.RESTRICTED_EVENT_NO_REGISTRATION_DATE, ); } + } - // Ensure registration dates are not in the past + private validateRegistrationDatesNotInPast( + registrationStartDate: Date | null, + registrationEndDate: Date | null, + currentDate: Date, + isRestricted: boolean, + ) { if (registrationStartDate < currentDate && !isRestricted) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_INVALID, @@ -77,15 +112,26 @@ export class RegistrationDateValidationPipe implements PipeTransform { ERROR_MESSAGES.REGISTRATION_END_DATE_INVALID, ); } + } - // Validate registration dates + private validateRegistrationDatesOrder( + registrationStartDate: Date | null, + registrationEndDate: Date | null, + isRestricted: boolean, + ) { if (registrationStartDate > registrationEndDate && !isRestricted) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_BEFORE_END_DATE, ); } + } - // Registration period must fall between the event period + private validateRegistrationPeriodWithinEventPeriod( + registrationStartDate: Date | null, + registrationEndDate: Date | null, + startDate: Date, + isRestricted: boolean, + ) { if (registrationStartDate > startDate && !isRestricted) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_BEFORE_EVENT_DATE, @@ -97,99 +143,13 @@ export class RegistrationDateValidationPipe implements PipeTransform { ERROR_MESSAGES.REGISTRATION_END_DATE_BEFORE_EVENT_DATE, ); } - - return createEventDto; } } export class RecurringEndDateValidationPipe implements PipeTransform { transform(createEventDto: CreateEventDto | UpdateEventDto) { if (createEventDto.isRecurring) { - const endConditionValue = - createEventDto.recurrencePattern?.endCondition?.value; - const endConditionType = - createEventDto.recurrencePattern?.endCondition?.type; - const recurringStartDate = - createEventDto.recurrencePattern.recurringStartDate; - - if (!endConditionType || !endConditionValue) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRING_PATTERN_REQUIRED, - ); - } - - if (endConditionType === EndConditionType.endDate) { - const recurrenceEndDate = new Date(endConditionValue); - - const dateValid = - recurrenceEndDate && !Number.isNaN(recurrenceEndDate.getTime()); - - if (!dateValid) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRENCE_END_DATE_INVALID, - ); - } - - const currentDate = new Date(); - if (recurrenceEndDate < currentDate) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRENCE_END_DATE_SHOULD_BE_GREATER_THAN_CURRENT_DATE, - ); - } - - if ( - recurrenceEndDate <= new Date(createEventDto.startDatetime) && - createEventDto instanceof CreateEventDto - ) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRENCE_END_DATE_AFTER_EVENT_DATE, - ); - } - - const endDateTime = endConditionValue.split('T'); - const endDate = endDateTime[0]; // recurring end date - const startDateTime = recurringStartDate.split('T'); - const startDate = startDateTime[0]; - - if ( - new Date(endConditionValue).getTime() !== - new Date( - endDate + 'T' + createEventDto.endDatetime.split('T')[1], - ).getTime() // compare with current event end time - ) { - throw new BadRequestException( - 'Event End time does not match with Recurrence End time', - ); - } - - if ( - new Date(recurringStartDate).getTime() !== - new Date( - startDate + 'T' + createEventDto.startDatetime.split('T')[1], - ).getTime() - ) { - throw new BadRequestException( - 'Event Start time does not match with Recurrence Start time', - ); - } - - // createEventDto.recurrencePattern.endCondition.value = endDate; - } else if (endConditionType === EndConditionType.occurrences) { - const occurrences = Number(endConditionValue); - - if (!occurrences || occurrences < 1) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRENCE_OCCURRENCES_INVALID, - ); - } - } else if ( - endConditionType !== EndConditionType.occurrences && - endConditionType !== EndConditionType.endDate - ) { - throw new BadRequestException( - ERROR_MESSAGES.RECURRENCE_PATTERN_INVALID, - ); - } + this.validateRecurringEvent(createEventDto); } else if ( !createEventDto.isRecurring && Object.keys(createEventDto?.recurrencePattern ?? {})?.length @@ -201,6 +161,113 @@ export class RecurringEndDateValidationPipe implements PipeTransform { return createEventDto; } + + private validateRecurringEvent( + createEventDto: CreateEventDto | UpdateEventDto, + ) { + const endConditionValue = + createEventDto.recurrencePattern?.endCondition?.value; + const endConditionType = + createEventDto.recurrencePattern?.endCondition?.type; + const recurringStartDate = + createEventDto.recurrencePattern.recurringStartDate; + + if (!endConditionType || !endConditionValue) { + throw new BadRequestException(ERROR_MESSAGES.RECURRING_PATTERN_REQUIRED); + } + + if (endConditionType === EndConditionType.endDate) { + this.validateEndDateCondition( + createEventDto, + endConditionValue, + recurringStartDate, + ); + } else if (endConditionType === EndConditionType.occurrences) { + this.validateOccurrencesCondition(endConditionValue); + } else { + throw new BadRequestException(ERROR_MESSAGES.RECURRENCE_PATTERN_INVALID); + } + } + + private validateEndDateCondition( + createEventDto: CreateEventDto | UpdateEventDto, + endConditionValue: string, + recurringStartDate: string, + ) { + const recurrenceEndDate = new Date(endConditionValue); + + const dateValid = + recurrenceEndDate && !Number.isNaN(recurrenceEndDate.getTime()); + + if (!dateValid) { + throw new BadRequestException(ERROR_MESSAGES.RECURRENCE_END_DATE_INVALID); + } + + const currentDate = new Date(); + if (recurrenceEndDate < currentDate) { + throw new BadRequestException( + ERROR_MESSAGES.RECURRENCE_END_DATE_SHOULD_BE_GREATER_THAN_CURRENT_DATE, + ); + } + + if ( + recurrenceEndDate <= new Date(createEventDto.startDatetime) && + createEventDto instanceof CreateEventDto + ) { + throw new BadRequestException( + ERROR_MESSAGES.RECURRENCE_END_DATE_AFTER_EVENT_DATE, + ); + } + + this.validateEndDateTime( + createEventDto, + endConditionValue, + recurringStartDate, + ); + } + + private validateEndDateTime( + createEventDto: CreateEventDto | UpdateEventDto, + endConditionValue: string, + recurringStartDate: string, + ) { + const endDateTime = endConditionValue.split('T'); + const endDate = endDateTime[0]; // recurring end date + const startDateTime = recurringStartDate.split('T'); + const startDate = startDateTime[0]; + + if ( + new Date(endConditionValue).getTime() !== + new Date( + endDate + 'T' + createEventDto.endDatetime.split('T')[1], + ).getTime() // compare with current event end time + ) { + throw new BadRequestException( + 'Event End time does not match with Recurrence End time', + ); + } + + if ( + new Date(recurringStartDate).getTime() !== + new Date( + startDate + 'T' + createEventDto.startDatetime.split('T')[1], + ).getTime() + ) { + throw new BadRequestException( + 'Event Start time does not match with Recurrence Start time', + ); + } + } + + private validateOccurrencesCondition(endConditionValue: string) { + const occurrences = Number(endConditionValue); + + if (!occurrences || occurrences < 1) { + throw new BadRequestException( + ERROR_MESSAGES.RECURRENCE_OCCURRENCES_INVALID, + ); + } + } } @Injectable() @@ -209,7 +276,7 @@ export class AttendeesValidationPipe implements PipeTransform { const attendees = createEventDto?.attendees; if (!createEventDto.isRestricted) { - if (attendees && attendees.length) { + if (attendees?.length) { throw new BadRequestException(ERROR_MESSAGES.ATTENDEES_NOT_REQUIRED); } } @@ -223,14 +290,22 @@ export class SearchDateValidationPipe implements PipeTransform { transform(value: any, metadata: ArgumentMetadata) { const { date, startDate, endDate } = value.filters || {}; - // Check if both startDate and endDate are passed with date + this.checkDateConflicts(date, startDate, endDate); + this.checkDateFields(date, startDate, endDate); + this.checkDateOrder(date, startDate, endDate); + + return value; + } + + private checkDateConflicts(date: any, startDate: any, endDate: any) { if (date && (startDate || endDate)) { throw new BadRequestException( 'Only one of date, startDate, or endDate should be provided.', ); } + } - // Check if after and before are provided with date + private checkDateFields(date: any, startDate: any, endDate: any) { if (date && (!date.after || !date.before)) { throw new BadRequestException( 'Both "after" and "before" fields are required when date is provided.', @@ -262,7 +337,9 @@ export class SearchDateValidationPipe implements PipeTransform { 'if StartDate and EndDate Provided then "after" fields is required in startDate and "before fields is required in endDate', ); } + } + private checkDateOrder(date: any, startDate: any, endDate: any) { if ( (date && new Date(date.after) > new Date(date.before)) || (startDate && @@ -279,8 +356,6 @@ export class SearchDateValidationPipe implements PipeTransform { '"after" should be less than and equal to "before" fields ', ); } - - return value; } } diff --git a/src/common/utils/validation.util.ts b/src/common/utils/validation.util.ts index 9beb877..affbb03 100644 --- a/src/common/utils/validation.util.ts +++ b/src/common/utils/validation.util.ts @@ -6,7 +6,7 @@ import { function validateMeetingUrl(url, provider) { const providerPatterns = { - zoom: /^https:\/\/[\w-]*\.?zoom.us\/(j|my)\/[\d\w?=-]+$/, + zoom: /^https:\/\/[\w-]*\.?zoom.us\/(j|my)\/[-\w?=]+$/, googlemeet: /^(https:\/\/)?meet\.google\.com\/[a-zA-Z0-9-]+$/, // microsoftteams: /^(https:\/\/)?teams\.microsoft\.com\/[a-zA-Z0-9?&=]+$/, // Add other supported providers as needed diff --git a/src/modules/event/dto/create-event.dto.ts b/src/modules/event/dto/create-event.dto.ts index c3e86dd..2e789ef 100644 --- a/src/modules/event/dto/create-event.dto.ts +++ b/src/modules/event/dto/create-event.dto.ts @@ -27,7 +27,6 @@ import { EventTypes, Frequency, MeetingDetails, - RecurrencePattern, } from 'src/common/utils/types'; import { ERROR_MESSAGES } from 'src/common/utils/constants.util'; import { EndsWithZConstraint } from 'src/common/pipes/event-validation.pipe'; diff --git a/src/modules/event/dto/update-event.dto.ts b/src/modules/event/dto/update-event.dto.ts index 9887e4a..597af5c 100644 --- a/src/modules/event/dto/update-event.dto.ts +++ b/src/modules/event/dto/update-event.dto.ts @@ -3,25 +3,19 @@ import { IsNotEmpty, IsOptional, IsString, - IsUUID, IsEnum, IsLongitude, IsLatitude, IsBoolean, - IsInt, - Min, IsDateString, IsObject, ValidateIf, ValidateNested, - Validate, IsIn, } from 'class-validator'; import { MeetingDetails } from 'src/common/utils/types'; import { Transform, Type } from 'class-transformer'; -import { UrlWithProviderValidator } from 'src/common/utils/validation.util'; -import { RecurrencePatternDto } from './create-event.dto'; -import { MeetingDetailsDto } from './create-event.dto'; +import { RecurrencePatternDto, MeetingDetailsDto } from './create-event.dto'; export interface UpdateResult { onlineDetails?: any; erMetaData?: any; diff --git a/src/modules/event/entities/event.entity.ts b/src/modules/event/entities/event.entity.ts index 0fd9b43..92892d3 100644 --- a/src/modules/event/entities/event.entity.ts +++ b/src/modules/event/entities/event.entity.ts @@ -4,7 +4,6 @@ import { Column, CreateDateColumn, UpdateDateColumn, - ManyToOne, JoinColumn, OneToMany, OneToOne, diff --git a/src/modules/event/event.module.ts b/src/modules/event/event.module.ts index 656b753..dfa73e0 100644 --- a/src/modules/event/event.module.ts +++ b/src/modules/event/event.module.ts @@ -1,9 +1,7 @@ import { Module } from '@nestjs/common'; import { EventService } from './event.service'; import { EventController } from './event.controller'; -// import { HasuraService } from 'src/services/hasura/hasura.service'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -// import { AxiosRequest } from 'src/common/middleware/axios.middleware'; +import { ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Events } from './entities/event.entity'; import { EventDetail } from './entities/eventDetail.entity'; @@ -21,10 +19,6 @@ import { EventRepetition } from './entities/eventRepetition.entity'; ]), ], controllers: [EventController], - providers: [ - EventService, - ConfigService, - AttendeesService, - ], + providers: [EventService, ConfigService, AttendeesService], }) -export class EventModule { } +export class EventModule {} From 79880d303868c01abbbec26d3d8971a7831698be Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:42:32 +0530 Subject: [PATCH 06/14] chore : remove unwanted loc --- src/modules/event/event.service.ts | 36 +++++++++--------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index c85546b..d8357f9 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -22,9 +22,7 @@ import { import { Events } from './entities/event.entity'; import { Response } from 'express'; import APIResponse from 'src/common/utils/response'; -import { SearchFilterDto } from './dto/search-event.dto'; import { AttendeesService } from '../attendees/attendees.service'; -import { EventAttendeesDTO } from '../attendees/dto/EventAttendance.dto'; import { EventDetail } from './entities/eventDetail.entity'; import { API_ID, ERROR_MESSAGES } from 'src/common/utils/constants.util'; import { EventRepetition } from './entities/eventRepetition.entity'; @@ -98,14 +96,14 @@ export class EventService { // ); } else { throw new NotImplementedException(ERROR_MESSAGES.PUBLIC_EVENTS); - // if event is public then registrationDate is required - if (createEventDto.eventType === 'online') { - // create online event - // this.createOnlineEvent(createEventDto); - } else if (createEventDto.eventType === 'offline') { - // create offline event - // this.createOfflineEvent(createEventDto); - } + // TODO: if event is public then registrationDate is required + // if (createEventDto.eventType === 'online') { + // create online event + // this.createOnlineEvent(createEventDto); + // } else if (createEventDto.eventType === 'offline') { + // create offline event + // this.createOfflineEvent(createEventDto); + // } } return response @@ -182,9 +180,6 @@ export class EventService { // Handle specific date records if (filters?.date) { - // const startDate = filters?.date; - // const startDateTime = `${startDate} 00:00:00`; - // const endDateTime = `${startDate} 23:59:59`; const startDateTime = filters?.date.after; // min date const endDateTime = filters?.date.before; // max date ---> seraching on the basis of max date whereClauses.push( @@ -194,9 +189,6 @@ export class EventService { // Handle startDate if (filters?.startDate && filters.endDate === undefined) { - const startDate = filters?.startDate; - // const startDateTime = `${startDate} 00:00:00`; - // const endDateTime = `${startDate} 23:59:59`; const startDateTime = filters.startDate.after; const endDateTime = filters.startDate.before; @@ -206,9 +198,6 @@ export class EventService { } if (filters?.startDate && filters.endDate) { - const startDate = filters?.startDate; - // const startDateTime = `${startDate} 00:00:00`; - // const endDateTime = `${filters?.endDate} 23:59:59`; const startDateTime = filters.startDate.after; // 21 -> startDate const endDateTime = filters.endDate.before; @@ -218,9 +207,6 @@ export class EventService { } if (filters.endDate && filters.startDate === undefined) { - // const endDate = filters?.endDate; - // const startDateTime = `${endDate} 00:00:00`; - // const endDateTime = `${endDate} 23:59:59`; const startDateTime = filters.endDate.after; const endDateTime = filters.endDate.before; whereClauses.push( @@ -557,7 +543,7 @@ export class EventService { true, ); - const extUpdt = await this.updateEventRepetitionPattern( + await this.updateEventRepetitionPattern( currentEventRepetition.eventId, currentEventRepetition.recurrencePattern, ); @@ -587,7 +573,7 @@ export class EventService { oldRecurrencePattern.endCondition.value = currentEventRepetition.startDatetime; - const extUpdt = await this.updateEventRepetitionPattern( + await this.updateEventRepetitionPattern( currentEventRepetition.eventId, oldRecurrencePattern, ); @@ -1361,7 +1347,7 @@ export class EventService { eventRepetitionIds: erep.identifiers, }; } else { - // this.createNonRecurringEvent(createEventDto); + // createNonRecurringEvent erep = await this.createEventRepetitionDB( createEventDto, createdEventDB, From 3193370f2dc785b2411e4c1e6520bc3d87967fb0 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:48:07 +0530 Subject: [PATCH 07/14] chore : sonar cloud - potentially hardcoded credential --- src/modules/event/dto/create-event.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/event/dto/create-event.dto.ts b/src/modules/event/dto/create-event.dto.ts index 2e789ef..6ad0b5b 100644 --- a/src/modules/event/dto/create-event.dto.ts +++ b/src/modules/event/dto/create-event.dto.ts @@ -281,8 +281,8 @@ export class CreateEventDto { description: 'Online Meeting Details', example: { url: 'https://example.com/meeting', - id: '123-456-789', - password: 'xxxxxxx', + id: 'meeting-id', + password: 'hidden-password', }, }) @IsObject() From 941fb4c35c8c4f48797fbb69028ce7fa1368440d Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:33:41 +0530 Subject: [PATCH 08/14] chore : added string const --- src/common/pipes/event-validation.pipe.ts | 20 ++++++++------------ src/common/utils/constants.util.ts | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index 5d575b0..13df441 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -242,9 +242,7 @@ export class RecurringEndDateValidationPipe implements PipeTransform { endDate + 'T' + createEventDto.endDatetime.split('T')[1], ).getTime() // compare with current event end time ) { - throw new BadRequestException( - 'Event End time does not match with Recurrence End time', - ); + throw new BadRequestException(ERROR_MESSAGES.ENDTIME_DOES_NOT_MATCH); } if ( @@ -254,7 +252,7 @@ export class RecurringEndDateValidationPipe implements PipeTransform { ).getTime() ) { throw new BadRequestException( - 'Event Start time does not match with Recurrence Start time', + ERROR_MESSAGES.EVENT_START_TIME_DOES_NOT_MATCH, ); } } @@ -299,16 +297,14 @@ export class SearchDateValidationPipe implements PipeTransform { private checkDateConflicts(date: any, startDate: any, endDate: any) { if (date && (startDate || endDate)) { - throw new BadRequestException( - 'Only one of date, startDate, or endDate should be provided.', - ); + throw new BadRequestException(ERROR_MESSAGES.ONLY_ONE_DATE_ALLOWED); } } private checkDateFields(date: any, startDate: any, endDate: any) { if (date && (!date.after || !date.before)) { throw new BadRequestException( - 'Both "after" and "before" fields are required when date is provided.', + ERROR_MESSAGES.BOTH_AFTER_AND_BEFORE_REQUIRED, ); } @@ -318,7 +314,7 @@ export class SearchDateValidationPipe implements PipeTransform { endDate === undefined ) { throw new BadRequestException( - 'Both "after" and "before" fields are required when startDate is provided.', + ERROR_MESSAGES.BOTH_AFTER_AND_BEFORE_REQUIRED_FOR_STARTDATE, ); } @@ -328,13 +324,13 @@ export class SearchDateValidationPipe implements PipeTransform { startDate === undefined ) { throw new BadRequestException( - 'Both "after" and "before" fields are required when endDate is provided.', + ERROR_MESSAGES.BOTH_AFTER_AND_BEFORE_REQUIRED_FOR_ENDDATE, ); } if (startDate && endDate && (!startDate.after || !endDate.before)) { throw new BadRequestException( - 'if StartDate and EndDate Provided then "after" fields is required in startDate and "before fields is required in endDate', + ERROR_MESSAGES.AFTER_IN_START_AND_BEFORE_IN_END, ); } } @@ -353,7 +349,7 @@ export class SearchDateValidationPipe implements PipeTransform { new Date(startDate.after) > new Date(endDate.before)) ) { throw new BadRequestException( - '"after" should be less than and equal to "before" fields ', + ERROR_MESSAGES.AFTER_SHOULD_BE_LESS_THAN_BEFORE, ); } } diff --git a/src/common/utils/constants.util.ts b/src/common/utils/constants.util.ts index 4729d9d..9f9381c 100644 --- a/src/common/utils/constants.util.ts +++ b/src/common/utils/constants.util.ts @@ -61,6 +61,23 @@ export const ERROR_MESSAGES = { RECURRENCE_PATTERN_MISSING: 'Recurring Pattern is missing for this event', CANNOT_PASS_MAIN_EVENT_FALSE: 'You can not pass isMainEvent false because event is non recurring', + EVENT_ALREADY_ARCHIVED: 'Event is already archived', + EVENT_END_TIME_DOES_NOT_MATCH: + 'Event End time does not match with Recurrence End time', + EVENT_START_TIME_DOES_NOT_MATCH: + 'Event Start time does not match with Recurrence Start time', + ONLY_ONE_DATE_ALLOWED: + 'Only one of date, startDate, or endDate should be provided.', + BOTH_AFTER_AND_BEFORE_REQUIRED: + 'Both "after" and "before" fields are required when date is provided.', + BOTH_AFTER_AND_BEFORE_REQUIRED_FOR_STARTDATE: + 'Both "after" and "before" fields are required when startDate is provided.', + BOTH_AFTER_AND_BEFORE_REQUIRED_FOR_ENDDATE: + 'Both "after" and "before" fields are required when endDate is provided.', + AFTER_IN_START_AND_BEFORE_IN_END: + 'if StartDate and EndDate Provided then "after" fields is required in startDate and "before fields is required in endDate', + AFTER_SHOULD_BE_LESS_THAN_BEFORE: + '"after" should be less than or equal to "before" fields ', TIMEZONE_NOT_PROVIDED: 'Timezone not provided', CREATION_LIMIT_UNAVAILABLE: 'Event creation limit unavailable', CREATION_COUNT_EXCEEDED: 'Event Creation Count exceeded', From e7d8715d3e955e466f7bb07709ff33ca7999fbe0 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:18:55 +0530 Subject: [PATCH 09/14] chore : code rabbit suggestions --- src/common/filters/exception.filter.ts | 5 ++++- src/modules/event/event.service.ts | 19 +++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/common/filters/exception.filter.ts b/src/common/filters/exception.filter.ts index 708021f..7a034c6 100644 --- a/src/common/filters/exception.filter.ts +++ b/src/common/filters/exception.filter.ts @@ -14,7 +14,10 @@ import { ERROR_MESSAGES } from '../utils/constants.util'; export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly apiId?: string) {} - catch(exception: any, host: ArgumentsHost) { + catch( + exception: Error | HttpException | QueryFailedError, + host: ArgumentsHost, + ) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const status = diff --git a/src/modules/event/event.service.ts b/src/modules/event/event.service.ts index d8357f9..c62f547 100644 --- a/src/modules/event/event.service.ts +++ b/src/modules/event/event.service.ts @@ -67,9 +67,7 @@ export class EventService { response: Response, ): Promise { const apiId = API_ID.CREATE_EVENT; - if (!this.timezone?.trim()?.length) { - throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); - } + this.validateTimezone(); // this.validateCreateEventDto(createEventDto); // true for private, false for public @@ -113,9 +111,7 @@ export class EventService { async getEvents(response, requestBody) { const apiId = API_ID.GET_EVENTS; - if (!this.timezone?.trim()?.length) { - throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); - } + this.validateTimezone(); const { filters } = requestBody; const today = new Date(); @@ -260,10 +256,7 @@ export class EventService { response: Response, ) { const apiId = API_ID.UPDATE_EVENT; - - if (!this.timezone?.trim()?.length) { - throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); - } + this.validateTimezone(); // Event repetition record must not be of passed date const currentTimestamp = new Date(); @@ -1612,6 +1605,12 @@ export class EventService { return occurrences; } + private validateTimezone() { + if (!this.timezone?.trim()?.length) { + throw new BadRequestException(ERROR_MESSAGES.TIMEZONE_NOT_PROVIDED); + } + } + async deleteEvent(eventId: string): Promise { return this.eventRepository.delete({ eventId }); } From 28ebc737922d7158983574f8a76617a52be9256f Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:22:32 +0530 Subject: [PATCH 10/14] chore : resolve security hotspot --- src/modules/event/dto/create-event.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/event/dto/create-event.dto.ts b/src/modules/event/dto/create-event.dto.ts index 6ad0b5b..e047418 100644 --- a/src/modules/event/dto/create-event.dto.ts +++ b/src/modules/event/dto/create-event.dto.ts @@ -282,7 +282,7 @@ export class CreateEventDto { example: { url: 'https://example.com/meeting', id: 'meeting-id', - password: 'hidden-password', + password: '**********', }, }) @IsObject() From 41e8754e9d6280471f5b43daf1de864a7c50e581 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:42:58 +0530 Subject: [PATCH 11/14] chore : sonar cloud issue --- src/modules/event/dto/create-event.dto.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/event/dto/create-event.dto.ts b/src/modules/event/dto/create-event.dto.ts index e047418..1e7853b 100644 --- a/src/modules/event/dto/create-event.dto.ts +++ b/src/modules/event/dto/create-event.dto.ts @@ -51,7 +51,10 @@ export class MeetingDetailsDto { @Validate(UrlWithProviderValidator) url: string; - @ApiProperty({ description: 'Meeting password', example: 'xxxxxx' }) + @ApiProperty({ + description: 'Meeting password', + writeOnly: true, // This will hide it from API response docs + }) @IsString() @IsOptional() password: string; @@ -282,7 +285,7 @@ export class CreateEventDto { example: { url: 'https://example.com/meeting', id: 'meeting-id', - password: '**********', + // password: '**********', // This will be hidden from API response docs }, }) @IsObject() From 852e3d1d6fad9cc6a7ce8fd43275882408822925 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:46:30 +0530 Subject: [PATCH 12/14] chore : code rabbit suggestions --- src/common/pipes/event-validation.pipe.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index 13df441..e3554ed 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -260,7 +260,7 @@ export class RecurringEndDateValidationPipe implements PipeTransform { private validateOccurrencesCondition(endConditionValue: string) { const occurrences = Number(endConditionValue); - if (!occurrences || occurrences < 1) { + if (!Number.isInteger(occurrences) || occurrences < 1) { throw new BadRequestException( ERROR_MESSAGES.RECURRENCE_OCCURRENCES_INVALID, ); From e05f7d49b50b2dec8376417f0eea57124c2d90a9 Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:58:49 +0530 Subject: [PATCH 13/14] chore : handle null comparison --- src/common/pipes/event-validation.pipe.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index e3554ed..abad91f 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -101,13 +101,13 @@ export class RegistrationDateValidationPipe implements PipeTransform { currentDate: Date, isRestricted: boolean, ) { - if (registrationStartDate < currentDate && !isRestricted) { + if (registrationStartDate && registrationStartDate < currentDate && !isRestricted) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_INVALID, ); } - if (registrationEndDate < currentDate && !isRestricted) { + if (registrationEndDate && registrationEndDate < currentDate && !isRestricted) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_END_DATE_INVALID, ); From a693b9bbd1be87021b6d94579035785ad54d455c Mon Sep 17 00:00:00 2001 From: Kshitija Kadam <65657373+Xitija@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:08:35 +0530 Subject: [PATCH 14/14] chore : code rabbit suggestions --- src/common/pipes/event-validation.pipe.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/common/pipes/event-validation.pipe.ts b/src/common/pipes/event-validation.pipe.ts index abad91f..bf6622c 100644 --- a/src/common/pipes/event-validation.pipe.ts +++ b/src/common/pipes/event-validation.pipe.ts @@ -101,13 +101,21 @@ export class RegistrationDateValidationPipe implements PipeTransform { currentDate: Date, isRestricted: boolean, ) { - if (registrationStartDate && registrationStartDate < currentDate && !isRestricted) { + if ( + registrationStartDate && + registrationStartDate < currentDate && + !isRestricted + ) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_INVALID, ); } - if (registrationEndDate && registrationEndDate < currentDate && !isRestricted) { + if ( + registrationEndDate && + registrationEndDate < currentDate && + !isRestricted + ) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_END_DATE_INVALID, ); @@ -119,7 +127,12 @@ export class RegistrationDateValidationPipe implements PipeTransform { registrationEndDate: Date | null, isRestricted: boolean, ) { - if (registrationStartDate > registrationEndDate && !isRestricted) { + if ( + registrationStartDate && + registrationEndDate && + registrationStartDate > registrationEndDate && + !isRestricted + ) { throw new BadRequestException( ERROR_MESSAGES.REGISTRATION_START_DATE_BEFORE_END_DATE, );