From 99ebbe8fbb1f69dd97770e5f00c09dbfab36ac91 Mon Sep 17 00:00:00 2001 From: Vladimir Klimontovich Date: Mon, 11 Dec 2023 18:26:25 -0500 Subject: [PATCH] feat: export user activity to intercom --- pnpm-lock.yaml | 95 +++++++++---------- webapps/ee-api/package.json | 9 +- .../marketing-automation/intercom/export.ts | 78 +++++++++++++++ 3 files changed, 126 insertions(+), 56 deletions(-) create mode 100644 webapps/ee-api/pages/api/marketing-automation/intercom/export.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc4e9b77c..a58a85d07 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -996,6 +996,9 @@ importers: firebase-admin: specifier: ^11.11.0 version: 11.11.0 + intercom-client: + specifier: ^4.0.0 + version: 4.0.0 is-valid-domain: specifier: ^0.1.6 version: 0.1.6 @@ -4085,7 +4088,6 @@ packages: minimatch: 3.1.2 transitivePeerDependencies: - supports-color - dev: true /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} @@ -4096,10 +4098,10 @@ packages: minimatch: 3.1.2 transitivePeerDependencies: - supports-color + dev: false /@humanwhocodes/gitignore-to-minimatch@1.0.2: resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} - dev: true /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -5435,7 +5437,6 @@ packages: picocolors: 1.0.0 tiny-glob: 0.2.9 tslib: 2.6.2 - dev: true /@playwright/test@1.39.0: resolution: {integrity: sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==} @@ -8902,7 +8903,6 @@ packages: typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true /@typescript-eslint/parser@5.58.0(eslint@8.29.0)(typescript@4.9.4): resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} @@ -8922,6 +8922,7 @@ packages: typescript: 4.9.4 transitivePeerDependencies: - supports-color + dev: false /@typescript-eslint/parser@5.58.0(eslint@8.29.0)(typescript@4.9.5): resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} @@ -8993,6 +8994,7 @@ packages: typescript: 4.9.4 transitivePeerDependencies: - supports-color + dev: false /@typescript-eslint/typescript-estree@5.58.0(typescript@4.9.5): resolution: {integrity: sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==} @@ -10113,6 +10115,14 @@ packages: resolution: {integrity: sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==} engines: {node: '>=4'} + /axios@0.24.0: + resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} + dependencies: + follow-redirects: 1.15.2 + transitivePeerDependencies: + - debug + dev: false + /axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: @@ -12917,7 +12927,7 @@ packages: eslint: 8.29.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.29.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.29.0) eslint-plugin-react: 7.32.2(eslint@8.29.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.29.0) @@ -12942,7 +12952,7 @@ packages: eslint: 8.24.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.24.0) eslint-plugin-react: 7.32.2(eslint@8.24.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.24.0) @@ -12971,7 +12981,7 @@ packages: confusing-browser-globals: 1.0.11 eslint: 8.29.0 eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.22.15)(eslint@8.29.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.58.0)(eslint@8.29.0)(jest@27.5.1)(typescript@4.9.5) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.29.0) eslint-plugin-react: 7.32.2(eslint@8.29.0) @@ -13005,13 +13015,14 @@ packages: dependencies: debug: 4.3.4 eslint: 8.29.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.2 tsconfig-paths: 3.14.2 transitivePeerDependencies: - supports-color + dev: false /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} @@ -13024,7 +13035,7 @@ packages: enhanced-resolve: 5.15.0 eslint: 8.24.0 eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) get-tsconfig: 4.5.0 globby: 13.1.4 is-core-module: 2.12.0 @@ -13035,36 +13046,6 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: true - - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.29.0)(typescript@4.9.4) - debug: 3.2.7(supports-color@5.5.0) - eslint: 8.29.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.29.0) - transitivePeerDependencies: - - supports-color /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0): resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} @@ -13094,7 +13075,6 @@ packages: eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0) transitivePeerDependencies: - supports-color - dev: true /eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.22.15)(eslint@8.29.0): resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} @@ -13111,7 +13091,7 @@ packages: string-natural-compare: 3.0.1 dev: false - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -13121,15 +13101,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.29.0)(typescript@4.9.4) + '@typescript-eslint/parser': 5.58.0(eslint@8.24.0)(typescript@4.9.5) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 - eslint: 8.29.0 + eslint: 8.24.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) has: 1.0.3 is-core-module: 2.12.0 is-glob: 4.0.3 @@ -13335,7 +13315,6 @@ packages: dependencies: eslint: 8.24.0 eslint-visitor-keys: 2.1.0 - dev: true /eslint-utils@3.0.0(eslint@8.29.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} @@ -13345,6 +13324,7 @@ packages: dependencies: eslint: 8.29.0 eslint-visitor-keys: 2.1.0 + dev: false /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} @@ -13416,7 +13396,6 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true /eslint@8.29.0: resolution: {integrity: sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==} @@ -13464,6 +13443,7 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color + dev: false /espree@9.5.1: resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} @@ -14335,7 +14315,6 @@ packages: /get-tsconfig@4.5.0: resolution: {integrity: sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==} - dev: true /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -14438,7 +14417,6 @@ packages: /globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} - dev: true /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -14460,7 +14438,6 @@ packages: ignore: 5.2.4 merge2: 1.4.1 slash: 4.0.0 - dev: true /globby@6.1.0: resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} @@ -14475,7 +14452,6 @@ packages: /globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true /google-auth-library@8.7.0: resolution: {integrity: sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==} @@ -14820,6 +14796,10 @@ packages: webpack: 5.88.2(webpack-cli@5.0.1) dev: false + /htmlencode@0.0.4: + resolution: {integrity: sha512-0uDvNVpzj/E2TfvLLyyXhKBRvF1y84aZsyRxRXFsQobnHaL4pcaXk+Y9cnFlvnxrBLeXDNq/VJBD+ngdBgQG1w==} + dev: false + /htmlparser2@5.0.1: resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} dependencies: @@ -15200,6 +15180,17 @@ packages: wrap-ansi: 6.2.0 dev: true + /intercom-client@4.0.0: + resolution: {integrity: sha512-xtAsO0wnyd46JNRP98/JK0BXIfWNTO/tUk+1op4rnZ+N/DSdhAaBw8ZaJD8bL8ePNhyAMl/Vd6h7kYI/h9riaw==} + engines: {node: '>= v8.0.0'} + dependencies: + axios: 0.24.0 + htmlencode: 0.0.4 + lodash: 4.17.21 + transitivePeerDependencies: + - debug + dev: false + /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -15428,6 +15419,7 @@ packages: /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + dev: false /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} @@ -24208,7 +24200,6 @@ packages: dependencies: '@pkgr/utils': 2.3.1 tslib: 2.6.2 - dev: true /tailwindcss@3.3.1(postcss@8.4.30)(ts-node@10.8.2): resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} @@ -24553,7 +24544,6 @@ packages: dependencies: globalyzer: 0.1.0 globrex: 0.1.2 - dev: true /tiny-hashes@1.0.1: resolution: {integrity: sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==} @@ -24937,6 +24927,7 @@ packages: dependencies: tslib: 1.14.1 typescript: 4.9.4 + dev: false /tsutils@3.21.0(typescript@4.9.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} diff --git a/webapps/ee-api/package.json b/webapps/ee-api/package.json index 80b46c57f..b99904806 100644 --- a/webapps/ee-api/package.json +++ b/webapps/ee-api/package.json @@ -8,9 +8,8 @@ "lint": "next lint" }, "dependencies": { - "is-valid-domain": "^0.1.6", - "@clickhouse/client": "^0.2.4", "@aws-sdk/client-s3": "^3.418.0", + "@clickhouse/client": "^0.2.4", "@types/node": "18.11.12", "@types/react": "18.0.28", "@types/react-dom": "18.0.11", @@ -18,6 +17,8 @@ "eslint": "8.29.0", "eslint-config-next": "13.0.0", "firebase-admin": "^11.11.0", + "intercom-client": "^4.0.0", + "is-valid-domain": "^0.1.6", "json5": "^2.2.3", "jsonwebtoken": "^9.0.2", "juava": "workspace:*", @@ -31,12 +32,12 @@ "tslib": "^2.4.0" }, "devDependencies": { - "typescript": "4.9.4", "@types/crypto-js": "^4.1.1", "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.185", "@types/pg": "^8.6.5", "@types/stripe": "^8.0.417", - "next-transpile-modules": "^10.0.1" + "next-transpile-modules": "^10.0.1", + "typescript": "4.9.4" } } diff --git a/webapps/ee-api/pages/api/marketing-automation/intercom/export.ts b/webapps/ee-api/pages/api/marketing-automation/intercom/export.ts new file mode 100644 index 000000000..5046b8d06 --- /dev/null +++ b/webapps/ee-api/pages/api/marketing-automation/intercom/export.ts @@ -0,0 +1,78 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { withErrorHandler } from "../../../../lib/error-handler"; +import { auth } from "../../../../lib/auth"; +import { assertTrue, getLog, requireDefined } from "juava"; +import { Client, Operators } from "intercom-client"; +import { applicationDb, store } from "../../../../lib/services"; + +const handler = async function handler(req: NextApiRequest, res: NextApiResponse) { + res.setHeader("Access-Control-Allow-Origin", req.headers.origin || "*"); + res.setHeader("Access-Control-Allow-Methods", "*"); + res.setHeader("Access-Control-Allow-Headers", "authorization, content-type, baggage, sentry-trace"); + res.setHeader("Access-Control-Allow-Credentials", "true"); + if (req.method === "OPTIONS") { + res.status(200).end(); + return; + } + const claims = await auth(req, res); + if (!claims) { + return; + } + assertTrue(claims.type === "admin", "Only admins can call this API"); + const client = new Client({ + tokenAuth: { token: requireDefined(process.env.INTERCOM_TOKEN, "INTERCOM_TOKEN env variable is not defined") }, + }); + const lookbackDays = req.query.lookbackDays ? parseInt(req.query.lookbackDays as string) : 30; + const result = await applicationDb.query( + `select * from newjitsu."IntercomEventsExport" where timestamp > now() - interval '${lookbackDays} day'` + ); + getLog().atInfo().log(`Found ${result.rowCount} events to export to Intercom`); + let alreadySent = 0; + let newEvents = 0; + let invalidEvents = 0; + for (const { messageId, timestamp, email, eventName } of result.rows) { + const sentEventsTable = store.getTable("intercom-events:sent"); + const sentEvents = (await sentEventsTable.get(email as string)) || { eventIds: [] }; + if (sentEvents.eventIds.includes(messageId as string)) { + getLog().atInfo().log(`Event ${messageId} was already sent to Intercom`); + alreadySent++; + } else { + getLog().atInfo().log(`Sending event ${eventName} from ${email} to Intercom. Date: ${timestamp.toISOString()}`); + const searchResult = await client.contacts.search({ + data: { + query: { + field: "email", + operator: Operators.EQUALS, + value: email as string, + }, + }, + }); + if (searchResult.data.length === 0) { + getLog().atInfo().log(`Contact ${email} not found in Intercom. Skipping event`); + invalidEvents++; + continue; + } else if (searchResult.data.length > 1) { + getLog().atInfo().log(`Contact ${email} has ${searchResult.data.length} in Intercom, taking the first one`); + } + const userId = searchResult.data.map(d => d.external_id).find(id => id !== undefined); + if (!userId) { + getLog() + .atInfo() + .log(`Non of ${searchResult.data.length} contacts attached to ${email} doesn't have external_id`); + invalidEvents++; + continue; + } + await client.events.create({ + eventName: eventName as string, + createdAt: Math.round((timestamp as Date).getTime() / 1000), + userId: userId, + id: messageId as string, + }); + sentEvents.eventIds.push(messageId); + await sentEventsTable.put(email as string, sentEvents); + } + } + return { ok: true, alreadySent, newEvents, invalidEvents }; +}; + +export default withErrorHandler(handler);