diff --git a/.env.example b/.env.example index 62442e99d..a8186d383 100644 --- a/.env.example +++ b/.env.example @@ -54,9 +54,11 @@ FRESHSALES_CRM_CLOUD_CLIENT_SECRET= # Attio ATTIO_CRM_CLOUD_CLIENT_ID= ATTIO_CRM_CLOUD_CLIENT_SECRET= -#close + +# Close CLOSE_CRM_CLOUD_CLIENT_ID= CLOSE_CRM_CLOUD_CLIENT_SECRET= + # ================================================ # Ticketing # ================================================ diff --git a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx index dc0480787..bb71e4f9a 100644 --- a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx +++ b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx @@ -119,7 +119,7 @@ export default function Page() { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture('api_key_created', { diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx index 2b96b4427..4be8b7044 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx @@ -85,7 +85,7 @@ const CreateUserForm = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx index 4495f96fd..1e49ab5e0 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx @@ -81,7 +81,7 @@ const LoginUserForm = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx b/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx index 03e71fa52..28e2050ff 100644 --- a/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx +++ b/apps/client-ts/src/components/Configuration/Catalog/CatalogWidget.tsx @@ -71,7 +71,7 @@ export function CatalogWidget() { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); }; diff --git a/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx index 3f39d905c..64714dab0 100644 --- a/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx +++ b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx @@ -130,7 +130,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_OAuth2_updated", { id_project: idProject, @@ -159,7 +159,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_OAuth2_created", { id_project: idProject, @@ -201,7 +201,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_API_KEY_updated", { id_project: idProject, @@ -230,7 +230,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_API_KEY_created", { id_project: idProject, @@ -277,7 +277,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_BASIC_AUTH_updated", { id_project: idProject, @@ -307,7 +307,7 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("Connection_strategy_BASIC_AUTH_created", { id_project: idProject, @@ -373,10 +373,9 @@ export function ConnectorDisplay({ item }: ItemDisplayProps) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); - setSwitchEnabled(enabled); } }; diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx index 0a5eeef44..2ed589efc 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/Stepper/stepper-form.tsx @@ -147,8 +147,8 @@ function FirstStepForm({setClose}: {setClose: () => void}) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); posthog?.capture("field_defined", { id_project: idProject, mode: config.DISTRIBUTION @@ -323,8 +323,8 @@ function SecondStepForm({setClose}: {setClose: () => void}) { ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); nextStep(); posthog?.capture("field_mapped", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx index 89e0c2d66..9d20c2c91 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/defineForm.tsx @@ -103,7 +103,7 @@ export function DefineForm({ onClose }: {onClose: () => void}) { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("field_defined", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx index 78befe50f..134aa013e 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/mapForm.tsx @@ -121,7 +121,7 @@ export function MapForm({ onClose, fieldToMap }: {onClose: () => void; fieldToMa ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("field_mapped", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx index 05ff1b49b..e03c12e71 100644 --- a/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx @@ -112,7 +112,7 @@ const [successImporting, setSuccessImporting]=useState(false) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); setShowNewLinkedUserDialog({open: false}) posthog?.capture("linked_account_created", { @@ -155,7 +155,7 @@ const [successImporting, setSuccessImporting]=useState(false) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("batch_linked_account_created", { id_project: idProject, diff --git a/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx index 87b750a53..9323c7975 100644 --- a/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx +++ b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx @@ -101,7 +101,7 @@ const AddWebhook = () => { ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); handleOpenChange(false); posthog?.capture("webhook_created", { diff --git a/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx index 7be05a6b2..4fe4f4b44 100644 --- a/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx +++ b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx @@ -25,7 +25,7 @@ export function useColumns(webhooks: Webhook[] | undefined, setWebhooks: React.D id: webhook_id, active: status, }), - { + { loading: 'Loading...', success: (data: any) => { const index = webhooks!.findIndex(webhook => webhook.id_webhook_endpoint === webhook_id); @@ -44,8 +44,8 @@ export function useColumns(webhooks: Webhook[] | undefined, setWebhooks: React.D ) ; }, - error: 'Error', - }); + error: (err: any) => err.message || 'Error' + }); } const handleCopy = (token: string) => { diff --git a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx index bd5af780d..28e8c65a9 100644 --- a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx +++ b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx @@ -126,7 +126,7 @@ const AddConnectionButton = ({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); posthog?.capture("magic_link_created", { diff --git a/apps/client-ts/src/components/shared/data-table-row-actions.tsx b/apps/client-ts/src/components/shared/data-table-row-actions.tsx index d81354487..00121144f 100644 --- a/apps/client-ts/src/components/shared/data-table-row-actions.tsx +++ b/apps/client-ts/src/components/shared/data-table-row-actions.tsx @@ -56,7 +56,7 @@ export function DataTableRowActions({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); break; case 'api-key': @@ -82,7 +82,7 @@ export function DataTableRowActions({ ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); break; default: diff --git a/apps/client-ts/src/components/shared/team-switcher.tsx b/apps/client-ts/src/components/shared/team-switcher.tsx index 2ec1b460e..5121ad8a7 100644 --- a/apps/client-ts/src/components/shared/team-switcher.tsx +++ b/apps/client-ts/src/components/shared/team-switcher.tsx @@ -129,7 +129,7 @@ export default function TeamSwitcher({ className ,projects}: TeamSwitcherProps) ) ; }, - error: 'Error', + error: (err: any) => err.message || 'Error' }); setShowNewDialog({open: false}) projectForm.reset(); diff --git a/apps/client-ts/src/components/ui/file-uploader.tsx b/apps/client-ts/src/components/ui/file-uploader.tsx index fe4f926a7..b7e2eb922 100644 --- a/apps/client-ts/src/components/ui/file-uploader.tsx +++ b/apps/client-ts/src/components/ui/file-uploader.tsx @@ -151,8 +151,8 @@ export function FileUploader(props: FileUploaderProps) { setFiles([]) return `${target} uploaded` }, - error: `Failed to upload ${target}`, - }) + error: (err: any) => err.message || 'Error' + }); } }, diff --git a/apps/client-ts/src/hooks/create/useCreateApiKey.tsx b/apps/client-ts/src/hooks/create/useCreateApiKey.tsx index 469926b6e..bdd0667ec 100644 --- a/apps/client-ts/src/hooks/create/useCreateApiKey.tsx +++ b/apps/client-ts/src/hooks/create/useCreateApiKey.tsx @@ -21,7 +21,8 @@ const useCreateApiKey = () => { }); if (!response.ok) { - throw new Error('Failed to add api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx b/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx index 169a8dfa2..383c03cab 100644 --- a/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateBatchLinkedUser.tsx @@ -19,7 +19,8 @@ const useCreateBatchLinkedUser = () => { }); if (!response.ok) { - throw new Error('Failed to batch add linked user'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx b/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx index 72f7ab198..d727705e2 100644 --- a/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/create/useCreateConnectionStrategy.tsx @@ -20,7 +20,8 @@ const useCreateConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to add Connection Strategy'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx b/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx index 4d90f688b..e2cdfe666 100644 --- a/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateLinkedUser.tsx @@ -19,7 +19,8 @@ const useCreateLinkedUser = () => { }); if (!response.ok) { - throw new Error('Failed to add linked user'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx b/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx index 473e1263a..231e35bc5 100644 --- a/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx +++ b/apps/client-ts/src/hooks/create/useCreateMagicLink.tsx @@ -21,7 +21,8 @@ const useCreateMagicLink = () => { }); if (!response.ok) { - throw new Error('Failed to generate link'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateProfile.tsx b/apps/client-ts/src/hooks/create/useCreateProfile.tsx index cc1f19bc7..549539db9 100644 --- a/apps/client-ts/src/hooks/create/useCreateProfile.tsx +++ b/apps/client-ts/src/hooks/create/useCreateProfile.tsx @@ -21,7 +21,8 @@ const useCreateProfile = () => { }); if (!response.ok) { - throw new Error('Failed to add profile'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateProject.tsx b/apps/client-ts/src/hooks/create/useCreateProject.tsx index 98e7e0468..c0050a0da 100644 --- a/apps/client-ts/src/hooks/create/useCreateProject.tsx +++ b/apps/client-ts/src/hooks/create/useCreateProject.tsx @@ -19,7 +19,8 @@ const useCreateProject = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateUser.tsx b/apps/client-ts/src/hooks/create/useCreateUser.tsx index a4ac462b2..b35201593 100644 --- a/apps/client-ts/src/hooks/create/useCreateUser.tsx +++ b/apps/client-ts/src/hooks/create/useCreateUser.tsx @@ -21,7 +21,8 @@ const useCreateUser = () => { }); if (!response.ok) { - throw new Error("Email already associated with other account!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useCreateWebhook.tsx b/apps/client-ts/src/hooks/create/useCreateWebhook.tsx index 8bb644674..aa2169df3 100644 --- a/apps/client-ts/src/hooks/create/useCreateWebhook.tsx +++ b/apps/client-ts/src/hooks/create/useCreateWebhook.tsx @@ -20,7 +20,8 @@ const useCreateWebhook = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useDefineField.tsx b/apps/client-ts/src/hooks/create/useDefineField.tsx index 985a39c57..6b75ec468 100644 --- a/apps/client-ts/src/hooks/create/useDefineField.tsx +++ b/apps/client-ts/src/hooks/create/useDefineField.tsx @@ -21,7 +21,8 @@ const useDefineField = () => { }); if (!response.ok) { - throw new Error('Failed to define field mapping'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useMapField.tsx b/apps/client-ts/src/hooks/create/useMapField.tsx index 019d5a069..0227a66cb 100644 --- a/apps/client-ts/src/hooks/create/useMapField.tsx +++ b/apps/client-ts/src/hooks/create/useMapField.tsx @@ -21,7 +21,8 @@ const useMapField = () => { }); if (!response.ok) { - throw new Error('Failed to map field mapping'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx b/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx index bedea3d80..d5dbc6ce4 100644 --- a/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx +++ b/apps/client-ts/src/hooks/create/useRefreshAccessToken.tsx @@ -20,7 +20,8 @@ const useRefreshAccessToken = () => { }); if (!response.ok) { - throw new Error("Login Failed!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx b/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx index 94c8a2459..0784d734c 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteApiKey.tsx @@ -17,7 +17,8 @@ const useDeleteApiKey = () => { }); if (!response.ok) { - throw new Error('Failed to delete api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx b/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx index 64304e31a..9dec95087 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteConnectionStrategy.tsx @@ -17,7 +17,8 @@ const useDeleteConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to delete cs'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx b/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx index 984d07d2d..7796e2dc9 100644 --- a/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx +++ b/apps/client-ts/src/hooks/delete/useDeleteWebhook.tsx @@ -17,7 +17,8 @@ const useDeleteWebhook = () => { }); if (!response.ok) { - throw new Error('Failed to delete api key'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/get/useApiKeys.tsx b/apps/client-ts/src/hooks/get/useApiKeys.tsx index 6bd57e3af..cf211ff8e 100644 --- a/apps/client-ts/src/hooks/get/useApiKeys.tsx +++ b/apps/client-ts/src/hooks/get/useApiKeys.tsx @@ -16,8 +16,9 @@ const useApiKeys = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx b/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx index 8adca921f..b3c587258 100644 --- a/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx +++ b/apps/client-ts/src/hooks/get/useConnectionStrategies.tsx @@ -15,9 +15,10 @@ const useConnectionStrategies = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx b/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx index 5c28ed68c..3e41e2fb5 100644 --- a/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx +++ b/apps/client-ts/src/hooks/get/useConnectionStrategyAuthCredentials.tsx @@ -18,10 +18,11 @@ const useConnectionStrategyAuthCredentials = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }) - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); }; return useMutation({ diff --git a/apps/client-ts/src/hooks/get/useConnections.tsx b/apps/client-ts/src/hooks/get/useConnections.tsx index 166936958..27b4948bc 100644 --- a/apps/client-ts/src/hooks/get/useConnections.tsx +++ b/apps/client-ts/src/hooks/get/useConnections.tsx @@ -16,8 +16,9 @@ const useConnections = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useEvents.tsx b/apps/client-ts/src/hooks/get/useEvents.tsx index 3f726d02b..27d647f84 100644 --- a/apps/client-ts/src/hooks/get/useEvents.tsx +++ b/apps/client-ts/src/hooks/get/useEvents.tsx @@ -20,7 +20,8 @@ const fetchEvents = async (params: PaginationParams): Promise => { }); if (!response.ok) { - throw new Error('Network response was not ok'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); }; diff --git a/apps/client-ts/src/hooks/get/useFieldMappings.tsx b/apps/client-ts/src/hooks/get/useFieldMappings.tsx index d6cb88f54..dcdc3e28a 100644 --- a/apps/client-ts/src/hooks/get/useFieldMappings.tsx +++ b/apps/client-ts/src/hooks/get/useFieldMappings.tsx @@ -16,8 +16,9 @@ const useFieldMappings = () => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); - } + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); }}); diff --git a/apps/client-ts/src/hooks/get/useLinkedUsers.tsx b/apps/client-ts/src/hooks/get/useLinkedUsers.tsx index 484b9575b..54405212d 100644 --- a/apps/client-ts/src/hooks/get/useLinkedUsers.tsx +++ b/apps/client-ts/src/hooks/get/useLinkedUsers.tsx @@ -15,10 +15,11 @@ const useLinkedUsers = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); } }); }; diff --git a/apps/client-ts/src/hooks/get/useProjectConnectors.tsx b/apps/client-ts/src/hooks/get/useProjectConnectors.tsx index e0f330704..a80325efc 100644 --- a/apps/client-ts/src/hooks/get/useProjectConnectors.tsx +++ b/apps/client-ts/src/hooks/get/useProjectConnectors.tsx @@ -19,7 +19,8 @@ const useProjectConnectors = (id: string) => { }, }); if (!response.ok) { - throw new Error('Network response was not ok'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); } diff --git a/apps/client-ts/src/hooks/get/useProjects.tsx b/apps/client-ts/src/hooks/get/useProjects.tsx index 1d65335be..7db671f01 100644 --- a/apps/client-ts/src/hooks/get/useProjects.tsx +++ b/apps/client-ts/src/hooks/get/useProjects.tsx @@ -16,9 +16,10 @@ const useProjects = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useProviderProperties.tsx b/apps/client-ts/src/hooks/get/useProviderProperties.tsx index 9ed5a5bd2..d6c718ec8 100644 --- a/apps/client-ts/src/hooks/get/useProviderProperties.tsx +++ b/apps/client-ts/src/hooks/get/useProviderProperties.tsx @@ -14,9 +14,10 @@ const useProviderProperties = (linkedUserId: string, providerId: string, vertica 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } return response.json(); } }); diff --git a/apps/client-ts/src/hooks/get/useUser.tsx b/apps/client-ts/src/hooks/get/useUser.tsx index 21ecf9f91..36252faf3 100644 --- a/apps/client-ts/src/hooks/get/useUser.tsx +++ b/apps/client-ts/src/hooks/get/useUser.tsx @@ -28,9 +28,9 @@ const useUser = () => { if (!response.ok) { Cookies.remove('access_token') - throw new Error("Fetch User Failed!!") + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } - return response.json(); }; return useMutation({ diff --git a/apps/client-ts/src/hooks/get/useWebhooks.tsx b/apps/client-ts/src/hooks/get/useWebhooks.tsx index 482580a96..7d15bf01d 100644 --- a/apps/client-ts/src/hooks/get/useWebhooks.tsx +++ b/apps/client-ts/src/hooks/get/useWebhooks.tsx @@ -16,10 +16,11 @@ const useWebhooks = () => { 'Authorization': `Bearer ${Cookies.get('access_token')}`, }, }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); + } + return response.json(); } }); }; diff --git a/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx b/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx index cd32638ba..5e0ef5290 100644 --- a/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateConnectionStrategy.tsx @@ -26,7 +26,8 @@ const useUpdateConnectionStrategy = () => { }); if (!response.ok) { - throw new Error('Failed to toggle cs'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); } else { diff --git a/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx b/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx index 219884810..4a0837b56 100644 --- a/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateProjectConnectors.tsx @@ -19,7 +19,8 @@ const useUpdateProjectConnectors = () => { }); if (!response.ok) { - throw new Error('Failed to update catalog option'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx b/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx index de57151db..2d08a028f 100644 --- a/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx +++ b/apps/client-ts/src/hooks/update/useUpdateWebhookStatus.tsx @@ -18,7 +18,8 @@ const useUpdateWebhookStatus = () => { }); if (!response.ok) { - throw new Error('Failed to add project'); + const errorData = await response.json(); + throw new Error(errorData.message || "Unknown error occurred"); } return response.json(); diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index a7f752731..4026fc34e 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -200,7 +200,7 @@ services: # - ./ngrok.yml:/etc/ngrok.yml #ports: # - 4040:4040 - #epends_on: + #depends_on: # api: # condition: service_healthy #network_mode: "host" diff --git a/docs/api-reference/pagination.mdx b/docs/api-reference/pagination.mdx index b1baace0d..82b8a711c 100644 --- a/docs/api-reference/pagination.mdx +++ b/docs/api-reference/pagination.mdx @@ -2,4 +2,4 @@ title: "Pagination" description: "" --- -Panora’s list API methods use cursor-based pagination through the `starting_after` and `ending_before` parameters. Both parameters accept an existing object ID value (see below) and return objects in reverse chronological order. The `ending_before` parameter returns objects listed before the named object. The `starting_after` parameter returns objects listed after the named object. These parameters are mutually exclusive. You can use either the `starting_after` or `ending_before` parameter, but not both simultaneously. \ No newline at end of file +Panora’s list API methods use cursor-based pagination through the `pageSize` and `cursor` parameters. diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 4f8e26aab..7e528d24a 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -649,13 +649,13 @@ model connector_sets { crm_zoho Boolean crm_attio Boolean crm_pipedrive Boolean + crm_zendesk Boolean + crm_close Boolean tcg_zendesk Boolean tcg_jira Boolean tcg_gorgias Boolean tcg_gitlab Boolean tcg_front Boolean - crm_zendesk Boolean - crm_close Boolean projects projects[] } diff --git a/packages/api/scripts/commonObject.sh b/packages/api/scripts/commonObject.sh index 089ca549f..246458fd5 100755 --- a/packages/api/scripts/commonObject.sh +++ b/packages/api/scripts/commonObject.sh @@ -51,12 +51,12 @@ export class ServiceRegistry { getService(integrationId: string): I${ObjectCap}Service { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError(`Service not found for integration ID: ${integrationId}`); } return service; } } -EOF +EOF cat > "services/${objectType}.service.ts" < = await service.add${ObjectCap}( @@ -348,7 +354,13 @@ export class ${ObjectCap}Service { ); return result_${objectType}; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'CREATE_${ObjectCap}_ERROR', + message: '${ObjectCap}Service.add${ObjectCap}() call failed', + cause: error, + }), + ); } } @@ -421,7 +433,13 @@ export class ${ObjectCap}Service { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'GET_${ObjectCap}_ERROR', + message: '${ObjectCap}Service.get${ObjectCap}() call failed', + cause: error, + }), + ); } } @@ -513,7 +531,13 @@ export class ${ObjectCap}Service { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new Unified${VerticalCap}Error({ + name: 'GET_${ObjectCap}S_ERROR', + message: '${ObjectCap}Service.get${ObjectCap}s() call failed', + cause: error, + }), + ); } } } @@ -523,7 +547,7 @@ cat > "sync/sync.service.ts" < { const name = provider.substring(0, 1).toUpperCase() + provider.substring(1) + - objectType.substring(0, 1).toUpperCase() + objectType.substring(1); + objectType.substring(0, 1).toUpperCase() + + objectType.substring(1); const inputObjPattern = new RegExp(`(${name}Input)`); const outputObjPattern = new RegExp(`(${name}Output)`); - if (inputObjPattern.test(fileContent) || outputObjPattern.test(fileContent)) { - return false + if ( + inputObjPattern.test(fileContent) || + outputObjPattern.test(fileContent) + ) { + return false; } return true; }); return possibleImports; - } // Function to generate import statements for new service types function generateImportStatements(possibleProviders, basePath, objectType) { - return possibleProviders.map((serviceName) => { const importPath = `${basePath}/${serviceName}/types`; const name = serviceName.substring(0, 1).toUpperCase() + serviceName.substring(1) + - objectType.substring(0, 1).toUpperCase() + objectType.substring(1); + objectType.substring(0, 1).toUpperCase() + + objectType.substring(1); return `import { ${name}Input, ${name}Output } from '${replaceRelativePaths( importPath, )}';`; @@ -85,9 +93,14 @@ function updateTargetFile(file, importStatements, serviceNames, objectType) { // Update OriginalObjectTypeInput // checking whether OriginalObjectTypeInput assigns null - const inputNullRegex = new RegExp(`(export type Original${objectType}Input = null)`); + const inputNullRegex = new RegExp( + `(export type Original${objectType}Input = null)`, + ); if (inputNullRegex.test(fileContent)) { - fileContent = fileContent.replace(inputNullRegex, `export type Original${objectType}Input =`) + fileContent = fileContent.replace( + inputNullRegex, + `export type Original${objectType}Input =`, + ); } const inputRegex = new RegExp(`(export type Original${objectType}Input =)`); if (inputRegex.test(fileContent)) { @@ -100,9 +113,14 @@ function updateTargetFile(file, importStatements, serviceNames, objectType) { // Update OriginalObjectTypeOutput // checking whether OriginalObjectTypeInput assigns null - const outputNullRegex = new RegExp(`(export type Original${objectType}Input = null)`); + const outputNullRegex = new RegExp( + `(export type Original${objectType}Input = null)`, + ); if (outputNullRegex.test(fileContent)) { - fileContent = fileContent.replace(outputNullRegex, `export type Original${objectType}Output =`) + fileContent = fileContent.replace( + outputNullRegex, + `export type Original${objectType}Output =`, + ); } const outputRegex = new RegExp( @@ -315,6 +333,82 @@ function updateEnumFile(enumFilePath, newServiceDirs, vertical) { console.error(`Could not find enum ${enumName} in file.`); } } + +// New function to update init.sql +function updateInitSQLFile(initSQLFile, newServiceDirs, vertical) { + let fileContent = fs.readFileSync(initSQLFile, 'utf8'); + const insertPoint = fileContent.indexOf( + 'CONSTRAINT PK_project_connector PRIMARY KEY', + ); + + if (insertPoint === -1) { + console.error( + `Could not find the PRIMARY KEY constraint in ${initSQLFile}`, + ); + return; + } + + let newLines = ''; + newServiceDirs.forEach((serviceName) => { + const columnName = `${vertical.toLowerCase()}_${serviceName.toLowerCase()}`; + newLines += ` ${columnName} boolean NOT NULL,\n`; + }); + + fileContent = [ + fileContent.slice(0, insertPoint), + newLines, + fileContent.slice(insertPoint), + ].join(''); + + fs.writeFileSync(initSQLFile, fileContent); +} + +// New function to update seed.sql +function updateSeedSQLFile(seedSQLFile, newServiceDirs, vertical) { + let fileContent = fs.readFileSync(seedSQLFile, 'utf8'); + + const tableInsertPoint = fileContent.indexOf('INSERT INTO connector_sets'); + if (tableInsertPoint === -1) { + console.error( + `Could not find the INSERT INTO connector_sets statement in ${seedSQLFile}`, + ); + return; + } + + const columnInsertPoint = fileContent.indexOf('(', tableInsertPoint); + const valuesInsertPoint = fileContent.indexOf('VALUES', columnInsertPoint); + const rowsInsertPoint = fileContent.indexOf('(', valuesInsertPoint); + + if ( + columnInsertPoint === -1 || + valuesInsertPoint === -1 || + rowsInsertPoint === -1 + ) { + console.error( + `Could not find the column or values insert points in ${seedSQLFile}`, + ); + return; + } + + let newColumns = ''; + let newValues = ''; + newServiceDirs.forEach((serviceName) => { + const columnName = `${vertical.toLowerCase()}_${serviceName.toLowerCase()}`; + newColumns += `${columnName}, `; + newValues += 'TRUE, '; + }); + + const updatedFileContent = [ + fileContent.slice(0, columnInsertPoint + 1), + newColumns, + fileContent.slice(columnInsertPoint + 1, rowsInsertPoint + 1), + newValues, + fileContent.slice(rowsInsertPoint + 1), + ].join(''); + + fs.writeFileSync(seedSQLFile, updatedFileContent); +} + // Main script logic function updateObjectTypes(baseDir, objectType, vertical) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -330,14 +424,15 @@ function updateObjectTypes(baseDir, objectType, vertical) { __dirname, '../../shared/src/connectors/index.ts', ); - const enumFilePath = path.join(__dirname, '../../shared/src/connectors/enum.ts'); + const enumFilePath = path.join( + __dirname, + '../../shared/src/connectors/enum.ts', + ); const currentProviders = extractArrayFromFile( providersFilePath, `${vertical.toUpperCase()}_PROVIDERS`, ); - - // Compare the extracted arrays with the new service names const newProviders = newServiceDirs.filter( (service) => !currentProviders.includes(service), @@ -369,7 +464,11 @@ function updateObjectTypes(baseDir, objectType, vertical) { // Call updateMappingsFile to update the mappings file with new services updateMappingsFile(mappingsFile, newServiceDirs, objectType, vertical); - const possibleProviderForImportStatements = getProvidersForImportStatements(targetFile, newServiceDirs, objectType); + const possibleProviderForImportStatements = getProvidersForImportStatements( + targetFile, + newServiceDirs, + objectType, + ); // Continue with the rest of the updateObjectTypes function... const importStatements = generateImportStatements( @@ -378,7 +477,19 @@ function updateObjectTypes(baseDir, objectType, vertical) { objectType, ); // console.log(importStatements) - updateTargetFile(targetFile, importStatements, possibleProviderForImportStatements, objectType); + updateTargetFile( + targetFile, + importStatements, + possibleProviderForImportStatements, + objectType, + ); + + // Update SQL files + const initSQLFile = path.join(__dirname, './init.sql'); + updateInitSQLFile(initSQLFile, newServiceDirs, slugFromCategory(vertical)); + + const seedSQLFile = path.join(__dirname, './seed.sql'); + updateSeedSQLFile(seedSQLFile, newServiceDirs, slugFromCategory(vertical)); } // Example usage for ticketing/team diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 244189d20..6eb0d80c0 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -416,6 +416,8 @@ CREATE TABLE connector_sets crm_zoho boolean NOT NULL, crm_attio boolean NOT NULL, crm_pipedrive boolean NOT NULL, + crm_zendesk boolean NOT NULL, + crm_close boolean NOT NULL, tcg_zendesk boolean NOT NULL, tcg_jira boolean NOT NULL, tcg_gorgias boolean NOT NULL, @@ -581,7 +583,6 @@ CREATE TABLE fs_folders CREATE INDEX FK_fs_folder_driveID ON fs_folders ( id_fs_drive -); CREATE INDEX FK_fs_folder_permissionID ON fs_folders ( @@ -592,9 +593,6 @@ CREATE INDEX FK_fs_folder_permissionID ON fs_folders - - - -- ************************************** crm_contacts CREATE TABLE crm_contacts @@ -1437,8 +1435,4 @@ CREATE INDEX id_job_jobs_status_history ON jobs_status_history COMMENT ON COLUMN jobs_status_history.previous_status IS 'void when first initialization'; -COMMENT ON COLUMN jobs_status_history.new_status IS 'pending, retry_scheduled, failed, success'; - - - - +COMMENT ON COLUMN jobs_status_history.new_status IS 'pending, retry_scheduled, failed, success'; \ No newline at end of file diff --git a/packages/api/scripts/oauthConnector.js b/packages/api/scripts/oauthConnector.js index fee1bd590..5f4256696 100755 --- a/packages/api/scripts/oauthConnector.js +++ b/packages/api/scripts/oauthConnector.js @@ -35,10 +35,16 @@ function createServiceFile(vertical, provider) { // Define the content of the service file const serviceFileContent = ` -import { Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -160,7 +166,18 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne } return db_res; } catch (error) { - handleServiceError(error, this.logger, '${provider}', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_${verticalUpper}', + message: \`${providerUpper}ConnectionService.handleCallback() call failed ---> \${format3rdPartyError( + '${provider}', + Action.oauthCallback, + ActionType.POST, + )}\`, + cause: error, + }), + this.logger, + ); } } @@ -181,10 +198,8 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne { headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: \`Basic ${Buffer.from( - `CREDENTIALS.CLIENT_ID}:\${ - CREDENTIALS.CLIENT_SECRET - }`, + Authorization: \`Basic \${Buffer.from( + \`\${CREDENTIALS.CLIENT_ID}:\${CREDENTIALS.CLIENT_SECRET}\`, ).toString('base64')}\`, }, }, @@ -204,7 +219,18 @@ export class ${providerUpper}ConnectionService implements I${verticalUpper}Conne }); this.logger.log('OAuth credentials updated : ${provider} '); } catch (error) { - handleServiceError(error, this.logger, '${provider}', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_${verticalUpper}', + message: \`${providerUpper}ConnectionService.handleTokenRefresh() call failed ---> \${format3rdPartyError( + '${provider}', + Action.oauthCallback, + ActionType.POST, + )}\`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 6cff60571..620985342 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, tcg_zendesk, tcg_gorgias, tcg_jira, tcg_gitlab, tcg_front, crm_zendesk ,crm_close ) VALUES +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pool', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/auth/auth.service.ts b/packages/api/src/@core/auth/auth.service.ts index bfb90b677..bb9ae6b85 100644 --- a/packages/api/src/@core/auth/auth.service.ts +++ b/packages/api/src/@core/auth/auth.service.ts @@ -1,8 +1,4 @@ -import { - BadRequestException, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { CreateUserDto } from './dto/create-user.dto'; import { PrismaService } from '../prisma/prisma.service'; @@ -10,7 +6,7 @@ import * as bcrypt from 'bcrypt'; import * as crypto from 'crypto'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { AuthError, throwTypedError } from '@@core/utils/errors'; import { LoginDto } from './dto/login.dto'; import { VerifyUserDto } from './dto/verify-user.dto'; import { ProjectsService } from '@@core/projects/projects.service'; @@ -30,9 +26,17 @@ export class AuthService { try { return await this.prisma.users.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'GET_USERS_ERROR', + message: 'AuthService.getUsers() call failed', + cause: error, + }), + this.logger, + ); } } + async verifyUser(verifyUser: VerifyUserDto) { try { const user = await this.prisma.users.findUnique({ @@ -42,22 +46,19 @@ export class AuthService { }); if (!user) { - throw new UnauthorizedException('user does not exist!'); + throw new ReferenceError('User undefined!'); } return verifyUser; - - // const projects = await this.prisma.projects.findMany({ - // where: { - // id_user: user.id_user, - // }, - // }); - // return { - // ...user, - // projects: projects, - // }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'VERIFY_USER_ERROR', + message: 'AuthService.verifyUser() call failed', + cause: error, + }), + this.logger, + ); } } @@ -69,7 +70,14 @@ export class AuthService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'GET_API_KEYS_ERROR', + message: 'AuthService.getApiKeys() call failed', + cause: error, + }), + this.logger, + ); } } @@ -80,12 +88,22 @@ export class AuthService { }); if (foundUser) { - throw new BadRequestException('Email is already exists!!'); + new AuthError({ + name: 'EMAIL_ALREADY_EXISTS_ERROR', + message: `Email already exists for user with email=${user.email}`, + }); } return await this.createUser(user); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'REGISTER_USER_ERROR', + message: 'AuthService.register() call failed', + cause: error, + }), + this.logger, + ); } } @@ -104,15 +122,20 @@ export class AuthService { created_at: new Date(), }, }); - const pro = await this.projectService.createProject({ + await this.projectService.createProject({ name: 'Project 1', id_user: user_.id_user, }); - this.logger.log('proj is ' + JSON.stringify(pro)); return user_; } catch (error) { - console.log(error); - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'CREATE_USER_ERROR', + message: 'AuthService.createUser() call failed', + cause: error, + }), + this.logger, + ); } } @@ -124,15 +147,18 @@ export class AuthService { }, }); + if (!foundUser) { + throw new ReferenceError('User undefined!'); + } + const project = await this.prisma.projects.findFirst({ where: { id_user: foundUser.id_user, }, }); - this.logger.log('Project found (login) is ' + JSON.stringify(project)); - if (!foundUser) { - throw new UnauthorizedException('user does not exist!'); + if (!project) { + throw new ReferenceError('Project undefined!'); } const isEq = await bcrypt.compare( @@ -140,7 +166,10 @@ export class AuthService { foundUser.password_hash, ); - if (!isEq) throw new UnauthorizedException('Invalid credentials.'); + if (!isEq) + throw new ReferenceError( + 'Bcrypt Invalid credentials, mismatch in password.', + ); const { ...userData } = foundUser; @@ -164,7 +193,14 @@ export class AuthService { }), // token used to generate api keys }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'LOGIN_USER_ERROR', + message: 'AuthService.login() call failed', + cause: error, + }), + this.logger, + ); } } @@ -189,12 +225,30 @@ export class AuthService { }), }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'REFRESH_ACCESS_TOKEN_ERROR', + message: 'AuthService.refreshAccessToken() call failed', + cause: error, + }), + this.logger, + ); } } hashApiKey(apiKey: string): string { - return crypto.createHash('sha256').update(apiKey).digest('hex'); + try { + return crypto.createHash('sha256').update(apiKey).digest('hex'); + } catch (error) { + throwTypedError( + new AuthError({ + name: 'HASH_API_KEY_ERROR', + message: 'AuthService.hashApiKey() call failed', + cause: error, + }), + this.logger, + ); + } } //must be called only if user is logged in @@ -214,7 +268,14 @@ export class AuthService { }), }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'GENERATE_API_KEY_ERROR', + message: 'AuthService.generateApiKey() call failed', + cause: error, + }), + this.logger, + ); } } @@ -228,21 +289,17 @@ export class AuthService { where: { id_project: projectId }, }); if (!foundProject) { - throw new UnauthorizedException( - 'project not found inside api key function generation', - ); + throw new ReferenceError('project undefined'); } const foundUser = await this.prisma.users.findUnique({ where: { id_user: userId }, }); if (!foundUser) { - throw new UnauthorizedException( - 'user not found inside api key function generation', - ); + throw new ReferenceError('user undefined'); } /*if (foundProject.id_organization !== foundUser.id_organization) { - throw new Error('User is not inside the project'); + throw new ReferenceError('User is not inside the project'); }*/ // Generate a new API key (use a secure method for generation) const { access_token } = await this.generateApiKey(projectId, userId); @@ -258,21 +315,39 @@ export class AuthService { }, }); if (!new_api_key) { - throw new UnauthorizedException('api keys issue to add to db'); + throw new ReferenceError('api key undefined'); } return { api_key: access_token, ...new_api_key }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'GENERATE_API_KEY_ERROR', + message: 'AuthService.generateApiKeyForUser() call failed', + cause: error, + }), + this.logger, + ); } } async deleteApiKey(apiKeyId: string) { - return await this.prisma.api_keys.delete({ - where: { - id_api_key: apiKeyId, - }, - }); + try { + return await this.prisma.api_keys.delete({ + where: { + id_api_key: apiKeyId, + }, + }); + } catch (error) { + throwTypedError( + new AuthError({ + name: 'DELETE_API_KEY_ERROR', + message: 'AuthService.deleteApiKey() call failed', + cause: error, + }), + this.logger, + ); + } } async validateApiKey(apiKey: string): Promise { @@ -288,24 +363,32 @@ export class AuthService { api_key_hash: apiKey, }, }); + if (!saved_api_key) { - throw new UnauthorizedException('Failed to fetch API key from DB'); + throw new ReferenceError('Api Key undefined'); } if (String(decoded.projectId) !== String(saved_api_key.id_project)) { - throw new UnauthorizedException( - 'Failed to validate API key: projectId invalid.', + throw new ReferenceError( + 'Failed to validate API key: projectId mismatch.', ); } // Validate that the JWT payload matches the provided userId and projectId if (String(decoded.sub) !== String(saved_api_key.id_user)) { - throw new UnauthorizedException( - 'Failed to validate API key: userId invalid.', + throw new ReferenceError( + 'Failed to validate API key: userId mismatch.', ); } return true; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new AuthError({ + name: 'VALIDATE_API_KEY_ERROR', + message: 'AuthService.validateApiKey() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts index b221efdfb..94a905c59 100644 --- a/packages/api/src/@core/connections-strategies/connections-strategies.service.ts +++ b/packages/api/src/@core/connections-strategies/connections-strategies.service.ts @@ -1,5 +1,10 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; +import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; +import { + ConnectionStrategiesError, + throwTypedError, +} from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { @@ -30,18 +35,31 @@ export class ConnectionsStrategiesService { private prisma: PrismaService, private crypto: EncryptionService, private configService: ConfigService, + private logger: LoggerService, ) {} async isCustomCredentials(projectId: string, type: string) { - const res = await this.prisma.connection_strategies.findFirst({ - where: { - id_project: projectId, - type: type, - status: true, - }, - }); - if (!res) return false; - return res.status; + try { + const res = await this.prisma.connection_strategies.findFirst({ + where: { + id_project: projectId, + type: type, + status: true, + }, + }); + if (!res) return false; + return res.status; + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'CUSTOM_CREDENTIALS_ERROR', + message: + 'ConnectionsStrategiesService.isCustomCredentials() call failed', + cause: error, + }), + this.logger, + ); + } } async createConnectionStrategy( @@ -50,51 +68,67 @@ export class ConnectionsStrategiesService { attributes: string[], values: string[], ) { - const checkCSDuplicate = await this.prisma.connection_strategies.findFirst({ - where: { - id_project: projectId, - type: type, - }, - }); - if (checkCSDuplicate) - throw new Error('The Connection Strategy already exists!'); + try { + const checkCSDuplicate = + await this.prisma.connection_strategies.findFirst({ + where: { + id_project: projectId, + type: type, + }, + }); + if (checkCSDuplicate) + throw new ConnectionStrategiesError({ + name: 'CONNECTION_STRATEGY_ALREADY_EXISTS', + message: `Connection strategy already exists for projectId=${projectId} and type=${type}`, + }); - const cs = await this.prisma.connection_strategies.create({ - data: { - id_connection_strategy: uuidv4(), - id_project: projectId, - type: type, - status: true, - }, - }); - const entity = await this.prisma.cs_entities.create({ - data: { - id_cs_entity: uuidv4(), - id_connection_strategy: cs.id_connection_strategy, - }, - }); - for (let i = 0; i < attributes.length; i++) { - const attribute_slug = attributes[i]; - const value = values[i]; - //create all attributes (for oauth => client_id, client_secret) - const attribute_ = await this.prisma.cs_attributes.create({ + const cs = await this.prisma.connection_strategies.create({ data: { - id_cs_attribute: uuidv4(), - id_cs_entity: entity.id_cs_entity, - attribute_slug: attribute_slug, - data_type: 'string', + id_connection_strategy: uuidv4(), + id_project: projectId, + type: type, + status: true, }, }); - const value_ = await this.prisma.cs_values.create({ + const entity = await this.prisma.cs_entities.create({ data: { - id_cs_value: uuidv4(), - value: this.crypto.encrypt(value), - id_cs_attribute: attribute_.id_cs_attribute, + id_cs_entity: uuidv4(), + id_connection_strategy: cs.id_connection_strategy, }, }); - } + for (let i = 0; i < attributes.length; i++) { + const attribute_slug = attributes[i]; + const value = values[i]; + //create all attributes (for oauth => client_id, client_secret) + const attribute_ = await this.prisma.cs_attributes.create({ + data: { + id_cs_attribute: uuidv4(), + id_cs_entity: entity.id_cs_entity, + attribute_slug: attribute_slug, + data_type: 'string', + }, + }); + const value_ = await this.prisma.cs_values.create({ + data: { + id_cs_value: uuidv4(), + value: this.crypto.encrypt(value), + id_cs_attribute: attribute_.id_cs_attribute, + }, + }); + } - return cs; + return cs; + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'CREATE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.createConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); + } } async toggle(id_cs: string) { @@ -104,7 +138,7 @@ export class ConnectionsStrategiesService { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); // Toggle the 'active' value const updatedCs = await this.prisma.connection_strategies.update({ where: { @@ -117,7 +151,14 @@ export class ConnectionsStrategiesService { return updatedCs; } catch (error) { - throw new Error(error); + throwTypedError( + new ConnectionStrategiesError({ + name: 'TOGGLE_CONNECTION_STRATEGY_ERROR', + message: 'ConnectionsStrategiesService.toggle() call failed', + cause: error, + }), + this.logger, + ); } } @@ -134,12 +175,15 @@ export class ConnectionsStrategiesService { type: type, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); const entity = await this.prisma.cs_entities.findFirst({ where: { id_connection_strategy: cs.id_connection_strategy, }, }); + if (!entity) + throw new ReferenceError('Connection strategy entity undefined !'); + const authValues: string[] = []; for (let i = 0; i < attributes.length; i++) { const attribute_slug = attributes[i]; @@ -150,13 +194,15 @@ export class ConnectionsStrategiesService { attribute_slug: attribute_slug, }, }); - if (!attribute_) throw new Error('No attribute found !'); + if (!attribute_) + throw new ReferenceError('Connection Strategy Attribute undefined !'); const value_ = await this.prisma.cs_values.findFirst({ where: { id_cs_attribute: attribute_.id_cs_attribute, }, }); - if (!value_) throw new Error('No value found !'); + if (!value_) + throw new ReferenceError('Connection Strategy Value undefined !'); authValues.push(this.crypto.decrypt(value_.value)); } return authValues; @@ -286,38 +332,50 @@ export class ConnectionsStrategiesService { } async getCredentials(projectId: string, type: string) { - const isCustomCred = await this.isCustomCredentials(projectId, type); - const provider = extractProvider(type); - const vertical = extractVertical(type); - //TODO: extract sofwtaremode - if (!vertical) - throw new Error(`vertical not found for provider ${provider}`); - const authStrategy = extractAuthMode(type); - if (!authStrategy) - throw new Error(`auth strategy not found for provider ${provider}`); - - if (isCustomCred) { - //customer is using custom credentials (set in the webapp UI) - //fetch the right credentials - return await this.getCustomCredentialsData( - projectId, - type, - provider, - vertical, - authStrategy, - ); - } else { - // type is of form = HUBSPOT_CRM_CLOUD_OAUTH so we must extract the parts - return this.getEnvData( - provider, - vertical, - authStrategy, - SoftwareMode.cloud, + try { + const isCustomCred = await this.isCustomCredentials(projectId, type); + const provider = extractProvider(type); + const vertical = extractVertical(type); + //TODO: extract sofwtaremode + if (!vertical) + throw new ReferenceError(`vertical not found for provider ${provider}`); + const authStrategy = extractAuthMode(type); + if (!authStrategy) + throw new ReferenceError( + `auth strategy not found for provider ${provider}`, + ); + + if (isCustomCred) { + //customer is using custom credentials (set in the webapp UI) + //fetch the right credentials + return await this.getCustomCredentialsData( + projectId, + type, + provider, + vertical, + authStrategy, + ); + } else { + // type is of form = HUBSPOT_CRM_CLOUD_OAUTH so we must extract the parts + return this.getEnvData( + provider, + vertical, + authStrategy, + SoftwareMode.cloud, + ); + } + } catch (error) { + throwTypedError( + new ConnectionStrategiesError({ + name: 'GET_CREDENTIALS_ERROR', + message: 'ConnectionsStrategiesService.getCredentials() call failed', + cause: error, + }), + this.logger, ); } } - // Fetching all connection strategies for Project async getConnectionStrategiesForProject(projectId: string) { try { return await this.prisma.connection_strategies.findMany({ @@ -326,11 +384,18 @@ export class ConnectionsStrategiesService { }, }); } catch (error) { - throw new Error('Connection Strategies for projectID is not found!'); + throwTypedError( + new ConnectionStrategiesError({ + name: 'GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR', + message: + 'ConnectionsStrategiesService.getConnectionStrategiesForProject() call failed', + cause: error, + }), + this.logger, + ); } } - // update connection strategy async updateConnectionStrategy( id_cs: string, status: boolean, @@ -338,13 +403,13 @@ export class ConnectionsStrategiesService { values: string[], ) { try { - console.log('In updateAPI xzx'); const cs = await this.prisma.connection_strategies.findFirst({ where: { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); + const updateCS = await this.prisma.connection_strategies.update({ where: { id_connection_strategy: id_cs, @@ -360,6 +425,9 @@ export class ConnectionsStrategiesService { }, }); + if (!id_cs_entity) + throw new ReferenceError('Connection strategy entity undefined !'); + for (let i = 0; i < attributes.length; i++) { const attribute_slug = attributes[i]; const value = values[i]; @@ -383,13 +451,18 @@ export class ConnectionsStrategiesService { } return cs; } catch (error) { - console.log('Error xzx'); - console.log(error); - throw new Error('Update Failed'); + throwTypedError( + new ConnectionStrategiesError({ + name: 'UPDATE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.updateConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); } } - // Delete connection strategy async deleteConnectionStrategy(id_cs: string) { try { const cs = await this.prisma.connection_strategies.findFirst({ @@ -397,13 +470,15 @@ export class ConnectionsStrategiesService { id_connection_strategy: id_cs, }, }); - if (!cs) throw new Error('No connection strategies found !'); + if (!cs) throw new ReferenceError('Connection strategy undefined !'); const { id_cs_entity } = await this.prisma.cs_entities.findFirst({ where: { id_connection_strategy: id_cs, }, }); + if (!id_cs_entity) + throw new ReferenceError('Connection strategy entity undefined !'); const attributes = await this.prisma.cs_attributes.findMany({ where: { @@ -444,7 +519,15 @@ export class ConnectionsStrategiesService { return deleteCS; } catch (error) { - throw new Error('Update Failed'); + throwTypedError( + new ConnectionStrategiesError({ + name: 'DELETE_CONNECTION_STRATEGY_ERROR', + message: + 'ConnectionsStrategiesService.deleteConnectionStrategy() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/@utils/index.ts b/packages/api/src/@core/connections/@utils/index.ts index f2c078cf7..7d1f9777d 100644 --- a/packages/api/src/@core/connections/@utils/index.ts +++ b/packages/api/src/@core/connections/@utils/index.ts @@ -1,3 +1,4 @@ +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { PrismaClient } from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; @@ -17,21 +18,24 @@ export class ConnectionUtils { token: string, ): Promise { try { - // console.log('token is ' + token); - if (!token) - throw new Error('token provided for connection token is invalid'); const res = await this.prisma.connections.findFirst({ where: { connection_token: token, }, }); - if (!res) throw new Error(`connection not found for token ${token}`); + if (!res) throw new ReferenceError(`Connection undefined for token ${token}`); return { linkedUserId: res.id_linked_user, remoteSource: res.provider_slug, }; } catch (error) { - throw new Error(error); + throwTypedError(new ConnectionsError( + { + name: "GET_CONNECTION_FROM_CONNECTION_TOKEN_ERROR", + message: "ConnectionUtils.getConnectionMetadataFromConnectionToken() call failed", + cause: error + } + )) } } diff --git a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts index 82fb92811..9a595fc58 100644 --- a/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts +++ b/packages/api/src/@core/connections/accounting/services/accounting.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -43,7 +43,7 @@ export class AccountingConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -73,7 +73,15 @@ export class AccountingConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_ACCOUNTING', + message: + 'AccountingConnectionsService.handleAccountngCallback() call failed', + cause: error, + }), + this.logger, + ); } } @@ -88,7 +96,7 @@ export class AccountingConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -98,7 +106,15 @@ export class AccountingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_ACCOUNTING', + message: + 'AccountingConnectionsService.handleAccountingTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts b/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts index 62357bd51..c2a5caf06 100644 --- a/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts +++ b/packages/api/src/@core/connections/accounting/services/freeagent/freeagent.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,7 +138,17 @@ export class FreeagentConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'freeagent', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `FreeagentConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "freeagent", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -181,7 +191,17 @@ export class FreeagentConnectionService }); this.logger.log('OAuth credentials updated : freeagent '); } catch (error) { - handleServiceError(error, this.logger, 'freeagent', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `FreeagentConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "freeagent", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts b/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts index b07e6ab46..00349c6ae 100644 --- a/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts +++ b/packages/api/src/@core/connections/accounting/services/freshbooks/freshbooks.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,12 +137,17 @@ export class FreshbooksConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'freshbooks', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `FreshbooksConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "freshbooks", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -186,7 +191,17 @@ export class FreshbooksConnectionService }); this.logger.log('OAuth credentials updated : freshbooks '); } catch (error) { - handleServiceError(error, this.logger, 'freshbooks', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `FreshbooksConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "freshbooks", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts b/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts index 44048e4fd..8c01b991d 100644 --- a/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts +++ b/packages/api/src/@core/connections/accounting/services/moneybird/moneybird.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,7 +138,17 @@ export class MoneybirdConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'moneybird', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `MoneybirdConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "moneybird", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -179,7 +189,17 @@ export class MoneybirdConnectionService }); this.logger.log('OAuth credentials updated : moneybird '); } catch (error) { - handleServiceError(error, this.logger, 'moneybird', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `MoneybirdConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "moneybird", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts index 91d22333a..d6a633255 100644 --- a/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts +++ b/packages/api/src/@core/connections/accounting/services/pennylane/pennylane.service.ts @@ -1,7 +1,13 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,7 +142,18 @@ export class PennylaneConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'pennylane', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_ACCOUNTING', + message: `PennylaneConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + 'pennylane', + Action.oauthCallback, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -179,7 +196,18 @@ export class PennylaneConnectionService }); this.logger.log('OAuth credentials updated : pennylane '); } catch (error) { - handleServiceError(error, this.logger, 'pennylane', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_ACCOUNTING', + message: `PennylaneConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + 'pennylane', + Action.oauthRefresh, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts b/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts index cd4647093..c1b683838 100644 --- a/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts +++ b/packages/api/src/@core/connections/accounting/services/quickbooks/quickbooks.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -138,12 +138,17 @@ export class QuickbooksConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'quickbooks', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `QuickbooksConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "quickbooks", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -182,7 +187,17 @@ export class QuickbooksConnectionService }); this.logger.log('OAuth credentials updated : quickbooks '); } catch (error) { - handleServiceError(error, this.logger, 'quickbooks', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `QuickbooksConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "quickbooks", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/registry.service.ts b/packages/api/src/@core/connections/accounting/services/registry.service.ts index 3125b3a83..5c614b98e 100644 --- a/packages/api/src/@core/connections/accounting/services/registry.service.ts +++ b/packages/api/src/@core/connections/accounting/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IAccountingConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts b/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts index 6fac99ed5..7b0543276 100644 --- a/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts +++ b/packages/api/src/@core/connections/accounting/services/sage/sage.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -135,7 +135,17 @@ export class SageConnectionService implements IAccountingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'sage', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `SageConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "sage", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class SageConnectionService implements IAccountingConnectionService { }); this.logger.log('OAuth credentials updated : sage '); } catch (error) { - handleServiceError(error, this.logger, 'sage', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `SageConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "sage", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts b/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts index 7e695e705..3c7146481 100644 --- a/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts +++ b/packages/api/src/@core/connections/accounting/services/wave_financial/wave_financial.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -143,12 +143,17 @@ export class WaveFinancialConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'wave_financial', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_ACCOUNTING", + message: `WaveFinancialConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "wave_financial", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -194,12 +199,17 @@ export class WaveFinancialConnectionService }); this.logger.log('OAuth credentials updated : wave_financial '); } catch (error) { - handleServiceError( - error, - this.logger, - 'wave_financial', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_ACCOUNTING", + message: `WaveFinancialConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "wave_financial", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index 3eeb4cba4..e468ce1e2 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -9,11 +9,11 @@ import { import { Response } from 'express'; import { CrmConnectionsService } from './crm/services/crm.connection.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { PrismaService } from '@@core/prisma/prisma.service'; import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; import { TicketingConnectionsService } from './ticketing/services/ticketing.connection.service'; -import { ConnectorCategory } from '@panora/shared'; +import { ConnectorCategory, CONNECTORS_METADATA } from '@panora/shared'; import { AccountingConnectionsService } from './accounting/services/accounting.connection.service'; import { MarketingAutomationConnectionsService } from './marketingautomation/services/marketingautomation.connection.service'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; @@ -35,9 +35,9 @@ export class ConnectionsController { private readonly ticketingConnectionsService: TicketingConnectionsService, private readonly accountingConnectionsService: AccountingConnectionsService, private readonly marketingAutomationConnectionsService: MarketingAutomationConnectionsService, - private readonly coreSyncService: CoreSyncService, private logger: LoggerService, private prisma: PrismaService, + private coreSync: CoreSyncService, ) { this.logger.setContext(ConnectionsController.name); } @@ -59,13 +59,15 @@ export class ConnectionsController { ) { try { if (!state) - throw new NotFoundError( - `No Callback Params found for state, found ${state}`, - ); + new ConnectionsError({ + name: 'OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR', + message: `No Callback Params found for state, found ${state}`, + }); if (!code) - throw new NotFoundError( - `No Callback Params found for code, found ${code}`, - ); + new ConnectionsError({ + name: 'OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR', + message: `No Callback Params found for code, found ${code}`, + }); const stateData: StateDataType = JSON.parse(decodeURIComponent(state)); const { projectId, vertical, linkedUserId, providerName, returnUrl } = @@ -112,16 +114,28 @@ export class ConnectionsController { ); break; } - // Performing Core Sync Service - this.coreSyncService.initialSync( - vertical.toLowerCase(), - providerName, - linkedUserId, - projectId, - ); res.redirect(returnUrl); + if ( + CONNECTORS_METADATA[vertical.toLowerCase()][providerName.toLowerCase()] + .active !== false + ) { + // Performing Core Sync Service for active connectors + await this.coreSync.initialSync( + vertical.toLowerCase(), + providerName, + linkedUserId, + projectId, + ); + } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'OAUTH_CALLBACK_ERROR', + message: 'ConnectionsController.handleCallback() call failed', + cause: error, + }), + this.logger, + ); } } @@ -137,11 +151,18 @@ export class ConnectionsController { @Query('state') state: string, ) { try { - if (!account) throw new Error('account prop not found'); + if (!account) throw new ReferenceError('account prop not found'); const params = `?client_id=${client_id}&response_type=${response_type}&redirect_uri=${redirect_uri}&state=${state}&nonce=${nonce}&scope=${scope}`; res.redirect(`https://${account}.gorgias.com/oauth/authorize${params}`); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'OAUTH_CALLBACK_ERROR', + message: 'ConnectionsController.handleGorgiasAuthUrl() call failed', + cause: error, + }), + this.logger, + ); } } @@ -153,11 +174,23 @@ export class ConnectionsController { @UseGuards(JwtAuthGuard) @Get() async getConnections(@Request() req: any) { - const { id_project } = req.user; - return await this.prisma.connections.findMany({ - where: { - id_project: id_project, - }, - }); + try { + const { id_project } = req.user; + console.log('Req data is:', req.user); + return await this.prisma.connections.findMany({ + where: { + id_project: id_project, + }, + }); + } catch (error) { + throwTypedError( + new ConnectionsError({ + name: 'GET_CONNECTIONS_ERROR', + message: 'ConnectionsController.getConnections() call failed', + cause: error, + }), + this.logger, + ); + } } } diff --git a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts index 223825385..4ca796d81 100644 --- a/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts +++ b/packages/api/src/@core/connections/crm/services/accelo/accelo.service.ts @@ -7,7 +7,13 @@ import { import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from '../registry.service'; @@ -144,7 +150,18 @@ export class AcceloConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'accelo', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', + message: `AcceloConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + 'accelo', + Action.oauthCallback, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -189,7 +206,18 @@ export class AcceloConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : accelo '); } catch (error) { - handleServiceError(error, this.logger, 'accelo', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', + message: `AcceloConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + 'accelo', + Action.oauthRefresh, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/attio/attio.service.ts b/packages/api/src/@core/connections/crm/services/attio/attio.service.ts index 65523aaae..ad10f5262 100644 --- a/packages/api/src/@core/connections/crm/services/attio/attio.service.ts +++ b/packages/api/src/@core/connections/crm/services/attio/attio.service.ts @@ -7,7 +7,7 @@ import { import { PrismaService } from '@@core/prisma/prisma.service'; import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ServiceRegistry } from '../registry.service'; @@ -131,7 +131,17 @@ export class AttioConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'attio', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `AttioConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "attio", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts b/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts index 383bea9b3..b17e72b71 100644 --- a/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts +++ b/packages/api/src/@core/connections/crm/services/capsule/capsule.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,7 +136,17 @@ export class CapsuleConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'capsule', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CapsuleConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "capsule", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class CapsuleConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : capsule '); } catch (error) { - handleServiceError(error, this.logger, 'capsule', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `CapsuleConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "capsule", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/close/close.service.ts b/packages/api/src/@core/connections/crm/services/close/close.service.ts index 9b8b139ee..bb1193cf4 100644 --- a/packages/api/src/@core/connections/crm/services/close/close.service.ts +++ b/packages/api/src/@core/connections/crm/services/close/close.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,7 +137,17 @@ export class CloseConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'close', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CloseConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "close", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -182,7 +192,17 @@ export class CloseConnectionService implements ICrmConnectionService { } this.logger.log('OAuth credentials updated : close '); } catch (error) { - handleServiceError(error, this.logger, 'close', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `CloseConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "close", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/copper/copper.service.ts b/packages/api/src/@core/connections/crm/services/copper/copper.service.ts index 88b6d39d0..745b25707 100644 --- a/packages/api/src/@core/connections/crm/services/copper/copper.service.ts +++ b/packages/api/src/@core/connections/crm/services/copper/copper.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -126,7 +126,17 @@ export class CopperConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'copper', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `CopperConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "copper", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts index f39da7a04..2662ab102 100644 --- a/packages/api/src/@core/connections/crm/services/crm.connection.service.ts +++ b/packages/api/src/@core/connections/crm/services/crm.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -42,7 +42,9 @@ export class CrmConnectionsService { ) { try { if (!code) { - throw new NotFoundError(`no ${providerName} code found, found ${code}`); + throw new ReferenceError( + `no ${providerName} code found, found ${code}`, + ); } const serviceName = providerName.toLowerCase(); @@ -50,7 +52,7 @@ export class CrmConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -81,7 +83,14 @@ export class CrmConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', + message: 'CrmConnectionsService.handleCRMCallBack() call failed', + cause: error, + }), + this.logger, + ); } } @@ -96,7 +105,7 @@ export class CrmConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -106,7 +115,14 @@ export class CrmConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', + message: 'CrmConnectionsService.handleCRMTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts b/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts index 0d83c3705..2bdc0ba37 100644 --- a/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts +++ b/packages/api/src/@core/connections/crm/services/hubspot/hubspot.service.ts @@ -7,7 +7,7 @@ import { RefreshParams, } from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; @@ -135,7 +135,17 @@ export class HubspotConnectionService implements ICrmConnectionService { this.logger.log('Successfully added tokens inside DB ' + db_res); return db_res; } catch (error) { - handleServiceError(error, this.logger, 'hubspot', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `HubspotConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "hubspot", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -180,7 +190,17 @@ export class HubspotConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : hubspot '); } catch (error) { - handleServiceError(error, this.logger, 'hubspot', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `HubspotConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "hubspot", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/keap/keap.service.ts b/packages/api/src/@core/connections/crm/services/keap/keap.service.ts index e9ccd25ef..dc15c185c 100644 --- a/packages/api/src/@core/connections/crm/services/keap/keap.service.ts +++ b/packages/api/src/@core/connections/crm/services/keap/keap.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class KeapConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'keap', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `KeapConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "keap", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -177,7 +187,17 @@ export class KeapConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : keap '); } catch (error) { - handleServiceError(error, this.logger, 'keap', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `KeapConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "keap", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts b/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts index 9c90ffdd1..ffb67f281 100644 --- a/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts +++ b/packages/api/src/@core/connections/crm/services/pipedrive/pipedrive.service.ts @@ -6,7 +6,7 @@ import { ICrmConnectionService, RefreshParams, } from '../../types'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class PipedriveConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'pipedrive', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `PipedriveConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "pipedrive", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -179,7 +189,17 @@ export class PipedriveConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : pipedrive '); } catch (error) { - handleServiceError(error, this.logger, 'pipedrive', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `PipedriveConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "pipedrive", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/registry.service.ts b/packages/api/src/@core/connections/crm/services/registry.service.ts index cec95d9c7..bbea65b82 100644 --- a/packages/api/src/@core/connections/crm/services/registry.service.ts +++ b/packages/api/src/@core/connections/crm/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ICrmConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts b/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts index 34286d993..4b40d1893 100644 --- a/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts +++ b/packages/api/src/@core/connections/crm/services/teamleader/teamleader.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -136,12 +136,17 @@ export class TeamleaderConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'teamleader', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `TeamleaderConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "teamleader", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -183,7 +188,17 @@ export class TeamleaderConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : teamleader '); } catch (error) { - handleServiceError(error, this.logger, 'teamleader', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `TeamleaderConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "teamleader", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts b/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts index 551f0bb50..7b8662a68 100644 --- a/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts +++ b/packages/api/src/@core/connections/crm/services/teamwork/teamwork.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -127,7 +127,17 @@ export class TeamworkConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'teamwork', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `TeamworkConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "teamwork", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts b/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts index e5c1729fa..e5b16709e 100644 --- a/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts +++ b/packages/api/src/@core/connections/crm/services/zendesk/zendesk.service.ts @@ -6,7 +6,7 @@ import { ICrmConnectionService, RefreshParams, } from '../../types'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -140,7 +140,17 @@ export class ZendeskConnectionService implements ICrmConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_CRM", + message: `ZendeskConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } async handleTokenRefresh(opts: RefreshParams) { @@ -182,7 +192,17 @@ export class ZendeskConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : zendesk '); } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_CRM", + message: `ZendeskConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts index 231663d89..266b3c1c7 100644 --- a/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts +++ b/packages/api/src/@core/connections/crm/services/zoho/zoho.service.ts @@ -7,7 +7,13 @@ import { RefreshParams, } from '../../types'; import { LoggerService } from '@@core/logger/logger.service'; -import { Action, NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; @@ -80,7 +86,7 @@ export class ZohoConnectionService implements ICrmConnectionService { try { const { linkedUserId, projectId, code, location } = opts; if (!location) { - throw new NotFoundError(`no zoho location, found ${location}`); + throw new ReferenceError(`no zoho location, found ${location}`); } const isNotUnique = await this.prisma.connections.findFirst({ where: { @@ -176,7 +182,18 @@ export class ZohoConnectionService implements ICrmConnectionService { return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zoho', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_CRM', + message: `ZohoConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + 'zoho', + Action.oauthCallback, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } async handleTokenRefresh(opts: RefreshParams) { @@ -218,7 +235,18 @@ export class ZohoConnectionService implements ICrmConnectionService { }); this.logger.log('OAuth credentials updated : zoho '); } catch (error) { - handleServiceError(error, this.logger, 'zoho', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_CRM', + message: `ZohoConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + 'zoho', + Action.oauthRefresh, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts b/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts index 6a94cd504..fa476b2b1 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/getresponse/getresponse.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -143,12 +143,17 @@ export class GetresponseConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'getresponse', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `GetresponseConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "getresponse", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -190,12 +195,17 @@ export class GetresponseConnectionService }); this.logger.log('OAuth credentials updated : getresponse '); } catch (error) { - handleServiceError( - error, - this.logger, - 'getresponse', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", + message: `GetresponseConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "getresponse", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts b/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts index 5b4657df3..3acbfee8d 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/mailchimp/mailchimp.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -149,7 +149,17 @@ export class MailchimpConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'mailchimp', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `MailchimpConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "mailchimp", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts index 03b9cf934..6799f5d8f 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/marketingautomation.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -43,7 +43,7 @@ export class MarketingAutomationConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -73,7 +73,15 @@ export class MarketingAutomationConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION', + message: + 'MarketingAutomationConnectionsService.handleMarketingAutomationCallBack() call failed', + cause: error, + }), + this.logger, + ); } } @@ -88,7 +96,7 @@ export class MarketingAutomationConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -98,7 +106,15 @@ export class MarketingAutomationConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION', + message: + 'MarketingAutomationConnectionsService.handleMarketingAutomationTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts b/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts index b835f0059..cbf43c92d 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/podium/podium.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -137,7 +137,17 @@ export class PodiumConnectionService } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'podium', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION", + message: `PodiumConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "podium", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -178,7 +188,17 @@ export class PodiumConnectionService }); this.logger.log('OAuth credentials updated : podium '); } catch (error) { - handleServiceError(error, this.logger, 'podium', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION", + message: `PodiumConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "podium", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts b/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts index b907b5f03..36412009e 100644 --- a/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts +++ b/packages/api/src/@core/connections/marketingautomation/services/registry.service.ts @@ -19,7 +19,9 @@ export class ServiceRegistry { getService(integrationId: string): IMarketingAutomationConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts b/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts index c57090984..8cd846197 100644 --- a/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/aha/aha.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -127,7 +127,17 @@ export class AhaConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'aha', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `AhaConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "aha", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts b/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts index c08aa68ff..ef2cbcdd6 100644 --- a/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/clickup/clickup.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -121,7 +121,17 @@ export class ClickupConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'clickup', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `ClickupConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "clickup", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/front/front.service.ts b/packages/api/src/@core/connections/ticketing/services/front/front.service.ts index ecf33b70f..a4d5c0a94 100644 --- a/packages/api/src/@core/connections/ticketing/services/front/front.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/front/front.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -131,7 +131,17 @@ export class FrontConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'front', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `FrontConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "front", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -173,7 +183,17 @@ export class FrontConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : front '); } catch (error) { - handleServiceError(error, this.logger, 'front', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `FrontConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "front", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/github/github.service.ts b/packages/api/src/@core/connections/ticketing/services/github/github.service.ts index 1d8a6c3fc..388f559b1 100644 --- a/packages/api/src/@core/connections/ticketing/services/github/github.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/github/github.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -132,7 +132,17 @@ export class GithubConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'github', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GithubConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "github", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -175,7 +185,17 @@ export class GithubConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : github '); } catch (error) { - handleServiceError(error, this.logger, 'github', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GithubConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "github", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts b/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts index 2a30ebdbf..1c53d4302 100644 --- a/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/gitlab/gitlab.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -134,7 +134,17 @@ export class GitlabConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'gitlab', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GitlabConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "gitlab", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -177,7 +187,17 @@ export class GitlabConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : gitlab '); } catch (error) { - handleServiceError(error, this.logger, 'gitlab', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GitlabConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "gitlab", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts b/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts index 48c9a47e4..8c55de22b 100644 --- a/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/gorgias/gorgias.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -142,7 +142,17 @@ export class GorgiasConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'gorgias', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `GorgiasConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "gorgias", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -185,7 +195,17 @@ export class GorgiasConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : gorgias '); } catch (error) { - handleServiceError(error, this.logger, 'gorgias', Action.oauthRefresh); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `GorgiasConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "gorgias", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts index 455a97526..efd7d4449 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira/jira.service.ts @@ -1,7 +1,13 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { + Action, + ActionType, + ConnectionsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -91,35 +97,32 @@ export class JiraConnectionService implements ITicketingConnectionService { // get the cloud id from atlassian jira, it is used across requests to the api //TODO: add a field inside our connections db to handle it - const res_ = await axios.post( + const res_ = await axios.get( `https://api.atlassian.com/oauth/token/accessible-resources`, - formData.toString(), { headers: { Authorization: `Bearer ${data.access_token}`, - 'Content-Type': 'application/json', + Accept: 'application/json', }, }, ); const sites_scopes: JiraCloudIdInformation[] = res_.data; - let cloud_id: string; - for (const site of sites_scopes) { - if (site.url == 'https://panora.atlassian.net') { - cloud_id = site.id; - break; - } - } + + const cloud_id: string = sites_scopes[0].id; //todo let db_res; const connection_token = uuidv4(); + const access_token = this.cryptoService.encrypt(data.access_token); + const refresh_token = this.cryptoService.encrypt(data.refresh_token); + if (isNotUnique) { db_res = await this.prisma.connections.update({ where: { id_connection: isNotUnique.id_connection, }, data: { - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), + access_token: access_token, + refresh_token: refresh_token, account_url: `https://api.atlassian.com/ex/jira/${cloud_id}`, expiration_timestamp: new Date( new Date().getTime() + Number(data.expires_in) * 1000, @@ -137,8 +140,8 @@ export class JiraConnectionService implements ITicketingConnectionService { vertical: 'ticketing', token_type: 'oauth', account_url: `https://api.atlassian.com/ex/jira/${cloud_id}`, - access_token: this.cryptoService.encrypt(data.access_token), - refresh_token: this.cryptoService.encrypt(data.refresh_token), + access_token: access_token, + refresh_token: refresh_token, expiration_timestamp: new Date( new Date().getTime() + Number(data.expires_in) * 1000, ), @@ -160,30 +163,43 @@ export class JiraConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'jira', Action.oauthCallback); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_TICKETING', + message: `JiraConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + 'jira', + Action.oauthCallback, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } async handleTokenRefresh(opts: RefreshParams) { try { const { connectionId, refreshToken, projectId } = opts; - const formData = new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: this.cryptoService.decrypt(refreshToken), - }); + const CREDENTIALS = (await this.cService.getCredentials( projectId, this.type, )) as OAuth2AuthData; + + const formData = { + grant_type: 'refresh_token', + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + refresh_token: this.cryptoService.decrypt(refreshToken), + }; + const res = await axios.post( `https://auth.atlassian.com/oauth/token`, - formData.toString(), + formData, { headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Basic ${Buffer.from( - `${CREDENTIALS.CLIENT_ID}:${CREDENTIALS.CLIENT_SECRET}`, - ).toString('base64')}`, + 'Content-Type': 'application/json', }, }, ); @@ -202,7 +218,18 @@ export class JiraConnectionService implements ITicketingConnectionService { }); this.logger.log('OAuth credentials updated : jira '); } catch (error) { - handleServiceError(error, this.logger, 'jira', Action.oauthRefresh); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_TICKETING', + message: `JiraConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + 'jira', + Action.oauthRefresh, + ActionType.POST, + )}`, + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts b/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts index fef87be92..d85c3b263 100644 --- a/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/jira_service_mgmt/jira.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -172,12 +172,17 @@ export class JiraServiceMgmtConnectionService } return db_res; } catch (error) { - handleServiceError( - error, - this.logger, - 'jira_service_mgmt', - Action.oauthCallback, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `JiraServiceMgmtConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "jira_service_mgmt", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } @@ -219,12 +224,17 @@ export class JiraServiceMgmtConnectionService }); this.logger.log('OAuth credentials updated : jira_service_mgmt '); } catch (error) { - handleServiceError( - error, - this.logger, - 'jira_service_mgmt', - Action.oauthRefresh, - ); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_REFRESH_TICKETING", + message: `JiraServiceMgmtConnectionService.handleTokenRefresh() call failed ---> ${format3rdPartyError( + "linear", + Action.oauthRefresh, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts b/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts index 4674a04d2..84d5e6567 100644 --- a/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/linear/linear.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -130,7 +130,17 @@ export class LinearConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'linear', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `LinearConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "linear", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/connections/ticketing/services/registry.service.ts b/packages/api/src/@core/connections/ticketing/services/registry.service.ts index 0de0f679d..a1192883a 100644 --- a/packages/api/src/@core/connections/ticketing/services/registry.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ITicketingConnectionService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts index 519b41849..8f1389e95 100644 --- a/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/ticketing.connection.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { ConnectionsError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { connections as Connection } from '@prisma/client'; @@ -44,7 +44,7 @@ export class TicketingConnectionsService { const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const callbackOpts: CallbackParams = { linkedUserId: linkedUserId, @@ -75,7 +75,15 @@ export class TicketingConnectionsService { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_CALLBACK_TICKETING', + message: + 'TicketingConnectionsService.handleTicketingCallBack() call failed', + cause: error, + }), + this.logger, + ); } } @@ -90,7 +98,7 @@ export class TicketingConnectionsService { const serviceName = providerName.toLowerCase(); const service = this.serviceRegistry.getService(serviceName); if (!service) { - throw new NotFoundError(`Unknown provider, found ${providerName}`); + throw new ReferenceError(`Unknown provider, found ${providerName}`); } const refreshOpts: RefreshParams = { connectionId: connectionId, @@ -100,7 +108,15 @@ export class TicketingConnectionsService { }; const data = await service.handleTokenRefresh(refreshOpts); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectionsError({ + name: 'HANDLE_OAUTH_REFRESH_TICKETING', + message: + 'TicketingConnectionsService.handleTicketingTokensRefresh() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts b/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts index 772abf7a0..1c4affc13 100644 --- a/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts +++ b/packages/api/src/@core/connections/ticketing/services/zendesk/zendesk.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { Action, handleServiceError } from '@@core/utils/errors'; +import { Action, ActionType, ConnectionsError, format3rdPartyError, throwTypedError } from '@@core/utils/errors'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { EnvironmentService } from '@@core/environment/environment.service'; @@ -172,7 +172,17 @@ export class ZendeskConnectionService implements ITicketingConnectionService { } return db_res; } catch (error) { - handleServiceError(error, this.logger, 'zendesk', Action.oauthCallback); + throwTypedError(new ConnectionsError( + { + name: "HANDLE_OAUTH_CALLBACK_TICKETING", + message: `ZendeskConnectionService.handleCallback() call failed ---> ${format3rdPartyError( + "zendesk", + Action.oauthCallback, + ActionType.POST + )}`, + cause: error + } + ), this.logger) } } diff --git a/packages/api/src/@core/core.module.ts b/packages/api/src/@core/core.module.ts index a06fc0f6d..918672f19 100644 --- a/packages/api/src/@core/core.module.ts +++ b/packages/api/src/@core/core.module.ts @@ -14,6 +14,7 @@ import { EncryptionService } from './encryption/encryption.service'; import { ConnectionsStrategiesModule } from './connections-strategies/connections-strategies.module'; import { SyncModule } from './sync/sync.module'; import { ProjectConnectorsModule } from './project-connectors/project-connectors.module'; +import { LoggerService } from './logger/logger.service'; @Module({ imports: [ @@ -48,6 +49,6 @@ import { ProjectConnectorsModule } from './project-connectors/project-connectors SyncModule, ProjectConnectorsModule, ], - providers: [EncryptionService], + providers: [EncryptionService, LoggerService], }) export class CoreModule {} diff --git a/packages/api/src/@core/encryption/encryption.service.ts b/packages/api/src/@core/encryption/encryption.service.ts index 1707b7a2a..efbc5c4d6 100644 --- a/packages/api/src/@core/encryption/encryption.service.ts +++ b/packages/api/src/@core/encryption/encryption.service.ts @@ -1,4 +1,6 @@ import { EnvironmentService } from '@@core/environment/environment.service'; +import { LoggerService } from '@@core/logger/logger.service'; +import { EncryptionError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; import * as crypto from 'crypto'; @@ -7,7 +9,7 @@ export class EncryptionService { private secretKey: string; private iv = crypto.randomBytes(16); - constructor(private env: EnvironmentService) { + constructor(private env: EnvironmentService, private logger: LoggerService) { this.secretKey = this.env.getCryptoKey(); } @@ -23,7 +25,13 @@ export class EncryptionService { const encryptedWithIv = this.iv.toString('hex') + ':' + encrypted; return encryptedWithIv; } catch (error) { - throw new Error('Encrypting error... ' + error); + throwTypedError(new EncryptionError( + { + name: "ENCRYPT_ERROR", + message: "EncryptionService.encrypt() call failed", + cause: error + } + ), this.logger) } } @@ -41,7 +49,13 @@ export class EncryptionService { decrypted += decipher.final('utf8'); return decrypted; } catch (error) { - throw new Error('Decrypting error... ' + error); + throwTypedError(new EncryptionError( + { + name: "DECRYPT_ERROR", + message: "EncryptionService.decrypt() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/events/events.service.ts b/packages/api/src/@core/events/events.service.ts index bab3e5126..8aab5304c 100644 --- a/packages/api/src/@core/events/events.service.ts +++ b/packages/api/src/@core/events/events.service.ts @@ -1,7 +1,7 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; import { PaginationDto } from '@@core/utils/dtos/pagination.dto'; -import { handleServiceError } from '@@core/utils/errors'; +import { EventsError, throwTypedError } from '@@core/utils/errors'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -36,7 +36,14 @@ export class EventsService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new EventsError({ + name: 'GET_EVENTS_ERROR', + message: 'EventsService.findEvents() call failed', + cause: error, + }), + this.logger, + ); } } @@ -44,7 +51,14 @@ export class EventsService { try { return await this.prisma.events.count(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new EventsError({ + name: 'GET_EVENTS_COUNT_ERROR', + message: 'EventsService.getEventsCount() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/field-mapping/field-mapping.service.ts b/packages/api/src/@core/field-mapping/field-mapping.service.ts index 91137fd84..6fd35e53e 100644 --- a/packages/api/src/@core/field-mapping/field-mapping.service.ts +++ b/packages/api/src/@core/field-mapping/field-mapping.service.ts @@ -8,7 +8,12 @@ import { } from './dto/create-custom-field.dto'; import { v4 as uuidv4 } from 'uuid'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { + ActionType, + CustomFieldsError, + format3rdPartyError, + throwTypedError, +} from '@@core/utils/errors'; import { CrmObject } from '@crm/@lib/@types'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { CONNECTORS_METADATA } from '@panora/shared'; @@ -31,7 +36,14 @@ export class FieldMappingService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'GET_ATTRIBUTES_ERROR', + message: 'FieldMappingService.getAttributes() call failed', + cause: error, + }), + this.logger, + ); } } @@ -39,16 +51,29 @@ export class FieldMappingService { try { return await this.prisma.value.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'GET_VALUES_ERROR', + message: 'FieldMappingService.getValues() call failed', + cause: error, + }), + this.logger, + ); } - return await this.prisma.value.findMany(); } async getEntities() { try { return await this.prisma.entity.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'GET_ENTITIES_ERROR', + message: 'FieldMappingService.getEntities() call failed', + cause: error, + }), + this.logger, + ); } } @@ -70,7 +95,14 @@ export class FieldMappingService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'GET_CUSTOM_FIELDS_ERROR', + message: 'FieldMappingService.getCustomFieldMappings() call failed', + cause: error, + }), + this.logger, + ); } } @@ -98,7 +130,14 @@ export class FieldMappingService { return attribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_DEFINE_FIELD_ERROR', + message: 'FieldMappingService.defineTargetField() call failed', + cause: error, + }), + this.logger, + ); } } @@ -118,7 +157,14 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_MAP_FIELD_ERROR', + message: 'FieldMappingService.mapFieldToProvider() call failed', + cause: error, + }), + this.logger, + ); } } @@ -154,7 +200,14 @@ export class FieldMappingService { return updatedAttribute; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CustomFieldsError({ + name: 'CREATE_CUSTOM_FIELD_ERROR', + message: 'FieldMappingService.createCustomField() call failed', + cause: error, + }), + this.logger, + ); } } @@ -197,12 +250,17 @@ export class FieldMappingService { statusCode: 200, }; } catch (error) { - handleServiceError( - error, + throwTypedError( + new CustomFieldsError({ + name: 'GET_3RD_PARTY_REMOTE_PROPERTIES', + message: `FieldMappingService.getCustomProperties() call failed ---> ${format3rdPartyError( + providerId, + CrmObject.contact, + ActionType.GET, + )}`, + cause: error, + }), this.logger, - providerId, - CrmObject.contact, - ActionType.GET, ); } } diff --git a/packages/api/src/@core/linked-users/linked-users.service.ts b/packages/api/src/@core/linked-users/linked-users.service.ts index 86db512da..ebbbab642 100644 --- a/packages/api/src/@core/linked-users/linked-users.service.ts +++ b/packages/api/src/@core/linked-users/linked-users.service.ts @@ -6,7 +6,7 @@ import { import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { LinkedUserError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class LinkedUsersService { @@ -21,7 +21,14 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USERS_ERROR', + message: 'LinkedUsersService.getLinkedUsers() call failed', + cause: error, + }), + this.logger, + ); } } async getLinkedUser(id: string) { @@ -32,7 +39,14 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USER_ERROR', + message: 'LinkedUsersService.getLinkedUser() call failed', + cause: error, + }), + this.logger, + ); } } async getLinkedUserV2(originId: string) { @@ -43,7 +57,14 @@ export class LinkedUsersService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new LinkedUserError({ + name: 'GET_LINKED_USER_FROM_REMOTE_ID_ERROR', + message: 'LinkedUsersService.getLinkedUserV2() call failed', + cause: error, + }), + this.logger, + ); } } async addLinkedUser(data: CreateLinkedUserDto) { @@ -58,7 +79,14 @@ export class LinkedUsersService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new LinkedUserError({ + name: 'CREATE_LINKED_USER_ERROR', + message: 'LinkedUsersService.addlinkedUser() call failed', + cause: error, + }), + this.logger, + ); } } async addBatchLinkedUsers(data: CreateBatchLinkedUserDto) { @@ -79,7 +107,14 @@ export class LinkedUsersService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new LinkedUserError({ + name: 'CREATE_BATCH_LINKED_USER_ERROR', + message: 'LinkedUsersService.addBatchLinkedUsers() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/magic-link/magic-link.service.ts b/packages/api/src/@core/magic-link/magic-link.service.ts index f6742efbb..89a0fa47f 100644 --- a/packages/api/src/@core/magic-link/magic-link.service.ts +++ b/packages/api/src/@core/magic-link/magic-link.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { CreateMagicLinkDto } from './dto/create-magic-link.dto'; -import { handleServiceError } from '@@core/utils/errors'; +import { MagicLinksError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class MagicLinkService { @@ -15,7 +15,13 @@ export class MagicLinkService { try { return await this.prisma.invite_links.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "GET_MAGIC_LINKS_ERROR", + message: "MagicLinkService.getMagicLinks() call failed", + cause: error + } + ), this.logger) } } @@ -26,17 +32,25 @@ export class MagicLinkService { id_invite_link: id, }, }); + if(!inviteLink) throw new ReferenceError("Magic link undefined") const linkedUser = await this.prisma.linked_users.findFirst({ where: { id_linked_user: inviteLink.id_linked_user, }, }); + if(!linkedUser) throw new ReferenceError("Linked User undefined") return { ...inviteLink, id_project: linkedUser.id_project, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "GET_MAGIC_LINK_ERROR", + message: "MagicLinkService.getMagicLink() call failed", + cause: error + } + ), this.logger) } } @@ -71,7 +85,13 @@ export class MagicLinkService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new MagicLinksError( + { + name: "CREATE_MAGIC_LINK_ERROR", + message: "MagicLinkService.createUniqueLink() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts index f0366bb6d..e163877dc 100644 --- a/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts +++ b/packages/api/src/@core/managed-webhooks/managed-webhooks.service.ts @@ -1,15 +1,12 @@ import { Injectable } from '@nestjs/common'; -import { Queue } from 'bull'; -import { InjectQueue } from '@nestjs/bull'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { ManagedWebhooksError, throwTypedError } from '@@core/utils/errors'; import { ManagedWebhooksDto, RemoteThirdPartyCreationDto, } from './dto/managed-webhooks.dto'; -import crypto from 'crypto'; import { ConnectorCategory } from '@panora/shared'; import { TicketingWebhookHandlerService } from '@ticketing/@webhook/handler.service'; @@ -31,7 +28,14 @@ export class ManagedWebhooksService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ManagedWebhooksError({ + name: 'GET_MANAGED_WEBHOOKS_ERROR', + message: 'ManagedWebhooksService.getManagedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -42,7 +46,15 @@ export class ManagedWebhooksService { data: { active: active }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ManagedWebhooksError({ + name: 'UPDATE_MANAGED_WEBHOOK_STATUS_ERROR', + message: + 'ManagedWebhooksService.updateStatusManagedWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } @@ -62,7 +74,14 @@ export class ManagedWebhooksService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ManagedWebhooksError({ + name: 'CREATE_MANAGED_WEBHOOK_ERROR', + message: 'ManagedWebhooksService.createManagedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -73,6 +92,7 @@ export class ManagedWebhooksService { id_connection: data.id_connection, }, }); + if (!conn) throw new ReferenceError('Connection undefined'); switch (conn.vertical) { case ConnectorCategory.Ticketing: return await this.ticketingHandler.createExternalWebhook( @@ -82,7 +102,15 @@ export class ManagedWebhooksService { ); } } catch (error) { - throw new Error(error); + throwTypedError( + new ManagedWebhooksError({ + name: 'CREATE_REMOTE_WEBHOOK_ERROR', + message: + 'ManagedWebhooksService.createRemoteThirdPartyWebhook() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/organisations/organisations.service.ts b/packages/api/src/@core/organisations/organisations.service.ts index fc47290de..c1f84aef3 100644 --- a/packages/api/src/@core/organisations/organisations.service.ts +++ b/packages/api/src/@core/organisations/organisations.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { CreateOrganizationDto } from './dto/create-organization.dto'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; @Injectable() export class OrganisationsService { @@ -15,7 +14,6 @@ export class OrganisationsService { try { return await this.prisma.organizations.findMany(); } catch (error) { - handleServiceError(error, this.logger); } } async createOrganization(data: CreateOrganizationDto) { @@ -28,7 +26,6 @@ export class OrganisationsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); } }*/ } diff --git a/packages/api/src/@core/passthrough/passthrough.service.ts b/packages/api/src/@core/passthrough/passthrough.service.ts index 403ae710f..1f33fc793 100644 --- a/packages/api/src/@core/passthrough/passthrough.service.ts +++ b/packages/api/src/@core/passthrough/passthrough.service.ts @@ -5,9 +5,9 @@ import axios, { AxiosResponse } from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { CONNECTORS_METADATA } from '@panora/shared'; +import { PassthroughRequestError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class PassthroughService { @@ -93,7 +93,14 @@ export class PassthroughService { data: response.data, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new PassthroughRequestError({ + name: 'PASSTHROUGH_REMOTE_API_CALL_ERROR', + message: 'WebhookService.sendPassthroughRequest() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/project-connectors/project-connectors.controller.ts b/packages/api/src/@core/project-connectors/project-connectors.controller.ts index 748c92610..75c250613 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.controller.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.controller.ts @@ -18,20 +18,6 @@ import { import { ProjectConnectorsService } from './project-connectors.service'; import { ProjectConnectorsDto } from './dto/project-connectors.dto'; import { JwtAuthGuard } from '@@core/auth/guards/jwt-auth.guard'; -export interface TypeCustom { - id_project: string; - crm_hubspot: boolean; - crm_zoho: boolean; - crm_zendesk: boolean; - crm_pipedrive: boolean; - crm_attio: boolean; - tcg_zendesk: boolean; - tcg_gorgias: boolean; - tcg_front: boolean; - tcg_jira: boolean; - tcg_gitlab: boolean; - crm_close: boolean; -} @ApiTags('project-connectors') @Controller('project-connectors') export class ProjectConnectorsController { @@ -63,10 +49,10 @@ export class ProjectConnectorsController { ); } - @Post('create') + /*@Post('create') async createConnectorsToProject(@Body() data: TypeCustom) { return await this.projectConnectorsService.createProjectConnectors(data); - } + }*/ @ApiOperation({ operationId: 'getConnectorsFromProject', diff --git a/packages/api/src/@core/project-connectors/project-connectors.service.ts b/packages/api/src/@core/project-connectors/project-connectors.service.ts index 394c03fca..fcc44c191 100644 --- a/packages/api/src/@core/project-connectors/project-connectors.service.ts +++ b/packages/api/src/@core/project-connectors/project-connectors.service.ts @@ -1,9 +1,7 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; -import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; -import { TypeCustom } from './project-connectors.controller'; +import { ConnectorSetError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class ProjectConnectorsService { @@ -25,6 +23,9 @@ export class ProjectConnectorsService { }, }); + if (!project) { + throw new ReferenceError('Project undefined!'); + } const existingPConnectors = await this.prisma.connector_sets.findFirst({ where: { id_connector_set: project.id_connector_set, @@ -32,8 +33,8 @@ export class ProjectConnectorsService { }); if (!existingPConnectors) { - throw new Error( - `No project connector entry found for project ${id_project}`, + throw new ReferenceError( + `No connector set entry found for project ${id_project}`, ); } @@ -49,11 +50,19 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectorSetError({ + name: 'UPDATE_CONNECTOR_SET_ERROR', + message: + 'ProjectConnectorsService.updateProjectConnectors() call failed', + cause: error, + }), + this.logger, + ); } } - async createProjectConnectors(data: TypeCustom) { + /*async createProjectConnectors(data: TypeCustom) { try { const updateData: any = { id_connector_set: uuidv4(), @@ -75,9 +84,17 @@ export class ProjectConnectorsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectorSetError({ + name: 'CREATE_CONNECTOR_SET_ERROR', + message: + 'ProjectConnectorsService.createProjectConnectors() call failed', + cause: error, + }), + this.logger, + ); } - } + }*/ async getConnectorsByProjectId(id_project: string) { try { @@ -88,7 +105,7 @@ export class ProjectConnectorsService { }); if (!project) { - throw new NotFoundException('Project does not exist!'); + throw new ReferenceError('Project undefined!'); } const res = await this.prisma.connector_sets.findFirst({ @@ -97,13 +114,19 @@ export class ProjectConnectorsService { }, }); if (!res) { - throw new NotFoundException( - 'Connectors not found for current project!', - ); + throw new ReferenceError('Connector set undefined!'); } return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new ConnectorSetError({ + name: 'GET_CONNECTOR_SET_BY_PROJECT_ERROR', + message: + 'ProjectConnectorsService.getConnectorsbyProjectId() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/@core/projects/projects.service.ts b/packages/api/src/@core/projects/projects.service.ts index 5d0f54362..935623150 100644 --- a/packages/api/src/@core/projects/projects.service.ts +++ b/packages/api/src/@core/projects/projects.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '../prisma/prisma.service'; import { LoggerService } from '../logger/logger.service'; import { CreateProjectDto } from './dto/create-project.dto'; import { v4 as uuidv4 } from 'uuid'; -import { handleServiceError } from '@@core/utils/errors'; +import { ProjectError, throwTypedError } from '@@core/utils/errors'; import { ConnectorCategory, providersArray, @@ -20,7 +20,13 @@ export class ProjectsService { try { return await this.prisma.projects.findMany(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "GET_PROJECTS_ERROR", + message: "ProjectsService.getProjects() call failed", + cause: error + } + ), this.logger) } } @@ -32,12 +38,24 @@ export class ProjectsService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "GET_PROJECT_FOR_USER_ERROR", + message: "ProjectsService.getProjectsByUser() call failed", + cause: error + } + ), this.logger) } } async createProject(data: CreateProjectDto) { try { + const user = await this.prisma.users.findUnique({ + where: { + id_user: data.id_user + } + }) + if(!user) throw ReferenceError("User undefined"); const ACTIVE_CONNECTORS = providersArray(); // update project-connectors table for the project const updateData: any = { @@ -69,7 +87,13 @@ export class ProjectsService { }); return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError(new ProjectError( + { + name: "CREATE_PROJECT_ERROR", + message: "ProjectsService.createProject() call failed", + cause: error + } + ), this.logger) } } } diff --git a/packages/api/src/@core/sync/sync.service.ts b/packages/api/src/@core/sync/sync.service.ts index f4d041699..6c5b893d5 100644 --- a/packages/api/src/@core/sync/sync.service.ts +++ b/packages/api/src/@core/sync/sync.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@nestjs/common'; import { LoggerService } from '../logger/logger.service'; import { ConnectorCategory } from '@panora/shared'; -import { CrmObject, ENGAGEMENTS_TYPE } from '@crm/@lib/@types'; +import { ENGAGEMENTS_TYPE } from '@crm/@lib/@types'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { handleServiceError } from '@@core/utils/errors'; import { SyncService as CrmCompanySyncService } from '@crm/company/sync/sync.service'; import { SyncService as CrmContactSyncService } from '@crm/contact/sync/sync.service'; import { SyncService as CrmDealSyncService } from '@crm/deal/sync/sync.service'; @@ -20,6 +19,7 @@ import { SyncService as TicketingTagSyncService } from '@ticketing/tag/sync/sync import { SyncService as TicketingTeamSyncService } from '@ticketing/team/sync/sync.service'; import { SyncService as TicketingTicketSyncService } from '@ticketing/ticket/sync/sync.service'; import { SyncService as TicketingUserSyncService } from '@ticketing/user/sync/sync.service'; +import { throwTypedError, CoreSyncError } from '@@core/utils/errors'; @Injectable() export class CoreSyncService { @@ -51,32 +51,87 @@ export class CoreSyncService { vertical: string, provider: string, linkedUserId: string, - id_project: string + id_project: string, ) { try { - const tasks = []; switch (vertical) { case ConnectorCategory.Crm: - // logic - tasks.push(() => this.CrmUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmCompanySyncService.syncCompaniesForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmDealSyncService.syncDealsForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.CrmUserSyncService.syncUsersForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmCompanySyncService.syncCompaniesForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmContactSyncService.syncContactsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmDealSyncService.syncDealsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const type of ENGAGEMENTS_TYPE) { - tasks.push(() => this.CrmEngagementSyncService.syncEngagementsForLinkedUser(provider, linkedUserId, id_project, type)); + tasks.push(() => + this.CrmEngagementSyncService.syncEngagementsForLinkedUser( + provider, + linkedUserId, + id_project, + type, + ), + ); } - tasks.push(() => this.CrmNoteSyncService.syncNotesForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.CrmTaskSyncService.syncTasksForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.CrmNoteSyncService.syncNotesForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.CrmTaskSyncService.syncTasksForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const task of tasks) { try { await task(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'CRM_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -97,19 +152,68 @@ export class CoreSyncService { break; case ConnectorCategory.Ticketing: - // logic - tasks.push(() => this.TicketingUserSyncService.syncUsersForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingAccountSyncService.syncAccountsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingCollectionSyncService.syncCollectionsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingTicketSyncService.syncTicketsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingTeamSyncService.syncTeamsForLinkedUser(provider, linkedUserId, id_project)); - tasks.push(() => this.TicketingContactSyncService.syncContactsForLinkedUser(provider, linkedUserId, id_project)); + tasks.push(() => + this.TicketingUserSyncService.syncUsersForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingAccountSyncService.syncAccountsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingCollectionSyncService.syncCollectionsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingTicketSyncService.syncTicketsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingTeamSyncService.syncTeamsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); + tasks.push(() => + this.TicketingContactSyncService.syncContactsForLinkedUser( + provider, + linkedUserId, + id_project, + ), + ); for (const task of tasks) { try { await task(); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'TICKETING_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } @@ -122,27 +226,70 @@ export class CoreSyncService { for (const ticket of tickets) { try { - await this.TicketingCommentSyncService.syncCommentsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket); - await this.TicketingTagSyncService.syncTagsForLinkedUser(provider, linkedUserId, id_project, ticket.id_tcg_ticket); + await this.TicketingCommentSyncService.syncCommentsForLinkedUser( + provider, + linkedUserId, + id_project, + ticket.id_tcg_ticket, + ); + await this.TicketingTagSyncService.syncTagsForLinkedUser( + provider, + linkedUserId, + id_project, + ticket.id_tcg_ticket, + ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'TICKETING_INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } break; } - } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'INITIAL_SYNC_ERROR', + message: `CoreSyncService.initialSync() call failed with args ---> ${JSON.stringify( + { + vertical, + provider, + linkedUserId, + id_project, + }, + )}`, + cause: error, + }), + this.logger, + ); } } // we must have a sync_jobs table with 7 (verticals) rows, one of each is syncing details async getSyncStatus(vertical: string) { try { - } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'GET_SYNC_STATUS_ERROR', + message: 'CoreSyncService.getSyncStatus() call failed', + cause: error, + }), + this.logger, + ); } } @@ -182,7 +329,14 @@ export class CoreSyncService { status: `SYNCING`, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new CoreSyncError({ + name: 'RESYNC_ERROR', + message: 'CoreSyncService.resync() call failed', + cause: error, + }), + this.logger, + ); } finally { // Handle background tasks completion Promise.allSettled(tasks).then((results) => { diff --git a/packages/api/src/@core/utils/dtos/fetch-objects-query.dto.ts b/packages/api/src/@core/utils/dtos/fetch-objects-query.dto.ts index 7da1509fc..e5fb612d8 100644 --- a/packages/api/src/@core/utils/dtos/fetch-objects-query.dto.ts +++ b/packages/api/src/@core/utils/dtos/fetch-objects-query.dto.ts @@ -1,44 +1,40 @@ import { ApiProperty, ApiQuery } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsNumber, IsOptional, IsUUID } from 'class-validator' - +import { IsBoolean, IsNumber, IsOptional, IsUUID } from 'class-validator'; // To provide a default pageSize const DEFAULT_PAGE_SIZE = 50; export class FetchObjectsQueryDto { - - @ApiProperty({ - name: 'remote_data', - description: 'Set to true to include data from the original software.', - required: false - }) - @IsOptional() - @Transform(({ value }) => value === 'true' ? true : value === 'false' ? false : value) - @IsBoolean() - remote_data: boolean; - - @ApiProperty({ - name: 'pageSize', - required: false, - description: 'Set to get the number of records.' - }) - @IsOptional() - @IsNumber() - @Transform(p => Number(p.value)) - pageSize: number = DEFAULT_PAGE_SIZE; - - @ApiProperty({ - name: 'cursor', - required: false, - description: 'Set to get the number of records after this cursor.' - }) - @IsOptional() - @Transform(p => Buffer.from(p.value, 'base64').toString()) - @IsUUID() - cursor: string; - + @ApiProperty({ + name: 'remote_data', + description: 'Set to true to include data from the original software.', + required: false, + }) + @IsOptional() + @Transform(({ value }) => + value === 'true' ? true : value === 'false' ? false : value, + ) + @IsBoolean() + remote_data: boolean; + + @ApiProperty({ + name: 'pageSize', + required: false, + description: 'Set to get the number of records.', + }) + @IsOptional() + @IsNumber() + @Transform((p) => Number(p.value)) + pageSize: number = DEFAULT_PAGE_SIZE; + + @ApiProperty({ + name: 'cursor', + required: false, + description: 'Set to get the number of records after this cursor.', + }) + @IsOptional() + @Transform((p) => Buffer.from(p.value, 'base64').toString()) + @IsUUID() + cursor: string; } - - - diff --git a/packages/api/src/@core/utils/errors.ts b/packages/api/src/@core/utils/errors.ts index 628d14ea9..1b3763070 100644 --- a/packages/api/src/@core/utils/errors.ts +++ b/packages/api/src/@core/utils/errors.ts @@ -5,11 +5,36 @@ import { Prisma } from '@prisma/client'; import { TargetObject } from './types'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -type ServiceError = AxiosError | PrismaClientKnownRequestError | Error; +type TypedError = + | AxiosError + | PrismaClientKnownRequestError + | T; + +export class ErrorBase extends Error { + name: T; + message: string; + cause: any; + + constructor({ + name, + message, + cause, + }: { + name: T; + message: string; + cause?: any; + }) { + super(); + this.name = name; + this.message = message; + this.cause = cause; + } +} export enum Action { oauthCallback = 'oauth-callback', oauthRefresh = 'oauth-refresh', + webhookCreation = 'webhook-creation', } export enum ActionType { @@ -19,88 +44,257 @@ export enum ActionType { DELETE = 'DELETE', } -// Custom error for general application errors -export class AppError extends Error { - constructor(message: string) { - super(message); - this.name = 'AppError'; - } -} +export class ProjectError extends ErrorBase< + 'CREATE_PROJECT_ERROR' | 'GET_PROJECT_FOR_USER_ERROR' | 'GET_PROJECTS_ERROR' +> {} -// Custom error for not found (404) errors -export class NotFoundError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.NOT_FOUND); - this.name = 'NotFoundError'; - } -} +export class ConnectorSetError extends ErrorBase< + | 'CREATE_CONNECTOR_SET_ERROR' + | 'GET_CONNECTOR_SET_BY_PROJECT_ERROR' + | 'UPDATE_CONNECTOR_SET_ERROR' +> {} +export class LinkedUserError extends ErrorBase< + | 'CREATE_LINKED_USER_ERROR' + | 'CREATE_BATCH_LINKED_USER_ERROR' + | 'GET_LINKED_USER_FROM_REMOTE_ID_ERROR' + | 'GET_LINKED_USERS_ERROR' + | 'GET_LINKED_USER_ERROR' +> {} + +export class ConnectionStrategiesError extends ErrorBase< + | 'CREATE_CONNECTION_STRATEGY_ERROR' + | 'DELETE_CONNECTION_STRATEGY_ERROR' + | 'UPDATE_CONNECTION_STRATEGY_ERROR' + | 'GET_CONNECTION_STRATEGIES_BY_PROJECT_ERROR' + | 'GET_CONNECTION_STRATEGY_STATUS_ERROR' + | 'GET_CREDENTIALS_ERROR' + | 'TOGGLE_CONNECTION_STRATEGY_ERROR' + | 'CONNECTION_STRATEGY_ALREADY_EXISTS' + | 'CUSTOM_CREDENTIALS_ERROR' +> {} + +export class EncryptionError extends ErrorBase< + 'ENCRYPT_ERROR' | 'DECRYPT_ERROR' +> {} + +export class EventsError extends ErrorBase< + 'GET_EVENTS_ERROR' | 'GET_EVENTS_COUNT_ERROR' +> {} + +export class MagicLinksError extends ErrorBase< + 'CREATE_MAGIC_LINK_ERROR' | 'GET_MAGIC_LINKS_ERROR' | 'GET_MAGIC_LINK_ERROR' +> {} + +export class CustomFieldsError extends ErrorBase< + | 'GET_3RD_PARTY_REMOTE_PROPERTIES' + | 'CREATE_DEFINE_FIELD_ERROR' + | 'CREATE_MAP_FIELD_ERROR' + | 'CREATE_CUSTOM_FIELD_ERROR' + | 'GET_CUSTOM_FIELDS_ERROR' + | 'GET_ENTITIES_ERROR' + | 'GET_ATTRIBUTES_ERROR' + | 'GET_VALUES_ERROR' +> {} + +export class ConnectionsError extends ErrorBase< + | 'GET_CONNECTION_FROM_CONNECTION_TOKEN_ERROR' + | 'GET_CONNECTIONS_ERROR' + | 'OAUTH_CALLBACK_STATE_NOT_FOUND_ERROR' + | 'OAUTH_CALLBACK_CODE_NOT_FOUND_ERROR' + | 'OAUTH_CALLBACK_ERROR' + | 'HANDLE_OAUTH_CALLBACK_TICKETING' + | 'HANDLE_OAUTH_CALLBACK_CRM' + | 'HANDLE_OAUTH_CALLBACK_MARKETINGAUTOMATION' + | 'HANDLE_OAUTH_CALLBACK_FILESTORAGE' + | 'HANDLE_OAUTH_CALLBACK_ACCOUNTING' + | 'HANDLE_OAUTH_CALLBACK_ATS' + | 'HANDLE_OAUTH_CALLBACK_HRIS' + | 'HANDLE_OAUTH_REFRESH_TICKETING' + | 'HANDLE_OAUTH_REFRESH_CRM' + | 'HANDLE_OAUTH_REFRESH_MARKETINGAUTOMATION' + | 'HANDLE_OAUTH_REFRESH_FILESTORAGE' + | 'HANDLE_OAUTH_REFRESH_ACCOUNTING' + | 'HANDLE_OAUTH_REFRESH_ATS' + | 'HANDLE_OAUTH_REFRESH_HRIS' +> {} + +export class ManagedWebhooksError extends ErrorBase< + | 'GET_MANAGED_WEBHOOKS_ERROR' + | 'CREATE_MANAGED_WEBHOOK_ERROR' + | 'UPDATE_MANAGED_WEBHOOK_STATUS_ERROR' + | 'CREATE_REMOTE_WEBHOOK_ERROR' + | 'SIGNATURE_VERIFICATION_AUTHENTICITY_ERROR' + | 'RECEIVING_WEBHOOK_PAYLOAD_ERROR' +> {} -// Custom error for bad request (400) errors -export class BadRequestError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.BAD_REQUEST); - this.name = 'BadRequestError'; +export class AuthError extends ErrorBase< + | 'GET_API_KEYS_ERROR' + | 'GET_USERS_ERROR' + | 'VERIFY_USER_ERROR' + | 'LOGIN_USER_ERROR' + | 'REGISTER_USER_ERROR' + | 'CREATE_USER_ERROR' + | 'DELETE_API_KEY_ERROR' + | 'REFRESH_ACCESS_TOKEN_ERROR' + | 'HASH_API_KEY_ERROR' + | 'GENERATE_API_KEY_ERROR' + | 'VALIDATE_API_KEY_ERROR' + | 'EMAIL_ALREADY_EXISTS_ERROR' +> {} + +export class PassthroughRequestError extends ErrorBase<'PASSTHROUGH_REMOTE_API_CALL_ERROR'> {} + +export class WebhooksError extends ErrorBase< + | 'CREATE_WEBHOOK_ERROR' + | 'DELETE_WEBHOOK_ERROR' + | 'UPDATE_WEBHOOK_STATUS_ERROR' + | 'VERIFY_PAYLOAD_ERROR' + | 'GET_WEBHOOKS_ERROR' + | 'INVALID_SIGNATURE_ERROR' + | 'SIGNATURE_GENERATION_ERROR' + | 'DELIVERING_WEBHOOK_ERROR' + | 'DELIVERING_FAILED_WEBHOOK_ERROR' + | 'DELIVERING_PRIORITY_WEBHOOK_ERROR' +> {} + +export class SyncError extends ErrorBase< + | 'TICKETING_USER_SYNC_ERROR' + | 'TICKETING_TICKET_SYNC_ERROR' + | 'TICKETING_ACCOUNT_SYNC_ERROR' + | 'TICKETING_CONTACT_SYNC_ERROR' + | 'TICKETING_TEAM_SYNC_ERROR' + | 'TICKETING_TAG_SYNC_ERROR' + | 'TICKETING_COMMENT_SYNC_ERROR' + | 'TICKETING_ATTACHMENT_SYNC_ERROR' + | 'TICKETING_COLLECTION_SYNC_ERROR' + | 'CRM_COMPANY_SYNC_ERROR' + | 'CRM_CONTACT_SYNC_ERROR' + | 'CRM_DEAL_SYNC_ERROR' + | 'CRM_ENGAGEMENT_SYNC_ERROR' + | 'CRM_NOTE_SYNC_ERROR' + | 'CRM_STAGE_SYNC_ERROR' + | 'CRM_TASK_SYNC_ERROR' + | 'CRM_USER_SYNC_ERROR' +> {} + +export class CoreSyncError extends ErrorBase< + | 'INITIAL_SYNC_ERROR' + | 'GET_SYNC_STATUS_ERROR' + | 'RESYNC_ERROR' + | 'CRM_INITIAL_SYNC_ERROR' + | 'TICKETING_INITIAL_SYNC_ERROR' + | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' + | 'FILESTORAGE_INITIAL_SYNC_ERROR' + | 'ACCOUNTING_INITIAL_SYNC_ERROR' + | 'HRIS_INITIAL_SYNC_ERROR' + | 'ATS_INITIAL_SYNC_ERROR' + | 'MARKETINGAUTOMATION_INITIAL_SYNC_ERROR' +> {} + +export class UnifiedTicketingError extends ErrorBase< + | 'GET_USER_ERROR' + | 'GET_USERS_ERROR' + | 'GET_TICKET_ERROR' + | 'GET_TICKETS_ERROR' + | 'CREATE_TICKET_ERROR' + | 'CREATE_TICKETS_ERROR' + | 'GET_TEAM_ERROR' + | 'GET_TEAMS_ERROR' + | 'GET_TAG_ERROR' + | 'GET_TAGS_ERROR' + | 'GET_CONTACT_ERROR' + | 'GET_CONTACTS_ERROR' + | 'GET_ACCOUNTS_ERROR' + | 'GET_ACCOUNT_ERROR' + | 'GET_COMMENT_ERROR' + | 'GET_COMMENTS_ERROR' + | 'CREATE_COMMENTS_ERROR' + | 'CREATE_COMMENT_ERROR' + | 'GET_ATTACHMENTS_ERROR' + | 'GET_ATTACHMENT_ERROR' + | 'CREATE_ATTACHMENTS_ERROR' + | 'CREATE_ATTACHMENT_ERROR' + | 'GET_COLLECTION_ERROR' + | 'GET_COLLECTIONS_ERROR' +> {} + +export class UnifiedCrmError extends ErrorBase< + | 'GET_COMPANY_ERROR' + | 'GET_COMPANIES_ERROR' + | 'CREATE_COMPANY_ERROR' + | 'CREATE_COMPANIES_ERROR' + | 'GET_CONTACTS_ERROR' + | 'GET_CONTACT_ERROR' + | 'CREATE_CONTACT_ERROR' + | 'CREATE_CONTACTS_ERROR' + | 'GET_DEAL_ERROR' + | 'GET_DEALS_ERROR' + | 'CREATE_DEAL_ERROR' + | 'CREATE_DEALS_ERROR' + | 'GET_ENGAGEMENT_ERROR' + | 'GET_ENGAGEMENTS_ERROR' + | 'CREATE_ENGAGEMENTS_ERROR' + | 'CREATE_ENGAGEMENT_ERROR' + | 'GET_NOTES_ERROR' + | 'GET_NOTE_ERROR' + | 'CREATE_NOTES_ERROR' + | 'CREATE_NOTE_ERROR' + | 'GET_STAGE_ERROR' + | 'GET_STAGES_ERROR' + | 'GET_TASKS_ERROR' + | 'GET_TASK_ERROR' + | 'CREATE_TASKS_ERROR' + | 'CREATE_TASK_ERROR' + | 'GET_USERS_ERROR' + | 'GET_USER_ERROR' +> {} + +export class ThirdPartyApiServiceError extends Error { + cause: any; + constructor(message: string, cause?: any) { + super(message); + this.name = 'ThirdPartyApiServiceError'; + this.cause = cause; } } -// Custom error for unauthorized (401) errors -export class UnauthorizedError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.UNAUTHORIZED); - this.name = 'UnauthorizedError'; +export function throwTypedError(error: TypedError, logger?: LoggerService) { + const errorMessage = error.message; + if (logger) { + logger.error('Error message: ', errorMessage); } + throw error; } -// Custom error for duplicate element inside Prisma DB errors -export class NotUniqueRecord extends Error { - constructor(message: string) { - super(message); - this.name = 'NotUniqueRecord'; +export function format3rdPartyError( + providerName: string, + action: TargetObject | Action, + actionType: ActionType, +) { + switch (actionType) { + case 'POST': + return `Failed to create ${action} for ${providerName}`; + case 'GET': + return `Failed to retrieve ${action} for ${providerName}`; + case 'PUT': + return `Failed to update ${action} for ${providerName}`; + case 'DELETE': + return `Failed to delete ${action} for ${providerName}`; } } -export function handleServiceError( - error: ServiceError, +export function handle3rdPartyServiceError( + error: any, logger: LoggerService, - providerName?: string, - action?: TargetObject | Action, - actionType?: ActionType, + providerName: string, + action: TargetObject | Action, + actionType: ActionType, ) { - let statusCode = 500; // Default to internal server error - let errorMessage = error.message; - - if (axios.isAxiosError(error)) { - statusCode = error.response?.status || 500; // Use HTTP status code from Axios error or default to 500 - errorMessage = error.response?.data || error.message; - logger.error('Error with Axios request:', errorMessage); - } else if (error instanceof Prisma.PrismaClientKnownRequestError) { - // Handle Prisma errors - logger.error('Error with Prisma request:', errorMessage); - } else { - logger.error('An unknown error occurred...', errorMessage); - } - - if (error instanceof HttpException) { - throw error; - } - - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new HttpException(errorMessage, statusCode); - } - - return { - data: null, - error: errorMessage, - message: - action && providerName && actionType - ? actionType == 'POST' - ? `Failed to create ${action} for ${providerName}.` - : actionType == 'GET' - ? `Failed to retrieve ${action} for ${providerName}.` - : actionType == 'PUT' - ? `Failed to update ${action} for ${providerName}.` - : `Failed to delete ${action} for ${providerName}.` - : errorMessage, - statusCode: statusCode, - }; + throwTypedError( + new ThirdPartyApiServiceError( + format3rdPartyError(providerName, action, actionType), + error, + ), + logger, + ); } diff --git a/packages/api/src/@core/utils/services/validateUser.service.ts b/packages/api/src/@core/utils/services/validateUser.service.ts index 47b65ac8c..b439fbd90 100644 --- a/packages/api/src/@core/utils/services/validateUser.service.ts +++ b/packages/api/src/@core/utils/services/validateUser.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; @Injectable() export class ValidateUserService { @@ -15,11 +14,11 @@ export class ValidateUserService { }, }); if (project.id_user !== user_id) { - throw new Error('Unauthorized call from sender'); + throw new ReferenceError('User mismatch'); } return true; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/@core/utils/unification/unify.ts b/packages/api/src/@core/utils/unification/unify.ts index 5c651fde0..5eeeb207b 100644 --- a/packages/api/src/@core/utils/unification/unify.ts +++ b/packages/api/src/@core/utils/unification/unify.ts @@ -58,5 +58,4 @@ export async function unify({ customFieldMappings, }); } - return; } diff --git a/packages/api/src/@core/webhook/webhook.service.ts b/packages/api/src/@core/webhook/webhook.service.ts index 83d4725a8..7580f7a4b 100644 --- a/packages/api/src/@core/webhook/webhook.service.ts +++ b/packages/api/src/@core/webhook/webhook.service.ts @@ -4,7 +4,7 @@ import { InjectQueue } from '@nestjs/bull'; import { PrismaService } from '@@core/prisma/prisma.service'; import { v4 as uuidv4 } from 'uuid'; import { LoggerService } from '@@core/logger/logger.service'; -import { handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, WebhooksError } from '@@core/utils/errors'; import { WebhookDto } from './dto/webhook.dto'; import axios from 'axios'; import crypto from 'crypto'; @@ -20,10 +20,21 @@ export class WebhookService { } generateSignature(payload: any, secret: string): string { - return crypto - .createHmac('sha256', secret) - .update(JSON.stringify(payload)) - .digest('hex'); + try { + return crypto + .createHmac('sha256', secret) + .update(JSON.stringify(payload)) + .digest('hex'); + } catch (error) { + throwTypedError( + new WebhooksError({ + name: 'SIGNATURE_GENERATION_ERROR', + message: 'WebhookService.generateSignature() call failed', + cause: error, + }), + this.logger, + ); + } } async getWebhookEndpoints(project_id: string) { @@ -34,9 +45,17 @@ export class WebhookService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'GET_WEBHOOKS_ERROR', + message: 'WebhookService.getWebhookEndpoints() call failed', + cause: error, + }), + this.logger, + ); } } + async updateStatusWebhookEndpoint(id: string, active: boolean) { try { return await this.prisma.webhook_endpoints.update({ @@ -44,7 +63,14 @@ export class WebhookService { data: { active: active }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'UPDATE_WEBHOOK_STATUS_ERROR', + message: 'WebhookService.updateStatusWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } @@ -63,16 +89,34 @@ export class WebhookService { }, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'CREATE_WEBHOOK_ERROR', + message: 'WebhookService.createWebhookEndpoint() call failed', + cause: error, + }), + this.logger, + ); } } async deleteWebhook(whId: string) { - return await this.prisma.webhook_endpoints.delete({ - where: { - id_webhook_endpoint: whId, - }, - }); + try { + return await this.prisma.webhook_endpoints.delete({ + where: { + id_webhook_endpoint: whId, + }, + }); + } catch (error) { + throwTypedError( + new WebhooksError({ + name: 'DELETE_WEBHOOK_ERROR', + message: 'WebhookService.deleteWebhook() call failed', + cause: error, + }), + this.logger, + ); + } } async handleWebhook( @@ -91,6 +135,8 @@ export class WebhookService { active: true, }, }); + + // we dont deliver the webhook if (!webhooks) return; const webhook = webhooks.find((wh) => { @@ -98,6 +144,7 @@ export class WebhookService { return scopes.includes(eventType); }); + // we dont deliver the webhook if (!webhook) return; this.logger.log('handling webhook payload....'); @@ -127,7 +174,14 @@ export class WebhookService { webhook_delivery_id: w_delivery.id_webhook_delivery_attempt, }); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_WEBHOOK_ERROR', + message: 'WebhookService.handleWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -251,7 +305,14 @@ export class WebhookService { } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_PRIORITY_WEBHOOK_ERROR', + message: 'WebhookService.handlePriorityWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -264,7 +325,14 @@ export class WebhookService { { delay: 60000 }, ); } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new WebhooksError({ + name: 'DELIVERING_FAILED_WEBHOOK_ERROR', + message: 'WebhookService.handleFailedWebhook() call failed', + cause: error, + }), + this.logger, + ); } } @@ -276,11 +344,21 @@ export class WebhookService { try { const expected = this.generateSignature(payload, secret); if (expected !== signature) { - throw new Error('Invalid signature'); + throw new WebhooksError({ + name: 'INVALID_SIGNATURE_ERROR', + message: `Signature mismatch for the payload received with signature=${signature}`, + }); } return 200; } catch (error) { - throw new Error(error); + throwTypedError( + new WebhooksError({ + name: 'VERIFY_PAYLOAD_ERROR', + message: 'WebhookService.verifyPayloadSignature() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/crm/@lib/@types/index.ts b/packages/api/src/crm/@lib/@types/index.ts index 6cd6cad51..cc1f45e32 100644 --- a/packages/api/src/crm/@lib/@types/index.ts +++ b/packages/api/src/crm/@lib/@types/index.ts @@ -415,7 +415,7 @@ export class Address { @ApiProperty({ type: String, - description: 'The country.', + description: 'The country', }) @IsString() country: string; diff --git a/packages/api/src/crm/@lib/@utils/index.ts b/packages/api/src/crm/@lib/@utils/index.ts index 921fa64c1..cfbf18d30 100644 --- a/packages/api/src/crm/@lib/@utils/index.ts +++ b/packages/api/src/crm/@lib/@utils/index.ts @@ -84,11 +84,10 @@ export class Utils { id_crm_user: uuid, }, }); - // if (!res) throw new Error(`crm_user not found for uuid ${uuid}`); - if (!res) return; + if (!res) throw new ReferenceError(`crm_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -99,11 +98,10 @@ export class Utils { id_crm_user: uuid, }, }); - // if (!res) throw new Error(`crm_user not found for uuid ${uuid}`); - if (!res) return; + if (!res) throw new ReferenceError(`crm_user not found for uuid ${uuid}`); return res; } catch (error) { - throw new Error(error); + throw error; } } @@ -116,14 +114,13 @@ export class Utils { }, }); if (!res) { - // throw new Error( - // `crm_user not found for remote_id ${remote_id} and integration ${remote_platform}`, - // ); - return; + throw new ReferenceError( + `crm_user not found for remote_id ${remote_id} and integration ${remote_platform}`, + ); } return res.id_crm_user; } catch (error) { - throw new Error(error); + throw error; } } @@ -136,11 +133,12 @@ export class Utils { }, }); - if (!res) return; - + if (!res) { + throw new ReferenceError(`crm_companies not found for id ${id}`); + } return res.name; } catch (error) { - throw new Error(error); + throw error; } } @@ -152,12 +150,12 @@ export class Utils { remote_platform: remote_platform, }, }); - - if (!res) return; + if (!res) + throw new ReferenceError(`crm_deals_stage not found for uuid ${id}`); return res.stage_name; } catch (error) { - throw new Error(error); + throw error; } } @@ -169,12 +167,11 @@ export class Utils { }, }); if (!res) { - // throw new Error(`crm_companies not found for uuid ${uuid}`); - return; + throw new ReferenceError(`crm_companies not found for uuid ${uuid}`); } return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -187,14 +184,13 @@ export class Utils { }, }); if (!res) { - return; - // throw new Error( - // `crm_companies not found for remote_id ${remote_id} and integration ${remote_platform}`, - // ); + throw new ReferenceError( + `crm_companies not found for remote_id ${remote_id} and integration ${remote_platform}`, + ); } return res.id_crm_company; } catch (error) { - throw new Error(error); + throw error; } } @@ -205,11 +201,11 @@ export class Utils { id_crm_deals_stage: uuid, }, }); - // if (!res) throw new Error(`crm_deals_stages not found for uuid ${uuid}`); - if (!res) return; + if (!res) + throw new ReferenceError(`crm_deals_stages not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -222,10 +218,9 @@ export class Utils { }, }); if (!res) { - return; - // throw new Error( - // `crm_deals_stages not found for remote_id ${remote_id} and integration ${remote_platform}`, - // ); + throw new ReferenceError( + `crm_deals_stages not found for remote_id ${remote_id} and integration ${remote_platform}`, + ); } return res.id_crm_deals_stage; } catch (error) { @@ -240,11 +235,11 @@ export class Utils { id_crm_contact: uuid, }, }); - // if (!res) throw new Error(`crm_contacts not found for uuid ${uuid}`); - if (!res) return; + if (!res) + throw new ReferenceError(`crm_contacts not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -256,14 +251,13 @@ export class Utils { remote_platform: remote_platform, }, }); - // if (!res) - // throw new Error( - // `crm_contacts not found for remote_id ${remote_id} and integration ${remote_platform}`, - // ); - if (!res) return; + if (!res) + throw new ReferenceError( + `crm_contacts not found for remote_id ${remote_id} and integration ${remote_platform}`, + ); return res.id_crm_contact; } catch (error) { - throw new Error(error); + throw error; } } @@ -274,11 +268,11 @@ export class Utils { id_crm_deal: uuid, }, }); - // if (!res) throw new Error(`crm_deals not found for uuid ${uuid}`); - if (!res) return; + if (!res) + throw new ReferenceError(`crm_deals not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -290,14 +284,13 @@ export class Utils { remote_platform: remote_platform, }, }); - // if (!res) - // throw new Error( - // `crm_deals not found for remote_id ${remote_id} and integration ${remote_platform}`, - // ); - if (!res) return; + if (!res) + throw new ReferenceError( + `crm_deals not found for remote_id ${remote_id} and integration ${remote_platform}`, + ); return res.id_crm_deal; } catch (error) { - throw new Error(error); + throw error; } } @@ -305,12 +298,12 @@ export class Utils { try { switch (provider_name.toLowerCase()) { default: - throw new Error( + throw new ReferenceError( 'Provider not supported for status custom task mapping', ); } } catch (error) { - throw new Error(error); + throw error; } } @@ -319,7 +312,7 @@ export class Utils { return priority === 'High' ? 'HIGH' : priority === 'Medium' - ? 'MEDIUM' - : 'LOW'; + ? 'MEDIUM' + : 'LOW'; } } diff --git a/packages/api/src/crm/company/services/attio/index.ts b/packages/api/src/crm/company/services/attio/index.ts index 23e70299b..c7fdad88e 100644 --- a/packages/api/src/crm/company/services/attio/index.ts +++ b/packages/api/src/crm/company/services/attio/index.ts @@ -4,7 +4,7 @@ import axios from 'axios'; import { CrmObject } from '@crm/@lib/@types'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ICompanyService } from '@crm/company/types'; @@ -58,7 +58,7 @@ export class AttioService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', @@ -97,7 +97,7 @@ export class AttioService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', diff --git a/packages/api/src/crm/company/services/close/index.ts b/packages/api/src/crm/company/services/close/index.ts index 2f9b0b23f..6a57c529e 100644 --- a/packages/api/src/crm/company/services/close/index.ts +++ b/packages/api/src/crm/company/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -40,7 +40,7 @@ export class CloseService implements ICompanyService { }, }); const resp = await axios.post( - `${connection.account_url}/lead/`, + `${connection.account_url}/lead`, JSON.stringify(companyData), { headers: { @@ -57,7 +57,7 @@ export class CloseService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -105,7 +105,7 @@ export class CloseService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/company/services/close/mappers.ts b/packages/api/src/crm/company/services/close/mappers.ts index 83fd20ef8..026c01879 100644 --- a/packages/api/src/crm/company/services/close/mappers.ts +++ b/packages/api/src/crm/company/services/close/mappers.ts @@ -77,9 +77,9 @@ export class CloseCompanyMapper implements ICompanyMapper { } } let opts: any = {}; - if (company?.created_by) { + if (company?.created_by || company?.custom?.close_owner_id) { const owner_id = await this.utils.getUserUuidFromRemoteId( - company?.created_by as string, + (company?.created_by || company?.custom?.close_owner_id) as string, 'close', ); if (owner_id) { diff --git a/packages/api/src/crm/company/services/close/types.ts b/packages/api/src/crm/company/services/close/types.ts index 8fe69ac4c..fb515f762 100644 --- a/packages/api/src/crm/company/services/close/types.ts +++ b/packages/api/src/crm/company/services/close/types.ts @@ -96,7 +96,7 @@ interface Opportunity { lead_id: string; } -interface Company { +interface Lead { status_id: string; status_label: string; tasks: any[]; @@ -119,7 +119,7 @@ interface Company { description: string; } -export type CloseCompanyOutput = Partial; +export type CloseCompanyOutput = Partial; export const commonCompanyCloseProperties = { city: '', diff --git a/packages/api/src/crm/company/services/company.service.ts b/packages/api/src/crm/company/services/company.service.ts index 156a31cda..3a948f47c 100644 --- a/packages/api/src/crm/company/services/company.service.ts +++ b/packages/api/src/crm/company/services/company.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCompanyInput, @@ -17,6 +16,7 @@ import { OriginalCompanyOutput } from '@@core/utils/types/original/original.crm' import { unify } from '@@core/utils/unification/unify'; import { ICompanyService } from '../types'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class CompanyService { @@ -52,7 +52,13 @@ export class CompanyService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_COMPANIES_ERROR', + message: 'CompanyService.batchAddCompanies() call failed', + cause: error, + }), + ); } } @@ -70,7 +76,7 @@ export class CompanyService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const user = unifiedCompanyData.user_id; //check if user_id refer to real uuids @@ -81,7 +87,9 @@ export class CompanyService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -363,7 +371,13 @@ export class CompanyService { ); return result_company; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_COMPANY_ERROR', + message: 'CompanyService.addCompany() call failed', + cause: error, + }), + ); } } @@ -447,7 +461,13 @@ export class CompanyService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_COMPANY_ERROR', + message: 'CompanyService.getCompany() call failed', + cause: error, + }), + ); } } @@ -456,10 +476,13 @@ export class CompanyService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedCompanyOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedCompanyOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { - let prev_cursor = null; let next_cursor = null; @@ -468,21 +491,23 @@ export class CompanyService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_company: cursor - } + id_crm_company: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let companies = await this.prisma.crm_companies.findMany({ + const companies = await this.prisma.crm_companies.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_company: cursor - } : undefined, + cursor: cursor + ? { + id_crm_company: cursor, + } + : undefined, orderBy: { - modified_at: 'asc' + modified_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -495,8 +520,10 @@ export class CompanyService { }, }); - if (companies.length === (pageSize + 1)) { - next_cursor = Buffer.from(companies[companies.length - 1].id_crm_company).toString('base64'); + if (companies.length === pageSize + 1) { + next_cursor = Buffer.from( + companies[companies.length - 1].id_crm_company, + ).toString('base64'); companies.pop(); } @@ -586,10 +613,16 @@ export class CompanyService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_COMPANIES_ERROR', + message: 'CompanyService.getCompanies() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/company/services/hubspot/index.ts b/packages/api/src/crm/company/services/hubspot/index.ts index 3c2a0b667..08de2829e 100644 --- a/packages/api/src/crm/company/services/hubspot/index.ts +++ b/packages/api/src/crm/company/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/company/services/pipedrive/index.ts b/packages/api/src/crm/company/services/pipedrive/index.ts index 60bcc32be..462c40bbd 100644 --- a/packages/api/src/crm/company/services/pipedrive/index.ts +++ b/packages/api/src/crm/company/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -91,7 +91,7 @@ export class PipedriveService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/company/services/zendesk/index.ts b/packages/api/src/crm/company/services/zendesk/index.ts index a8d1ce4c5..11f727c77 100644 --- a/packages/api/src/crm/company/services/zendesk/index.ts +++ b/packages/api/src/crm/company/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements ICompanyService { ActionType.POST, ); } - return; } async syncCompanies( @@ -101,7 +100,7 @@ export class ZendeskService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/company/services/zoho/index.ts b/packages/api/src/crm/company/services/zoho/index.ts index 180c7c52e..e4584a89e 100644 --- a/packages/api/src/crm/company/services/zoho/index.ts +++ b/packages/api/src/crm/company/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements ICompanyService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements ICompanyService { ActionType.POST, ); } - return; } async syncCompanies( @@ -97,7 +96,7 @@ export class ZohoService implements ICompanyService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/company/sync/sync.service.ts b/packages/api/src/crm/company/sync/sync.service.ts index 48b59dadb..8e76a90f1 100644 --- a/packages/api/src/crm/company/sync/sync.service.ts +++ b/packages/api/src/crm/company/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -18,6 +17,7 @@ import { CRM_PROVIDERS } from '@panora/shared'; import { Queue } from 'bull'; import { InjectQueue } from '@nestjs/bull'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -39,7 +39,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -106,18 +106,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_COMPANY_SYNC_ERROR', + message: 'SyncService.syncCompanies() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -143,7 +150,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping companies syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -199,7 +206,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -216,7 +223,7 @@ export class SyncService implements OnModuleInit { const originId = company.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingCompany = await this.prisma.crm_companies.findFirst({ @@ -481,7 +488,7 @@ export class SyncService implements OnModuleInit { } return companies_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/contact/services/attio/index.ts b/packages/api/src/crm/contact/services/attio/index.ts index 544a2c461..8a82f9e08 100644 --- a/packages/api/src/crm/contact/services/attio/index.ts +++ b/packages/api/src/crm/contact/services/attio/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -57,7 +57,7 @@ export class AttioService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', @@ -65,7 +65,6 @@ export class AttioService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -104,7 +103,7 @@ export class AttioService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Attio', diff --git a/packages/api/src/crm/contact/services/close/index.ts b/packages/api/src/crm/contact/services/close/index.ts index 579d6bd84..ec6fd24c5 100644 --- a/packages/api/src/crm/contact/services/close/index.ts +++ b/packages/api/src/crm/contact/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class CloseService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -95,7 +95,7 @@ export class CloseService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/contact/services/close/mappers.ts b/packages/api/src/crm/contact/services/close/mappers.ts index 16bba49b5..1f5f39e8b 100644 --- a/packages/api/src/crm/contact/services/close/mappers.ts +++ b/packages/api/src/crm/contact/services/close/mappers.ts @@ -44,11 +44,7 @@ export class CloseContactMapper implements IContactMapper { ), }; - if (source.user_id) { - result.lead_id = await this.utils.getRemoteIdFromCompanyUuid( - source.user_id, - ); - } + result.lead_id = source?.field_mappings?.['company_id']; if (customFieldMappings && source.field_mappings) { for (const [k, v] of Object.entries(source.field_mappings)) { @@ -72,42 +68,32 @@ export class CloseContactMapper implements IContactMapper { }[], ): Promise { if (!Array.isArray(source)) { - return await this.mapSingleContactToUnified(source, customFieldMappings); + return this.mapSingleContactToUnified(source, customFieldMappings); } // Handling array of CloseContactOutput - return await Promise.all( - source.map((contact) => - this.mapSingleContactToUnified(contact, customFieldMappings), - ), + return source.map((contact) => + this.mapSingleContactToUnified(contact, customFieldMappings), ); } - private async mapSingleContactToUnified( + private mapSingleContactToUnified( contact: CloseContactOutput, customFieldMappings?: { slug: string; remote_id: string; }[], - ): Promise { + ): UnifiedContactOutput { const field_mappings: { [key: string]: any } = {}; if (customFieldMappings) { for (const mapping of customFieldMappings) { field_mappings[mapping.slug] = contact[mapping.remote_id]; } } - const opts: any = {}; - if (contact.created_by) { - opts.user_id = await this.utils.getUserUuidFromRemoteId( - contact.created_by, - 'close', - ); - } - const [firstName, lastName] = contact?.name?.split(' '); return { remote_id: contact.id, - first_name: firstName || contact?.name, - last_name: lastName ?? '', + first_name: contact.name, + last_name: '', email_addresses: contact.emails?.map(({ email, type }) => ({ email_address: email, email_address_type: type, @@ -119,7 +105,6 @@ export class CloseContactMapper implements IContactMapper { owner_type: 'contact', })), field_mappings, - ...opts, addresses: [], }; } diff --git a/packages/api/src/crm/contact/services/close/types.ts b/packages/api/src/crm/contact/services/close/types.ts index 6bd5ce1e2..a1d937600 100644 --- a/packages/api/src/crm/contact/services/close/types.ts +++ b/packages/api/src/crm/contact/services/close/types.ts @@ -17,7 +17,7 @@ interface CustomFields { [key: string]: string; // Allows dynamic keys for custom fields } -interface ContactInput { +interface LeadInput { lead_id: string; name: string; title: string; @@ -54,7 +54,7 @@ interface Contact { updated_by: string; } -export type CloseContactInput = Partial; +export type CloseContactInput = Partial; export type CloseContactOutput = Partial; diff --git a/packages/api/src/crm/contact/services/contact.service.ts b/packages/api/src/crm/contact/services/contact.service.ts index e3f17000a..bee32c011 100644 --- a/packages/api/src/crm/contact/services/contact.service.ts +++ b/packages/api/src/crm/contact/services/contact.service.ts @@ -11,12 +11,12 @@ import { UnifiedContactOutput, } from '@crm/contact/types/model.unified'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { WebhookService } from '@@core/webhook/webhook.service'; import { OriginalContactOutput } from '@@core/utils/types/original/original.crm'; import { ServiceRegistry } from './registry.service'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class ContactService { @@ -53,7 +53,13 @@ export class ContactService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_CONTACTS_ERROR', + message: 'ContactService.batchAddContacts() call failed', + cause: error, + }), + ); } } @@ -398,7 +404,13 @@ export class ContactService { ); return result_contact; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_CONTACT_ERROR', + message: 'ContactService.addContact() call failed', + cause: error, + }), + ); } } @@ -479,7 +491,13 @@ export class ContactService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_CONTACT_ERROR', + message: 'ContactService.getContact() call failed', + cause: error, + }), + ); } } @@ -488,8 +506,12 @@ export class ContactService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedContactOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedContactOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced @@ -501,21 +523,23 @@ export class ContactService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_contact: cursor - } + id_crm_contact: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let contacts = await this.prisma.crm_contacts.findMany({ + const contacts = await this.prisma.crm_contacts.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_contact: cursor - } : undefined, + cursor: cursor + ? { + id_crm_contact: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -528,8 +552,10 @@ export class ContactService { }, }); - if (contacts.length === (pageSize + 1)) { - next_cursor = Buffer.from(contacts[contacts.length - 1].id_crm_contact).toString('base64'); + if (contacts.length === pageSize + 1) { + next_cursor = Buffer.from( + contacts[contacts.length - 1].id_crm_contact, + ).toString('base64'); contacts.pop(); } @@ -618,10 +644,16 @@ export class ContactService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_CONTACTS_ERROR', + message: 'ContactService.getContacts() call failed', + cause: error, + }), + ); } } //TODO @@ -630,9 +662,7 @@ export class ContactService { updateContactData: Partial, ): Promise { try { - } catch (error) { - handleServiceError(error, this.logger); - } + } catch (error) {} // TODO: fetch the contact from the database using 'id' // TODO: update the contact with 'updateContactData' // TODO: save the updated contact back to the database diff --git a/packages/api/src/crm/contact/services/hubspot/index.ts b/packages/api/src/crm/contact/services/hubspot/index.ts index e083db679..10a36f9d1 100644 --- a/packages/api/src/crm/contact/services/hubspot/index.ts +++ b/packages/api/src/crm/contact/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -61,7 +61,7 @@ export class HubspotService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -110,7 +110,7 @@ export class HubspotService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/contact/services/pipedrive/index.ts b/packages/api/src/crm/contact/services/pipedrive/index.ts index 96301a01f..6e17f6b38 100644 --- a/packages/api/src/crm/contact/services/pipedrive/index.ts +++ b/packages/api/src/crm/contact/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class PipedriveService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -63,7 +63,6 @@ export class PipedriveService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -92,7 +91,7 @@ export class PipedriveService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/contact/services/registry.service.ts b/packages/api/src/crm/contact/services/registry.service.ts index be0d9c608..bc664c41c 100644 --- a/packages/api/src/crm/contact/services/registry.service.ts +++ b/packages/api/src/crm/contact/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IContactService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/crm/contact/services/zendesk/index.ts b/packages/api/src/crm/contact/services/zendesk/index.ts index c5a0c70ec..14bf5bbb9 100644 --- a/packages/api/src/crm/contact/services/zendesk/index.ts +++ b/packages/api/src/crm/contact/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -102,7 +101,7 @@ export class ZendeskService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/contact/services/zoho/index.ts b/packages/api/src/crm/contact/services/zoho/index.ts index e1d3f8a91..a81bb1718 100644 --- a/packages/api/src/crm/contact/services/zoho/index.ts +++ b/packages/api/src/crm/contact/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements IContactService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements IContactService { ActionType.POST, ); } - return; } async syncContacts( @@ -98,7 +97,7 @@ export class ZohoService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/contact/sync/sync.service.ts b/packages/api/src/crm/contact/sync/sync.service.ts index 0adc59b52..707302683 100644 --- a/packages/api/src/crm/contact/sync/sync.service.ts +++ b/packages/api/src/crm/contact/sync/sync.service.ts @@ -1,7 +1,6 @@ import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { ApiResponse } from '@@core/utils/types'; import { unify } from '@@core/utils/unification/unify'; import { WebhookService } from '@@core/webhook/webhook.service'; @@ -18,6 +17,7 @@ import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; import { Utils } from '@crm/@lib/@utils'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -39,7 +39,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -119,18 +119,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_CONTACT_SYNC_ERROR', + message: 'SyncService.syncContacts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -156,7 +163,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -212,7 +219,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -229,7 +236,7 @@ export class SyncService implements OnModuleInit { const originId = contact.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingContact = await this.prisma.crm_contacts.findFirst({ @@ -494,7 +501,7 @@ export class SyncService implements OnModuleInit { } return contacts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/deal/services/close/index.ts b/packages/api/src/crm/deal/services/close/index.ts index c12148a5a..ab9bfe1c7 100644 --- a/packages/api/src/crm/deal/services/close/index.ts +++ b/packages/api/src/crm/deal/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -35,7 +35,7 @@ export class CloseService implements IDealService { }, }); const resp = await axios.post( - `${connection.account_url}/opportunity/`, + `${connection.account_url}/opportunity`, JSON.stringify(dealData), { headers: { @@ -53,7 +53,7 @@ export class CloseService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -94,7 +94,7 @@ export class CloseService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/deal/services/close/mappers.ts b/packages/api/src/crm/deal/services/close/mappers.ts index 2a0538f73..2fd716778 100644 --- a/packages/api/src/crm/deal/services/close/mappers.ts +++ b/packages/api/src/crm/deal/services/close/mappers.ts @@ -20,33 +20,32 @@ export class CloseDealMapper implements IDealMapper { remote_id: string; }[], ): Promise { - const emptyPromise = new Promise((resolve) => { - return resolve(''); - }); - const promises = []; - - promises.push( - source.company_id - ? await this.utils.getRemoteIdFromCompanyUuid(source.company_id) - : emptyPromise, - ); - - // promises.push( - // source.stage_id - // ? await this.utils.getStageIdFromStageUuid(source.stage_id) - // : emptyPromise, - // ); - const [lead_id] = await Promise.all(promises); const result: CloseDealInput = { note: source.description, confidence: 0, value: source.amount || 0, - value_period: 'monthly', + value_period: 'one_time', custom: {}, - lead_id, - status_id: source.stage_id, + lead_id: '', }; + if (source.company_id) { + const lead_id = await this.utils.getRemoteIdFromCompanyUuid( + source.company_id, + ); + if (lead_id) { + result.lead_id = lead_id; + } + } + if (source.stage_id) { + const stage_id = await this.utils.getStageIdFromStageUuid( + source.company_id, + ); + if (stage_id) { + result.status_id = stage_id; + } + } + if (customFieldMappings && source.field_mappings) { for (const [k, v] of Object.entries(source.field_mappings)) { const mapping = customFieldMappings.find( @@ -92,37 +91,51 @@ export class CloseDealMapper implements IDealMapper { } } - const emptyPromise = new Promise((resolve) => { - return resolve(''); - }); - const promises = []; - - promises.push( - deal.user_id - ? await this.utils.getUserUuidFromRemoteId(deal.user_id, 'close') - : emptyPromise, - ); - promises.push( - deal.lead_id - ? await this.utils.getCompanyUuidFromRemoteId(deal.lead_id, 'close') - : emptyPromise, - ); - // promises.push( - // deal.status_id - // ? await this.utils.getStageUuidFromRemoteId(deal.status_id, 'close') - // : emptyPromise, - // ); - - const [user_id, company_id] = await Promise.all(promises); - + let opts: any = {}; + if (deal.user_id) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + deal.user_id, + 'close', + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + if (deal.lead_id) { + const lead_id = await this.utils.getCompanyUuidFromRemoteId( + deal.lead_id, + 'close', + ); + if (lead_id) { + opts = { + ...opts, + company_id: lead_id, + }; + } + } + if (deal.contact_id) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + deal.contact_id, + 'close', + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id, + }; + } + } return { remote_id: deal.id, name: deal.note, description: deal.note, // Placeholder if there's no direct mapping amount: parseFloat(`${deal.value || 0}`), + //TODO; stage_id: deal.properties.dealstage, field_mappings, - user_id, - company_id, + ...opts, }; } } diff --git a/packages/api/src/crm/deal/services/deal.service.ts b/packages/api/src/crm/deal/services/deal.service.ts index 64d3d30dc..85a484fa2 100644 --- a/packages/api/src/crm/deal/services/deal.service.ts +++ b/packages/api/src/crm/deal/services/deal.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedDealInput, UnifiedDealOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalDealOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { IDealService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class DealService { @@ -46,7 +46,13 @@ export class DealService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_DEALS_ERROR', + message: 'DealService.batchAddDeals() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class DealService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const stage = unifiedDealData.stage_id; //check if contact_id and account_id refer to real uuids @@ -75,7 +81,9 @@ export class DealService { }, }); if (!search) - throw new Error('You inserted a stage_id which does not exist'); + throw new ReferenceError( + 'You inserted a stage_id which does not exist', + ); } const user = unifiedDealData.user_id; @@ -87,7 +95,9 @@ export class DealService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -242,7 +252,13 @@ export class DealService { ); return result_deal; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_DEAL_ERROR', + message: 'DealService.addDeal() call failed', + cause: error, + }), + ); } } @@ -311,7 +327,13 @@ export class DealService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_DEAL_ERROR', + message: 'DealService.getDeal() call failed', + cause: error, + }), + ); } } @@ -320,10 +342,13 @@ export class DealService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedDealOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedDealOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { - let prev_cursor = null; let next_cursor = null; @@ -332,21 +357,23 @@ export class DealService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_deal: cursor - } + id_crm_deal: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let deals = await this.prisma.crm_deals.findMany({ + const deals = await this.prisma.crm_deals.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_deal: cursor - } : undefined, + cursor: cursor + ? { + id_crm_deal: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -354,8 +381,10 @@ export class DealService { }, }); - if (deals.length === (pageSize + 1)) { - next_cursor = Buffer.from(deals[deals.length - 1].id_crm_deal).toString('base64'); + if (deals.length === pageSize + 1) { + next_cursor = Buffer.from(deals[deals.length - 1].id_crm_deal).toString( + 'base64', + ); deals.pop(); } @@ -436,10 +465,16 @@ export class DealService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_DEALS_ERROR', + message: 'DealService.getDeals() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/deal/services/hubspot/index.ts b/packages/api/src/crm/deal/services/hubspot/index.ts index 5c9ed96d3..8e463e344 100644 --- a/packages/api/src/crm/deal/services/hubspot/index.ts +++ b/packages/api/src/crm/deal/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -62,7 +62,7 @@ export class HubspotService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -112,7 +112,7 @@ export class HubspotService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/deal/services/pipedrive/index.ts b/packages/api/src/crm/deal/services/pipedrive/index.ts index 20c931ed9..f20ba46b9 100644 --- a/packages/api/src/crm/deal/services/pipedrive/index.ts +++ b/packages/api/src/crm/deal/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveDealInput, PipedriveDealOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -91,7 +90,7 @@ export class PipedriveService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/deal/services/zendesk/index.ts b/packages/api/src/crm/deal/services/zendesk/index.ts index 7ae8b94a3..324a2c566 100644 --- a/packages/api/src/crm/deal/services/zendesk/index.ts +++ b/packages/api/src/crm/deal/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskDealInput, ZendeskDealOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -97,7 +96,7 @@ export class ZendeskService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/deal/services/zoho/index.ts b/packages/api/src/crm/deal/services/zoho/index.ts index 4ec0f9cb2..491344722 100644 --- a/packages/api/src/crm/deal/services/zoho/index.ts +++ b/packages/api/src/crm/deal/services/zoho/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class ZohoService implements IDealService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -62,7 +62,6 @@ export class ZohoService implements IDealService { ActionType.POST, ); } - return; } async syncDeals( @@ -97,7 +96,7 @@ export class ZohoService implements IDealService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/deal/sync/sync.service.ts b/packages/api/src/crm/deal/sync/sync.service.ts index ebd069aec..1bdb33547 100644 --- a/packages/api/src/crm/deal/sync/sync.service.ts +++ b/packages/api/src/crm/deal/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { crm_deals as CrmDeal } from '@prisma/client'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -108,18 +108,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_DEAL_SYNC_ERROR', + message: 'SyncService.syncDeals() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -145,7 +152,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping deals syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -203,7 +210,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -220,7 +227,7 @@ export class SyncService implements OnModuleInit { const originId = deal.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingDeal = await this.prisma.crm_deals.findFirst({ @@ -366,7 +373,7 @@ export class SyncService implements OnModuleInit { } return deals_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/engagement/services/close/index.ts b/packages/api/src/crm/engagement/services/close/index.ts index 7662a3e73..9832920e5 100644 --- a/packages/api/src/crm/engagement/services/close/index.ts +++ b/packages/api/src/crm/engagement/services/close/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -58,7 +58,7 @@ export class CloseService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -98,7 +98,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -138,7 +138,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -181,7 +181,7 @@ export class CloseService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -208,7 +208,7 @@ export class CloseService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -246,7 +246,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -286,7 +286,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -322,7 +322,7 @@ export class CloseService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/engagement/services/engagement.service.ts b/packages/api/src/crm/engagement/services/engagement.service.ts index 83921540c..60f5e4dd6 100644 --- a/packages/api/src/crm/engagement/services/engagement.service.ts +++ b/packages/api/src/crm/engagement/services/engagement.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedEngagementInput, @@ -49,7 +49,13 @@ export class EngagementService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_ENGAGEMENTS_ERROR', + message: 'EngagmentService.batchAddEngagements() call failed', + cause: error, + }), + ); } } @@ -67,7 +73,7 @@ export class EngagementService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const company = unifiedEngagementData.company_id; //check if contact_id and account_id refer to real uuids @@ -78,16 +84,18 @@ export class EngagementService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const type = unifiedEngagementData.type.toUpperCase(); if (type) { if (!ENGAGEMENTS_TYPE.includes(type)) - throw new Error( + throw new ReferenceError( 'You inserted a engagement type which does not exist', ); } else { - throw new Error('You didnt insert a type for your engagement'); + throw new ReferenceError('You didnt insert a type for your engagement'); } const engagement_contacts = unifiedEngagementData.contacts; @@ -99,7 +107,7 @@ export class EngagementService { }, }); if (!search) - throw new Error( + throw new ReferenceError( 'You inserted an id_crm_engagement_contact which does not exist', ); }); @@ -124,8 +132,8 @@ export class EngagementService { type === 'CALL' ? CrmObject.engagement_call : type === 'MEETING' - ? CrmObject.engagement_meeting - : CrmObject.engagement_email; + ? CrmObject.engagement_meeting + : CrmObject.engagement_email; //unify the data according to the target obj wanted const unifiedObject = (await unify({ @@ -286,7 +294,13 @@ export class EngagementService { ); return result_engagement; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_ENGAGEMENT_ERROR', + message: 'EngagmentService.addEngagement() call failed', + cause: error, + }), + ); } } @@ -363,7 +377,13 @@ export class EngagementService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_ENGAGEMENT_ERROR', + message: 'EngagmentService.getEngagement() call failed', + cause: error, + }), + ); } } @@ -372,8 +392,12 @@ export class EngagementService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedEngagementOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedEngagementOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { let prev_cursor = null; let next_cursor = null; @@ -383,21 +407,23 @@ export class EngagementService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_engagement: cursor - } + id_crm_engagement: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let engagements = await this.prisma.crm_engagements.findMany({ + const engagements = await this.prisma.crm_engagements.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_engagement: cursor - } : undefined, + cursor: cursor + ? { + id_crm_engagement: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -405,8 +431,10 @@ export class EngagementService { }, }); - if (engagements.length === (pageSize + 1)) { - next_cursor = Buffer.from(engagements[engagements.length - 1].id_crm_engagement).toString('base64'); + if (engagements.length === pageSize + 1) { + next_cursor = Buffer.from( + engagements[engagements.length - 1].id_crm_engagement, + ).toString('base64'); engagements.pop(); } @@ -495,10 +523,16 @@ export class EngagementService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_ENGAGEMENTS_ERROR', + message: 'EngagmentService.getEngagements() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/engagement/services/hubspot/index.ts b/packages/api/src/crm/engagement/services/hubspot/index.ts index 82eb5fa2d..161f6f420 100644 --- a/packages/api/src/crm/engagement/services/hubspot/index.ts +++ b/packages/api/src/crm/engagement/services/hubspot/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -103,7 +103,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -146,7 +146,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -189,7 +189,7 @@ export class HubspotService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -216,7 +216,7 @@ export class HubspotService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -262,7 +262,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -311,7 +311,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -357,7 +357,7 @@ export class HubspotService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/engagement/services/pipedrive/index.ts b/packages/api/src/crm/engagement/services/pipedrive/index.ts index 5df31add2..5ff75674d 100644 --- a/packages/api/src/crm/engagement/services/pipedrive/index.ts +++ b/packages/api/src/crm/engagement/services/pipedrive/index.ts @@ -4,7 +4,7 @@ import { CrmObject } from '@crm/@lib/@types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -44,7 +44,7 @@ export class PipedriveService implements IEngagementService { statusCode: 201, };*/ } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -70,7 +70,7 @@ export class PipedriveService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -110,7 +110,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -149,7 +149,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -188,7 +188,7 @@ export class PipedriveService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/engagement/services/zendesk/index.ts b/packages/api/src/crm/engagement/services/zendesk/index.ts index 5b77b5a14..970ded332 100644 --- a/packages/api/src/crm/engagement/services/zendesk/index.ts +++ b/packages/api/src/crm/engagement/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskEngagementInput, ZendeskEngagementOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -41,7 +41,7 @@ export class ZendeskService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -83,7 +83,7 @@ export class ZendeskService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -109,7 +109,7 @@ export class ZendeskService implements IEngagementService { break; } } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -148,7 +148,7 @@ export class ZendeskService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/engagement/services/zoho/index.ts b/packages/api/src/crm/engagement/services/zoho/index.ts index 81662ff87..d342fd96f 100644 --- a/packages/api/src/crm/engagement/services/zoho/index.ts +++ b/packages/api/src/crm/engagement/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoEngagementInput, ZohoEngagementOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements IEngagementService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements IEngagementService { ActionType.POST, ); } - return; } async syncEngagements( @@ -98,7 +97,7 @@ export class ZohoService implements IEngagementService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/engagement/sync/sync.service.ts b/packages/api/src/crm/engagement/sync/sync.service.ts index a494ef883..119af1e66 100644 --- a/packages/api/src/crm/engagement/sync/sync.service.ts +++ b/packages/api/src/crm/engagement/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalEngagementOutput } from '@@core/utils/types/original/original.c import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing engagements....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -104,18 +104,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_ENGAGEMENT_SYNC_ERROR', + message: 'SyncService.syncEngagements() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -142,7 +149,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping engagements syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -204,7 +210,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -221,7 +227,7 @@ export class SyncService implements OnModuleInit { const originId = engagement.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingEngagement = await this.prisma.crm_engagements.findFirst({ @@ -387,7 +393,7 @@ export class SyncService implements OnModuleInit { } return engagements_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/note/services/close/index.ts b/packages/api/src/crm/note/services/close/index.ts index ab278defd..d9ee7177e 100644 --- a/packages/api/src/crm/note/services/close/index.ts +++ b/packages/api/src/crm/note/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseNoteInput, CloseNoteOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class CloseService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -93,7 +93,7 @@ export class CloseService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/note/services/hubspot/index.ts b/packages/api/src/crm/note/services/hubspot/index.ts index 5d7fff184..137cf65fe 100644 --- a/packages/api/src/crm/note/services/hubspot/index.ts +++ b/packages/api/src/crm/note/services/hubspot/index.ts @@ -9,7 +9,7 @@ import { import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/note/services/note.service.ts b/packages/api/src/crm/note/services/note.service.ts index 3e23c8fc6..3db4934b9 100644 --- a/packages/api/src/crm/note/services/note.service.ts +++ b/packages/api/src/crm/note/services/note.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedNoteInput, UnifiedNoteOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalNoteOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { INoteService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class NoteService { @@ -46,7 +46,13 @@ export class NoteService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_NOTES_ERROR', + message: 'NoteService.batchAddNotes() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class NoteService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const user = unifiedNoteData.user_id; if (user) { @@ -74,7 +80,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const company = unifiedNoteData.company_id; @@ -86,7 +94,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a company_id which does not exist'); + throw new ReferenceError( + 'You inserted a company_id which does not exist', + ); } const contact = unifiedNoteData.contact_id; //check if contact_id and account_id refer to real uuids @@ -97,7 +107,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const deal = unifiedNoteData.deal_id; @@ -109,7 +121,9 @@ export class NoteService { }, }); if (!search) - throw new Error('You inserted a deal_id which does not exist'); + throw new ReferenceError( + 'You inserted a deal_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -257,7 +271,13 @@ export class NoteService { ); return result_note; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_NOTE_ERROR', + message: 'NoteService.addNote()) call failed', + cause: error, + }), + ); } } @@ -326,7 +346,13 @@ export class NoteService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_NOTE_ERROR', + message: 'NoteService.getNote() call failed', + cause: error, + }), + ); } } @@ -335,8 +361,12 @@ export class NoteService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedNoteOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedNoteOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { let prev_cursor = null; let next_cursor = null; @@ -346,21 +376,23 @@ export class NoteService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_note: cursor - } + id_crm_note: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let notes = await this.prisma.crm_notes.findMany({ + const notes = await this.prisma.crm_notes.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_note: cursor - } : undefined, + cursor: cursor + ? { + id_crm_note: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -368,8 +400,10 @@ export class NoteService { }, }); - if (notes.length === (pageSize + 1)) { - next_cursor = Buffer.from(notes[notes.length - 1].id_crm_note).toString('base64'); + if (notes.length === pageSize + 1) { + next_cursor = Buffer.from(notes[notes.length - 1].id_crm_note).toString( + 'base64', + ); notes.pop(); } @@ -450,10 +484,16 @@ export class NoteService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_NOTES_ERROR', + message: 'NoteService.getNotes() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/note/services/pipedrive/index.ts b/packages/api/src/crm/note/services/pipedrive/index.ts index b18c3dd27..9be8eb573 100644 --- a/packages/api/src/crm/note/services/pipedrive/index.ts +++ b/packages/api/src/crm/note/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveNoteInput, PipedriveNoteOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -91,7 +90,7 @@ export class PipedriveService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/note/services/zendesk/index.ts b/packages/api/src/crm/note/services/zendesk/index.ts index 0d8e481d0..d40df6071 100644 --- a/packages/api/src/crm/note/services/zendesk/index.ts +++ b/packages/api/src/crm/note/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskNoteInput, ZendeskNoteOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -56,7 +56,7 @@ export class ZendeskService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -64,7 +64,6 @@ export class ZendeskService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -97,7 +96,7 @@ export class ZendeskService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/note/services/zoho/index.ts b/packages/api/src/crm/note/services/zoho/index.ts index 1dc7ab52d..566b2b404 100644 --- a/packages/api/src/crm/note/services/zoho/index.ts +++ b/packages/api/src/crm/note/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoNoteInput, ZohoNoteOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements INoteService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements INoteService { ActionType.POST, ); } - return; } async syncNotes( @@ -97,7 +96,7 @@ export class ZohoService implements INoteService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/note/sync/sync.service.ts b/packages/api/src/crm/note/sync/sync.service.ts index 73ce8de09..4c1b74ab1 100644 --- a/packages/api/src/crm/note/sync/sync.service.ts +++ b/packages/api/src/crm/note/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalNoteOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing notes....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_NOTE_SYNC_ERROR', + message: 'SyncService.syncNotes() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping notes syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +203,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +220,7 @@ export class SyncService implements OnModuleInit { const originId = note.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingNote = await this.prisma.crm_notes.findFirst({ @@ -347,7 +354,7 @@ export class SyncService implements OnModuleInit { } return notes_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/stage/services/close/index.ts b/packages/api/src/crm/stage/services/close/index.ts index 29235b67d..11617dfad 100644 --- a/packages/api/src/crm/stage/services/close/index.ts +++ b/packages/api/src/crm/stage/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseStageOutput, commonStageCloseProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -41,7 +41,7 @@ export class CloseService implements IStageService { const res = await this.prisma.crm_deals.findUnique({ where: { id_crm_deal: deal_id }, }); - const baseURL = `${connection.account_url}/activity/status_change/opportunity/?opportunity_id=${res?.remote_id}`; + const baseURL = `${connection.account_url}/activity/status_change/opportunity/?opportunity_id=${res.remote_id}`; const resp = await axios.get(baseURL, { headers: { @@ -58,7 +58,7 @@ export class CloseService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/stage/services/hubspot/index.ts b/packages/api/src/crm/stage/services/hubspot/index.ts index 9a1e049d7..8244be028 100644 --- a/packages/api/src/crm/stage/services/hubspot/index.ts +++ b/packages/api/src/crm/stage/services/hubspot/index.ts @@ -5,7 +5,7 @@ import { HubspotStageOutput, commonStageHubspotProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -68,7 +68,7 @@ export class HubspotService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/stage/services/pipedrive/index.ts b/packages/api/src/crm/stage/services/pipedrive/index.ts index 83089432d..b74f3e794 100644 --- a/packages/api/src/crm/stage/services/pipedrive/index.ts +++ b/packages/api/src/crm/stage/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveStageOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -71,7 +71,7 @@ export class PipedriveService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/stage/services/stage.service.ts b/packages/api/src/crm/stage/services/stage.service.ts index f34d839cb..c2035ef58 100644 --- a/packages/api/src/crm/stage/services/stage.service.ts +++ b/packages/api/src/crm/stage/services/stage.service.ts @@ -2,11 +2,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; -import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedStageOutput } from '../types/model.unified'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; -import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class StageService { @@ -75,7 +72,13 @@ export class StageService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_STAGE_ERROR', + message: 'StageService.getStage() call failed', + cause: error, + }), + ); } } @@ -84,8 +87,12 @@ export class StageService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedStageOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedStageOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { let prev_cursor = null; let next_cursor = null; @@ -95,21 +102,23 @@ export class StageService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_deals_stage: cursor - } + id_crm_deals_stage: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let stages = await this.prisma.crm_deals_stages.findMany({ + const stages = await this.prisma.crm_deals_stages.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_deals_stage: cursor - } : undefined, + cursor: cursor + ? { + id_crm_deals_stage: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -117,8 +126,10 @@ export class StageService { }, }); - if (stages.length === (pageSize + 1)) { - next_cursor = Buffer.from(stages[stages.length - 1].id_crm_deals_stage).toString('base64'); + if (stages.length === pageSize + 1) { + next_cursor = Buffer.from( + stages[stages.length - 1].id_crm_deals_stage, + ).toString('base64'); stages.pop(); } @@ -195,10 +206,16 @@ export class StageService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_STAGES_ERROR', + message: 'StageService.getStages() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/stage/services/zendesk/index.ts b/packages/api/src/crm/stage/services/zendesk/index.ts index f6eb415b2..5d5bb5dc4 100644 --- a/packages/api/src/crm/stage/services/zendesk/index.ts +++ b/packages/api/src/crm/stage/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskStageOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -71,7 +71,7 @@ export class ZendeskService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/stage/services/zoho/index.ts b/packages/api/src/crm/stage/services/zoho/index.ts index 97b28db9c..7ee5a1509 100644 --- a/packages/api/src/crm/stage/services/zoho/index.ts +++ b/packages/api/src/crm/stage/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoStageOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -57,7 +57,7 @@ export class ZohoService implements IStageService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/stage/sync/sync.service.ts b/packages/api/src/crm/stage/sync/sync.service.ts index a427405f1..8cadce66d 100644 --- a/packages/api/src/crm/stage/sync/sync.service.ts +++ b/packages/api/src/crm/stage/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalStageOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing stages....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -112,21 +112,28 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_STAGE_SYNC_ERROR', + message: 'SyncService.syncStages() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -153,7 +160,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping stages syncing... No ${integrationId} connection was found for linked stage ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -213,7 +220,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -231,7 +238,7 @@ export class SyncService implements OnModuleInit { const originId = stage.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingStage = await this.prisma.crm_deals.findFirst({ @@ -376,7 +383,7 @@ export class SyncService implements OnModuleInit { } return stages_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/task/services/close/index.ts b/packages/api/src/crm/task/services/close/index.ts index 49ca59e24..6ce71f88c 100644 --- a/packages/api/src/crm/task/services/close/index.ts +++ b/packages/api/src/crm/task/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseTaskInput, CloseTaskOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class CloseService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', @@ -93,7 +93,7 @@ export class CloseService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/task/services/close/mappers.ts b/packages/api/src/crm/task/services/close/mappers.ts index 8a58ff67b..61b9a9073 100644 --- a/packages/api/src/crm/task/services/close/mappers.ts +++ b/packages/api/src/crm/task/services/close/mappers.ts @@ -88,22 +88,42 @@ export class CloseTaskMapper implements ITaskMapper { field_mappings[mapping.slug] = task[mapping.remote_id]; } } - const emptyPromise = new Promise((resolve) => { - return resolve(''); - }); - const promises = []; - - promises.push( - task.assigned_to - ? await this.utils.getUserUuidFromRemoteId(task.assigned_to, 'close') - : emptyPromise, - ); - promises.push( - task.lead_id - ? await this.utils.getCompanyUuidFromRemoteId(task.lead_id, 'close') - : emptyPromise, - ); - const [user_id, company_id] = await Promise.all(promises); + let opts: any = {}; + if (task.assigned_to) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + task.assigned_to, + 'close', + ); + if (owner_id) { + opts = { + user_id: owner_id, + }; + } + } + if (task.contact_id) { + const contact_id = await this.utils.getContactUuidFromRemoteId( + task.contact_id, + 'close', + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id, + }; + } + } + if (task.lead_id) { + const lead_id = await this.utils.getCompanyUuidFromRemoteId( + task.lead_id, + 'close', + ); + if (lead_id) { + opts = { + ...opts, + company_id: lead_id, + }; + } + } return { remote_id: task.id, @@ -111,10 +131,10 @@ export class CloseTaskMapper implements ITaskMapper { content: task.text, status: task?.is_complete ? 'COMPLETED' : 'PENDING', due_date: new Date(task.due_date), - finished_date: task.finished_date ? new Date(task.finished_date) : null, + finished_date: task.finished_date ? new Date(task.finished_date) : '', field_mappings, - user_id, - company_id, + ...opts, + // Additional fields mapping based on UnifiedTaskOutput structure }; } } diff --git a/packages/api/src/crm/task/services/hubspot/index.ts b/packages/api/src/crm/task/services/hubspot/index.ts index a1223aaa2..b08c28947 100644 --- a/packages/api/src/crm/task/services/hubspot/index.ts +++ b/packages/api/src/crm/task/services/hubspot/index.ts @@ -9,7 +9,7 @@ import { import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class HubspotService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', @@ -109,7 +109,7 @@ export class HubspotService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/task/services/pipedrive/index.ts b/packages/api/src/crm/task/services/pipedrive/index.ts index c67e53de8..19a5809f9 100644 --- a/packages/api/src/crm/task/services/pipedrive/index.ts +++ b/packages/api/src/crm/task/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveTaskInput, PipedriveTaskOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class PipedriveService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', @@ -62,7 +62,6 @@ export class PipedriveService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -94,7 +93,7 @@ export class PipedriveService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/task/services/task.service.ts b/packages/api/src/crm/task/services/task.service.ts index a28c05923..f1657d2a2 100644 --- a/packages/api/src/crm/task/services/task.service.ts +++ b/packages/api/src/crm/task/services/task.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedTaskInput, UnifiedTaskOutput } from '../types/model.unified'; import { desunify } from '@@core/utils/unification/desunify'; @@ -13,6 +12,7 @@ import { ServiceRegistry } from './registry.service'; import { OriginalTaskOutput } from '@@core/utils/types/original/original.crm'; import { unify } from '@@core/utils/unification/unify'; import { ITaskService } from '../types'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class TaskService { @@ -46,7 +46,13 @@ export class TaskService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_TASKS_ERROR', + message: 'TaskService.batchAddTasks() call failed', + cause: error, + }), + ); } } @@ -64,7 +70,7 @@ export class TaskService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const company = unifiedTaskData.company_id; //check if contact_id and account_id refer to real uuids @@ -75,7 +81,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a company_id which does not exist'); + throw new ReferenceError( + 'You inserted a company_id which does not exist', + ); } const user = unifiedTaskData.user_id; //check if contact_id and account_id refer to real uuids @@ -86,7 +94,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const deal = unifiedTaskData.deal_id; @@ -98,7 +108,9 @@ export class TaskService { }, }); if (!search) - throw new Error('You inserted a deal_id which does not exist'); + throw new ReferenceError( + 'You inserted a deal_id which does not exist', + ); } //desunify the data according to the target obj wanted @@ -263,7 +275,13 @@ export class TaskService { ); return result_task; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'CREATE_TASK_ERROR', + message: 'TaskService.addTask() call failed', + cause: error, + }), + ); } } @@ -334,7 +352,13 @@ export class TaskService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_TASK_ERROR', + message: 'TaskService.getTask() call failed', + cause: error, + }), + ); } } @@ -343,8 +367,12 @@ export class TaskService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedTaskOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedTaskOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { let prev_cursor = null; let next_cursor = null; @@ -354,21 +382,23 @@ export class TaskService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_task: cursor - } + id_crm_task: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let tasks = await this.prisma.crm_tasks.findMany({ + const tasks = await this.prisma.crm_tasks.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_task: cursor - } : undefined, + cursor: cursor + ? { + id_crm_task: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -376,8 +406,10 @@ export class TaskService { }, }); - if (tasks.length === (pageSize + 1)) { - next_cursor = Buffer.from(tasks[tasks.length - 1].id_crm_task).toString('base64'); + if (tasks.length === pageSize + 1) { + next_cursor = Buffer.from(tasks[tasks.length - 1].id_crm_task).toString( + 'base64', + ); tasks.pop(); } @@ -460,10 +492,16 @@ export class TaskService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_TASKS_ERROR', + message: 'TaskService.getTasks() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/crm/task/services/zendesk/index.ts b/packages/api/src/crm/task/services/zendesk/index.ts index 915074e52..64fbb6b25 100644 --- a/packages/api/src/crm/task/services/zendesk/index.ts +++ b/packages/api/src/crm/task/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskTaskInput, ZendeskTaskOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -60,7 +60,7 @@ export class ZendeskService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', @@ -68,7 +68,6 @@ export class ZendeskService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -101,7 +100,7 @@ export class ZendeskService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/task/services/zoho/index.ts b/packages/api/src/crm/task/services/zoho/index.ts index 1a20399a1..d6fe98c98 100644 --- a/packages/api/src/crm/task/services/zoho/index.ts +++ b/packages/api/src/crm/task/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoTaskInput, ZohoTaskOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class ZohoService implements ITaskService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', @@ -63,7 +63,6 @@ export class ZohoService implements ITaskService { ActionType.POST, ); } - return; } async syncTasks( @@ -98,7 +97,7 @@ export class ZohoService implements ITaskService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/task/sync/sync.service.ts b/packages/api/src/crm/task/sync/sync.service.ts index 01d11fea9..25b4cc023 100644 --- a/packages/api/src/crm/task/sync/sync.service.ts +++ b/packages/api/src/crm/task/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalTaskOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing tasks....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_TASK_SYNC_ERROR', + message: 'SyncService.syncTasks() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tasks syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +203,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +220,7 @@ export class SyncService implements OnModuleInit { const originId = task.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTask = await this.prisma.crm_tasks.findFirst({ @@ -365,7 +372,7 @@ export class SyncService implements OnModuleInit { } return tasks_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/crm/user/services/close/index.ts b/packages/api/src/crm/user/services/close/index.ts index d9ad8981b..727c7a2d5 100644 --- a/packages/api/src/crm/user/services/close/index.ts +++ b/packages/api/src/crm/user/services/close/index.ts @@ -5,7 +5,7 @@ import { CloseUserOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -55,7 +55,7 @@ export class CloseService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Close', diff --git a/packages/api/src/crm/user/services/hubspot/index.ts b/packages/api/src/crm/user/services/hubspot/index.ts index e99f6d9d4..a9f04624c 100644 --- a/packages/api/src/crm/user/services/hubspot/index.ts +++ b/packages/api/src/crm/user/services/hubspot/index.ts @@ -5,7 +5,7 @@ import { HubspotUserOutput, commonUserHubspotProperties } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -64,7 +64,7 @@ export class HubspotService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Hubspot', diff --git a/packages/api/src/crm/user/services/pipedrive/index.ts b/packages/api/src/crm/user/services/pipedrive/index.ts index a3f0eaa76..7c2daa98f 100644 --- a/packages/api/src/crm/user/services/pipedrive/index.ts +++ b/packages/api/src/crm/user/services/pipedrive/index.ts @@ -5,7 +5,7 @@ import { PipedriveUserOutput } from './types'; import axios from 'axios'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -50,7 +50,7 @@ export class PipedriveService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Pipedrive', diff --git a/packages/api/src/crm/user/services/user.service.ts b/packages/api/src/crm/user/services/user.service.ts index 38f47183a..a8b0432f3 100644 --- a/packages/api/src/crm/user/services/user.service.ts +++ b/packages/api/src/crm/user/services/user.service.ts @@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedUserOutput } from '../types/model.unified'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedCrmError } from '@@core/utils/errors'; @Injectable() export class UserService { @@ -82,7 +82,13 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_USER_ERROR', + message: 'UserService.getUser() call failed', + cause: error, + }), + ); } } @@ -91,10 +97,13 @@ export class UserService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedUserOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedUserOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { - let prev_cursor = null; let next_cursor = null; @@ -103,21 +112,23 @@ export class UserService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_crm_user: cursor - } + id_crm_user: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let users = await this.prisma.crm_users.findMany({ + const users = await this.prisma.crm_users.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_crm_user: cursor - } : undefined, + cursor: cursor + ? { + id_crm_user: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -125,8 +136,10 @@ export class UserService { }, }); - if (users.length === (pageSize + 1)) { - next_cursor = Buffer.from(users[users.length - 1].id_crm_user).toString('base64'); + if (users.length === pageSize + 1) { + next_cursor = Buffer.from(users[users.length - 1].id_crm_user).toString( + 'base64', + ); users.pop(); } @@ -204,10 +217,16 @@ export class UserService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedCrmError({ + name: 'GET_USERS_ERROR', + message: 'UserService.getUsers() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/crm/user/services/zendesk/index.ts b/packages/api/src/crm/user/services/zendesk/index.ts index f9ad2d85f..e0afe1d00 100644 --- a/packages/api/src/crm/user/services/zendesk/index.ts +++ b/packages/api/src/crm/user/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { ZendeskUserOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -53,7 +53,7 @@ export class ZendeskService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zendesk', diff --git a/packages/api/src/crm/user/services/zoho/index.ts b/packages/api/src/crm/user/services/zoho/index.ts index de3c52ac1..430eaeecf 100644 --- a/packages/api/src/crm/user/services/zoho/index.ts +++ b/packages/api/src/crm/user/services/zoho/index.ts @@ -5,7 +5,7 @@ import { ZohoUserOutput } from './types'; import axios from 'axios'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import { ServiceRegistry } from '../registry.service'; @@ -54,7 +54,7 @@ export class ZohoService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, 'Zoho', diff --git a/packages/api/src/crm/user/sync/sync.service.ts b/packages/api/src/crm/user/sync/sync.service.ts index a4917c417..8dc1fdc87 100644 --- a/packages/api/src/crm/user/sync/sync.service.ts +++ b/packages/api/src/crm/user/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalUserOutput } from '@@core/utils/types/original/original.crm'; import { CRM_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing users....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -101,18 +101,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'CRM_USER_SYNC_ERROR', + message: 'SyncService.syncUsers() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -138,7 +145,7 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; + } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +203,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +220,7 @@ export class SyncService implements OnModuleInit { const originId = user.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingUser = await this.prisma.crm_users.findFirst({ @@ -331,7 +338,7 @@ export class SyncService implements OnModuleInit { } return users_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/@lib/@utils/index.ts b/packages/api/src/ticketing/@lib/@utils/index.ts index 625e4f968..ec18ed174 100644 --- a/packages/api/src/ticketing/@lib/@utils/index.ts +++ b/packages/api/src/ticketing/@lib/@utils/index.ts @@ -6,9 +6,6 @@ export class Utils { constructor() { this.prisma = new PrismaClient(); - /*this.cryptoService = new EncryptionService( - new EnvironmentService(new ConfigService()), - );*/ } async fetchFileStreamFromURL(file_url: string) { @@ -23,10 +20,10 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) throw ReferenceError('Tcg_User Undefined'); return res.id_tcg_user; } catch (error) { - throw new Error(error); + throw error; } } @@ -37,11 +34,10 @@ export class Utils { id_tcg_user: uuid, }, }); - // if (!res) throw new Error(`tcg_user not found for uuid ${uuid}`); - if (!res) return; + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -53,10 +49,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_account not found for remote_id ${remote_id}`, + ); return res.id_tcg_contact; } catch (error) { - throw new Error(error); + throw error; } } @@ -67,11 +66,11 @@ export class Utils { id_tcg_contact: uuid, }, }); - // if (!res) throw new Error(`tcg_contact not found for uuid ${uuid}`); - if (!res) return; + if (!res) + throw new ReferenceError(`tcg_contact not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -82,13 +81,10 @@ export class Utils { id_tcg_user: uuid, }, }); - if (!res) return; - /*throw new Error( - `tcg_user not found for uuid ${uuid} and integration ${remote_platform}`, - );*/ + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -99,11 +95,10 @@ export class Utils { id_tcg_user: uuid, }, }); - // if (!res) throw new Error(`tcg_user not found for uuid ${uuid}`); - if (!res) return; + if (!res) throw new ReferenceError(`tcg_user not found for uuid ${uuid}`); return res.email_address; } catch (error) { - throw new Error(error); + throw error; } } @@ -118,10 +113,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_collection not found for remote_id ${remote_id}`, + ); return res.id_tcg_collection; } catch (error) { - throw new Error(error); + throw error; } } @@ -132,10 +130,11 @@ export class Utils { id_tcg_collection: uuid, }, }); - if (!res) return; + if (!res) + throw new ReferenceError(`tcg_collection not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } @@ -147,10 +146,13 @@ export class Utils { remote_platform: remote_platform, }, }); - if (!res) return; + if (!res) + throw new ReferenceError( + `tcg_ticket not found for remote_id ${remote_id}`, + ); return res.id_tcg_ticket; } catch (error) { - throw new Error(error); + throw error; } } @@ -161,11 +163,11 @@ export class Utils { id_tcg_ticket: uuid, }, }); - // if (!res) throw new Error(`tcg_contact not found for uuid ${uuid}`); - if (!res) return; + if (!res) + throw new ReferenceError(`tcg_contact not found for uuid ${uuid}`); return res.remote_id; } catch (error) { - throw new Error(error); + throw error; } } } diff --git a/packages/api/src/ticketing/@webhook/zendesk/handler.ts b/packages/api/src/ticketing/@webhook/zendesk/handler.ts index 2b49f5bb0..4661e1818 100644 --- a/packages/api/src/ticketing/@webhook/zendesk/handler.ts +++ b/packages/api/src/ticketing/@webhook/zendesk/handler.ts @@ -7,6 +7,13 @@ import axios from 'axios'; import { Payload } from './types'; import { mapToRemoteEvent } from './utils'; import * as crypto from 'crypto'; +import { + Action, + ActionType, + handle3rdPartyServiceError, + ManagedWebhooksError, + throwTypedError, +} from '@@core/utils/errors'; @Injectable() export class ZendeskHandlerService { @@ -20,220 +27,250 @@ export class ZendeskHandlerService { } async createWebhook(data: { [key: string]: any }, mw_ids: string[]) { - if (mw_ids[0]) { - await this.createBasicWebhook(data.name_basic, mw_ids[0]); - } - if (mw_ids[1]) { - await this.createTriggerWebhook(data.name_trigger, mw_ids[1]); + try { + if (mw_ids[0]) { + await this.createBasicWebhook(data.name_basic, mw_ids[0]); + } + if (mw_ids[1]) { + await this.createTriggerWebhook(data.name_trigger, mw_ids[1]); + } + } catch (error) { + throw error; } } async createBasicWebhook(webhook_name: string, mw_id: string) { - const mw = await this.prisma.managed_webhooks.findUnique({ - where: { - id_managed_webhook: mw_id, - }, - }); - const conn = await this.prisma.connections.findUnique({ - where: { - id_connection: mw.id_connection, - }, - }); - const unified_events = mw.active_events; + try { + const mw = await this.prisma.managed_webhooks.findUnique({ + where: { + id_managed_webhook: mw_id, + }, + }); + if (!mw) throw ReferenceError('Managed Webhook undefined'); + const conn = await this.prisma.connections.findUnique({ + where: { + id_connection: mw.id_connection, + }, + }); + if (!conn) throw ReferenceError('Connection undefined'); + const unified_events = mw.active_events; - const events_ = unified_events - .flatMap((event) => mapToRemoteEvent(event)) - .filter((item) => item !== null && item !== undefined); + const events_ = unified_events + .flatMap((event) => mapToRemoteEvent(event)) + .filter((item) => item !== null && item !== undefined); - const body_data = { - webhook: { - name: webhook_name, - status: 'active', - endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, - http_method: 'POST', - request_format: 'json', - subscriptions: events_, - }, - }; + const body_data = { + webhook: { + name: webhook_name, + status: 'active', + endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, + http_method: 'POST', + request_format: 'json', + subscriptions: events_, + }, + }; - this.logger.log('Creating basic webhook... '); + this.logger.log('Creating basic webhook... '); - const resp = await axios.post( - `${conn.account_url}/webhooks`, - JSON.stringify(body_data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const resp = await axios.post( + `${conn.account_url}/webhooks`, + JSON.stringify(body_data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); + ); - this.logger.log( - 'Zendesk basic webhook created ' + JSON.stringify(resp.data), - ); + this.logger.log( + 'Zendesk basic webhook created ' + JSON.stringify(resp.data), + ); - this.logger.log('Fetching basic webhook secret... '); + this.logger.log('Fetching basic webhook secret... '); - const webhook_result = await axios.get( - `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const webhook_result = await axios.get( + `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, + }, + ); + //update signing secret inside mw table + await this.prisma.managed_webhooks.update({ + where: { + id_managed_webhook: mw.id_managed_webhook, }, - }, - ); - //update signing secret inside mw table - await this.prisma.managed_webhooks.update({ - where: { - id_managed_webhook: mw.id_managed_webhook, - }, - data: { - remote_signing_secret: webhook_result.data.signing_secret.secret, - }, - }); + data: { + remote_signing_secret: webhook_result.data.signing_secret.secret, + }, + }); + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'zendesk', + Action.webhookCreation, + ActionType.POST, + ); + } } async createTriggerWebhook(webhook_name: string, mw_id: string) { - const mw = await this.prisma.managed_webhooks.findUnique({ - where: { - id_managed_webhook: mw_id, - }, - }); - const conn = await this.prisma.connections.findUnique({ - where: { - id_connection: mw.id_connection, - }, - }); - const body_data = { - webhook: { - name: webhook_name, - status: 'active', - endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, - http_method: 'POST', - request_format: 'json', - subscriptions: ['conditional_ticket_events'], - }, - }; + try { + const mw = await this.prisma.managed_webhooks.findUnique({ + where: { + id_managed_webhook: mw_id, + }, + }); + if (!mw) throw ReferenceError('Managed Webhook undefined'); - this.logger.log('Creating trigger webhook... '); - const resp = await axios.post( - `${conn.account_url}/webhooks`, - JSON.stringify(body_data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + const conn = await this.prisma.connections.findUnique({ + where: { + id_connection: mw.id_connection, }, - }, - ); + }); + if (!conn) throw ReferenceError('Connection undefined'); - this.logger.log( - 'Zendesk trigger webhook created ' + JSON.stringify(resp.data), - ); + const body_data = { + webhook: { + name: webhook_name, + status: 'active', + endpoint: `${this.env.getPanoraBaseUrl()}/mw/${mw.endpoint}`, + http_method: 'POST', + request_format: 'json', + subscriptions: ['conditional_ticket_events'], + }, + }; - // create trigger webhook - const b_ = { - trigger: { - actions: [ - { - field: 'notification_webhook', - value: [ - resp.data.webhook.id, - ` - { - "id_ticket": "{{ticket.id}}" - } - `, - ], + this.logger.log('Creating trigger webhook... '); + const resp = await axios.post( + `${conn.account_url}/webhooks`, + JSON.stringify(body_data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, }, - ], - conditions: { - any: [ - { - field: 'assignee_id', - operator: 'changed', - }, - { - field: 'attachment', - operator: 'is', - value: 'present', - }, - { - field: 'comment_is_public', - value: 'true', - }, - { - field: 'priority', - operator: 'changed', - }, - { - field: 'status', - value: 'changed', - }, - { - field: 'update_type', - value: 'Create', - }, - { - field: 'update_type', - value: 'Change', - }, - { - field: 'cc', - operator: 'is', - value: 'present', - }, + }, + ); + + this.logger.log( + 'Zendesk trigger webhook created ' + JSON.stringify(resp.data), + ); + + // create trigger webhook + const b_ = { + trigger: { + actions: [ { - field: 'type', - operator: 'changed', + field: 'notification_webhook', + value: [ + resp.data.webhook.id, + ` + { + "id_ticket": "{{ticket.id}}" + } + `, + ], }, ], + conditions: { + any: [ + { + field: 'assignee_id', + operator: 'changed', + }, + { + field: 'attachment', + operator: 'is', + value: 'present', + }, + { + field: 'comment_is_public', + value: 'true', + }, + { + field: 'priority', + operator: 'changed', + }, + { + field: 'status', + value: 'changed', + }, + { + field: 'update_type', + value: 'Create', + }, + { + field: 'update_type', + value: 'Change', + }, + { + field: 'cc', + operator: 'is', + value: 'present', + }, + { + field: 'type', + operator: 'changed', + }, + ], + }, + title: 'Trigger Webhooks', }, - title: 'Trigger Webhooks', - }, - }; - const trigger_result = await axios.post( - `${conn.account_url}/triggers.json`, - JSON.stringify(b_), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + }; + const trigger_result = await axios.post( + `${conn.account_url}/triggers.json`, + JSON.stringify(b_), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); + ); - this.logger.log('Fetching trigger webhook secret... '); - const webhook_result = await axios.get( - `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - conn.access_token, - )}`, + this.logger.log('Fetching trigger webhook secret... '); + const webhook_result = await axios.get( + `${conn.account_url}/webhooks/${resp.data.webhook.id}/signing_secret`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + conn.access_token, + )}`, + }, }, - }, - ); - //update signing secret inside mw table - await this.prisma.managed_webhooks.update({ - where: { - id_managed_webhook: mw.id_managed_webhook, - }, - data: { - remote_signing_secret: webhook_result.data.signing_secret.secret, - }, - }); + ); + //update signing secret inside mw table + await this.prisma.managed_webhooks.update({ + where: { + id_managed_webhook: mw.id_managed_webhook, + }, + data: { + remote_signing_secret: webhook_result.data.signing_secret.secret, + }, + }); + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'zendesk', + Action.webhookCreation, + ActionType.POST, + ); + } } async handler(payload: Payload, headers: any, id_managed_webhook: string) { @@ -251,7 +288,14 @@ export class ZendeskHandlerService { //non-ticket payload } } catch (error) { - throw new Error(error); + throwTypedError( + new ManagedWebhooksError({ + name: 'RECEIVING_WEBHOOK_PAYLOAD_ERROR', + message: 'ZendeskHandlerService.handler() call failed', + cause: error, + }), + this.logger, + ); } } @@ -275,7 +319,15 @@ export class ZendeskHandlerService { const sig = hmac.update(timestamp + body).digest('base64'); return Buffer.compare(Buffer.from(signature), Buffer.from(sig)) === 0; } catch (error) { - throw new Error(error); + throwTypedError( + new ManagedWebhooksError({ + name: 'SIGNATURE_VERIFICATION_AUTHENTICITY_ERROR', + message: + 'ZendeskHandlerService.verifyWebhookAuthenticity() call failed', + cause: error, + }), + this.logger, + ); } } } diff --git a/packages/api/src/ticketing/account/services/account.service.ts b/packages/api/src/ticketing/account/services/account.service.ts index b9cedab96..b738e96ea 100644 --- a/packages/api/src/ticketing/account/services/account.service.ts +++ b/packages/api/src/ticketing/account/services/account.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { UnifiedAccountOutput } from '../types/model.unified'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; @Injectable() export class AccountService { @@ -72,7 +72,13 @@ export class AccountService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ACCOUNT_ERROR', + message: 'AccountService.getAccount() call failed', + cause: error, + }), + ); } } @@ -81,8 +87,12 @@ export class AccountService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedAccountOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedAccountOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced @@ -94,21 +104,23 @@ export class AccountService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_account: cursor - } + id_tcg_account: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let accounts = await this.prisma.tcg_accounts.findMany({ + const accounts = await this.prisma.tcg_accounts.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_account: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_account: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -116,8 +128,10 @@ export class AccountService { }, }); - if (accounts.length === (pageSize + 1)) { - next_cursor = Buffer.from(accounts[accounts.length - 1].id_tcg_account).toString('base64'); + if (accounts.length === pageSize + 1) { + next_cursor = Buffer.from( + accounts[accounts.length - 1].id_tcg_account, + ).toString('base64'); accounts.pop(); } @@ -194,10 +208,16 @@ export class AccountService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ACCOUNTS_ERROR', + message: 'AccountService.getAccounts() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/account/services/front/index.ts b/packages/api/src/ticketing/account/services/front/index.ts index 4295f41f5..9d84fa9ed 100644 --- a/packages/api/src/ticketing/account/services/front/index.ts +++ b/packages/api/src/ticketing/account/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IAccountService } from '@ticketing/account/types'; import { FrontAccountOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements IAccountService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.account, ActionType.GET, ); diff --git a/packages/api/src/ticketing/account/services/zendesk/index.ts b/packages/api/src/ticketing/account/services/zendesk/index.ts index 895ffca17..97e285bba 100644 --- a/packages/api/src/ticketing/account/services/zendesk/index.ts +++ b/packages/api/src/ticketing/account/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IAccountService } from '@ticketing/account/types'; @@ -58,10 +58,10 @@ export class ZendeskService implements IAccountService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.account, ActionType.GET, ); diff --git a/packages/api/src/ticketing/account/sync/sync.service.ts b/packages/api/src/ticketing/account/sync/sync.service.ts index d97246851..4f6b95ae8 100644 --- a/packages/api/src/ticketing/account/sync/sync.service.ts +++ b/packages/api/src/ticketing/account/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing accounts....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_ACCOUNT_SYNC_ERROR', + message: 'SyncService.syncAccounts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping accounts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name @@ -194,7 +200,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -211,7 +217,7 @@ export class SyncService implements OnModuleInit { const originId = account.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingAccount = await this.prisma.tcg_accounts.findFirst({ @@ -245,7 +251,7 @@ export class SyncService implements OnModuleInit { id_tcg_account: uuidv4(), name: account.name, domains: account.domains, - // created_at: new Date(), + created_at: new Date(), modified_at: new Date(), id_linked_user: linkedUserId, remote_id: originId, @@ -317,7 +323,7 @@ export class SyncService implements OnModuleInit { } return accounts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/attachment/services/attachment.service.ts b/packages/api/src/ticketing/attachment/services/attachment.service.ts index 8736e2fbc..c82303934 100644 --- a/packages/api/src/ticketing/attachment/services/attachment.service.ts +++ b/packages/api/src/ticketing/attachment/services/attachment.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedAttachmentInput, @@ -40,7 +40,13 @@ export class AttachmentService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_ATTACHMENTS_ERROR', + message: 'AttachmentService.batchAddAttachments() call failed', + cause: error, + }), + ); } } @@ -56,7 +62,7 @@ export class AttachmentService { id_linked_user: linkedUserId, }, }); - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); //EXCEPTION: for Attachments we directly store them inside our db (no raw call to the provider) //the actual job to retrieve the attachment info would be done inside /comments @@ -132,7 +138,13 @@ export class AttachmentService { ); return result_attachment; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_ATTACHMENT_ERROR', + message: 'AttachmentService.addAttachment() call failed', + cause: error, + }), + ); } } @@ -198,7 +210,13 @@ export class AttachmentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ATTACHMENT_ERROR', + message: 'AttachmentService.getAttachment() call failed', + cause: error, + }), + ); } } @@ -207,8 +225,12 @@ export class AttachmentService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedAttachmentOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedAttachmentOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced let prev_cursor = null; @@ -219,20 +241,22 @@ export class AttachmentService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_attachment: cursor - } + id_tcg_attachment: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let attachments = await this.prisma.tcg_attachments.findMany({ + const attachments = await this.prisma.tcg_attachments.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_attachment: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_attachment: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -240,8 +264,10 @@ export class AttachmentService { }, }); - if (attachments.length === (pageSize + 1)) { - next_cursor = Buffer.from(attachments[attachments.length - 1].id_tcg_attachment).toString('base64'); + if (attachments.length === pageSize + 1) { + next_cursor = Buffer.from( + attachments[attachments.length - 1].id_tcg_attachment, + ).toString('base64'); attachments.pop(); } @@ -321,10 +347,16 @@ export class AttachmentService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_ATTACHMENTS_ERROR', + message: 'AttachmentService.getAttachments() call failed', + cause: error, + }), + ); } } diff --git a/packages/api/src/ticketing/attachment/services/registry.service.ts b/packages/api/src/ticketing/attachment/services/registry.service.ts index 8a9f8f360..56484d3bd 100644 --- a/packages/api/src/ticketing/attachment/services/registry.service.ts +++ b/packages/api/src/ticketing/attachment/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IAttachmentService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/collection/services/collection.service.ts b/packages/api/src/ticketing/collection/services/collection.service.ts index 243ec0f97..7256361bd 100644 --- a/packages/api/src/ticketing/collection/services/collection.service.ts +++ b/packages/api/src/ticketing/collection/services/collection.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCollectionOutput } from '../types/model.unified'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; @@ -58,7 +58,13 @@ export class CollectionService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COLLECTION_ERROR', + message: 'CollectionService.getCollection() call failed', + cause: error, + }), + ); } } @@ -67,10 +73,13 @@ export class CollectionService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedCollectionOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedCollectionOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { - let prev_cursor = null; let next_cursor = null; @@ -79,21 +88,23 @@ export class CollectionService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_collection: cursor - } + id_tcg_collection: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let collections = await this.prisma.tcg_collections.findMany({ + const collections = await this.prisma.tcg_collections.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_collection: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_collection: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -101,8 +112,10 @@ export class CollectionService { }, }); - if (collections.length === (pageSize + 1)) { - next_cursor = Buffer.from(collections[collections.length - 1].id_tcg_collection).toString('base64'); + if (collections.length === pageSize + 1) { + next_cursor = Buffer.from( + collections[collections.length - 1].id_tcg_collection, + ).toString('base64'); collections.pop(); } @@ -155,10 +168,16 @@ export class CollectionService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COLLECTIONS_ERROR', + message: 'CollectionService.getCollections() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/collection/services/gitlab/index.ts b/packages/api/src/ticketing/collection/services/gitlab/index.ts index 492093cf9..e397f0b7c 100644 --- a/packages/api/src/ticketing/collection/services/gitlab/index.ts +++ b/packages/api/src/ticketing/collection/services/gitlab/index.ts @@ -5,86 +5,85 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ICollectionService } from '@ticketing/collection/types'; import { GitlabCollectionInput, GitlabCollectionOutput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; @Injectable() export class GitlabService implements ICollectionService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.collection.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } - - async syncCollections( - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.collection.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } - // It fetches all project from gitlab - // const resp = await axios.get(`${connection.account_url}/projects`, { - // headers: { - // 'Content-Type': 'application/json', - // Authorization: `Bearer ${this.cryptoService.decrypt( - // connection.access_token, - // )}`, - // }, - // }); + async syncCollections( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const currentUser = await axios.get(`${connection.account_url}/user`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }) + // It fetches all project from gitlab + // const resp = await axios.get(`${connection.account_url}/projects`, { + // headers: { + // 'Content-Type': 'application/json', + // Authorization: `Bearer ${this.cryptoService.decrypt( + // connection.access_token, + // )}`, + // }, + // }); - const resp = await axios.get(`${connection.account_url}/users/${currentUser.data.id}/projects`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }) + const currentUser = await axios.get(`${connection.account_url}/user`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); - this.logger.log(`Synced gitlab collections !`); + const resp = await axios.get( + `${connection.account_url}/users/${currentUser.data.id}/projects`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); - // console.log("In index of gitlab", JSON.stringify(resp.data)) + this.logger.log(`Synced gitlab collections !`); + // console.log("In index of gitlab", JSON.stringify(resp.data)) - return { - data: resp.data, - message: 'Gitlab collections retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.collection, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'Gitlab collections retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.collection, + ActionType.GET, + ); } - - + } } diff --git a/packages/api/src/ticketing/collection/services/jira/index.ts b/packages/api/src/ticketing/collection/services/jira/index.ts index 7b09a4856..da356b9eb 100644 --- a/packages/api/src/ticketing/collection/services/jira/index.ts +++ b/packages/api/src/ticketing/collection/services/jira/index.ts @@ -5,11 +5,10 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ICollectionService } from '@ticketing/collection/types'; import { JiraCollectionOutput, JiraCollectionInput } from './types'; -import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; @Injectable() export class JiraService implements ICollectionService { @@ -53,15 +52,13 @@ export class JiraService implements ICollectionService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.collection, ActionType.GET, ); } } - - } diff --git a/packages/api/src/ticketing/collection/sync/sync.service.ts b/packages/api/src/ticketing/collection/sync/sync.service.ts index c591cfd25..cb1c4dd95 100644 --- a/packages/api/src/ticketing/collection/sync/sync.service.ts +++ b/packages/api/src/ticketing/collection/sync/sync.service.ts @@ -1,11 +1,10 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from '../services/registry.service'; import { unify } from '@@core/utils/unification/unify'; import { TicketingObject } from '@ticketing/@lib/@types'; @@ -34,7 +33,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -66,12 +65,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing collections....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -98,18 +97,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_COLLECTION_SYNC_ERROR', + message: 'SyncService.syncCollections() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -135,7 +141,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping collections syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } const service: ICollectionService = @@ -183,7 +188,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -195,13 +200,13 @@ export class SyncService implements OnModuleInit { ): Promise { try { let collections_results: TicketingCollection[] = []; - console.log(collections) + console.log(collections); for (let i = 0; i < collections.length; i++) { const collection = collections[i]; const originId = collection.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTeam = await this.prisma.tcg_collections.findFirst({ @@ -237,7 +242,7 @@ export class SyncService implements OnModuleInit { name: collection.name, description: collection.description, collection_type: collection.collection_type, - // created_at: new Date(), + created_at: new Date(), modified_at: new Date(), id_linked_user: linkedUserId, remote_id: originId, @@ -270,7 +275,7 @@ export class SyncService implements OnModuleInit { } return collections_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/comment/services/comment.service.ts b/packages/api/src/ticketing/comment/services/comment.service.ts index fd34b2f5e..455d0207f 100644 --- a/packages/api/src/ticketing/comment/services/comment.service.ts +++ b/packages/api/src/ticketing/comment/services/comment.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedCommentInput, @@ -47,7 +47,13 @@ export class CommentService { return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_COMMENTS_ERROR', + message: 'CommentService.addComments() call failed', + cause: error, + }), + ); } } @@ -65,7 +71,7 @@ export class CommentService { }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const tick = unifiedCommentData.ticket_id; //check if contact_id and account_id refer to real uuids if (tick) { @@ -75,9 +81,11 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a ticket_id which does not exist'); + throw new ReferenceError( + 'You inserted a ticket_id which does not exist', + ); } else { - throw new Error( + throw new ReferenceError( 'You must attach your comment to a ticket, specify a ticket_id', ); } @@ -91,7 +99,9 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const user = unifiedCommentData.user_id; //check if contact_id and account_id refer to real uuids @@ -102,7 +112,9 @@ export class CommentService { }, }); if (!search) - throw new Error('You inserted a user_id which does not exist'); + throw new ReferenceError( + 'You inserted a user_id which does not exist', + ); } const attachmts = unifiedCommentData.attachments; @@ -115,7 +127,7 @@ export class CommentService { }, }); if (!search) - throw new Error( + throw new ReferenceError( 'You inserted an attachment_id which does not exist', ); }); @@ -142,7 +154,7 @@ export class CommentService { }, }); if (!ticket) - throw new Error( + throw new ReferenceError( 'ticket does not exist for the comment you try to create', ); const resp: ApiResponse = await service.addComment( @@ -176,13 +188,13 @@ export class CommentService { const opts = target_comment.creator_type === 'contact' ? { - id_tcg_contact: unifiedCommentData.contact_id, - } + id_tcg_contact: unifiedCommentData.contact_id, + } : target_comment.creator_type === 'user' - ? { + ? { id_tcg_user: unifiedCommentData.user_id, } - : {}; //case where nothing is passed for creator or a not authorized value; + : {}; //case where nothing is passed for creator or a not authorized value; if (existingComment) { // Update the existing comment @@ -290,7 +302,13 @@ export class CommentService { ); return result_comment; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_COMMENT_ERROR', + message: 'CommentService.addComment() call failed', + cause: error, + }), + ); } } @@ -364,7 +382,13 @@ export class CommentService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COMMENT_ERROR', + message: 'CommentService.getComment() call failed', + cause: error, + }), + ); } } @@ -375,8 +399,12 @@ export class CommentService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedCommentOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedCommentOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { let prev_cursor = null; let next_cursor = null; @@ -386,21 +414,23 @@ export class CommentService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_comment: cursor - } + id_tcg_comment: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let comments = await this.prisma.tcg_comments.findMany({ + const comments = await this.prisma.tcg_comments.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_comment: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_comment: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -408,8 +438,10 @@ export class CommentService { }, }); - if (comments.length === (pageSize + 1)) { - next_cursor = Buffer.from(comments[comments.length - 1].id_tcg_comment).toString('base64'); + if (comments.length === pageSize + 1) { + next_cursor = Buffer.from( + comments[comments.length - 1].id_tcg_comment, + ).toString('base64'); comments.pop(); } @@ -492,10 +524,16 @@ export class CommentService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_COMMENTS_ERROR', + message: 'CommentService.getComments() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/comment/services/front/index.ts b/packages/api/src/ticketing/comment/services/front/index.ts index a04bb7d85..8ec9e507e 100644 --- a/packages/api/src/ticketing/comment/services/front/index.ts +++ b/packages/api/src/ticketing/comment/services/front/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { FrontCommentInput, FrontCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; @Injectable() export class FrontService implements ICommentService { @@ -62,7 +62,9 @@ export class FrontService implements ICommentService { }, }); if (!attachment) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // TODO: Construct the right binary attachment // Get the AWS S3 right file @@ -120,10 +122,10 @@ export class FrontService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.comment, ActionType.POST, ); @@ -170,10 +172,10 @@ export class FrontService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/gitlab/index.ts b/packages/api/src/ticketing/comment/services/gitlab/index.ts index e045cfb27..fc0914e74 100644 --- a/packages/api/src/ticketing/comment/services/gitlab/index.ts +++ b/packages/api/src/ticketing/comment/services/gitlab/index.ts @@ -4,199 +4,207 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { GitlabCommentInput, GitlabCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() export class GitlabService implements ICommentService { - private readonly utils: Utils; - - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.comment.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - this.utils = new Utils(); - } - - async addComment( - commentData: GitlabCommentInput, - linkedUserId: string, - remoteIdTicket: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - - let uploads = []; - const uuids = commentData.attachment as any[]; - if (uuids && uuids.length > 0) { - const attachmentPromises = uuids.map(async (uuid) => { - const res = await this.prisma.tcg_attachments.findUnique({ - where: { - id_tcg_attachment: uuid.extra, - }, - }); - if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); - } - // Assuming you want to construct the right binary attachment here - // For now, we'll just return the URL - const stats = fs.statSync(res.file_url); - return { - url: res.file_url, - name: res.file_name, - size: stats.size, - content_type: 'application/pdf', //todo - }; - }); - uploads = await Promise.all(attachmentPromises); - } - - // Assuming you want to modify the comment object here - // For now, we'll just add the uploads to the comment - const data = { - ...commentData, - attachments: uploads, - }; - - const ticket = await this.prisma.tcg_tickets.findFirst({ - where: { - remote_id: remoteIdTicket, - remote_platform: 'gitlab', - }, - select: { - collections: true, - id_tcg_ticket: true - } - }); - - // const ticket = await this.prisma.tcg_tickets.findUnique({ - // where: { - // id_tcg_ticket: remoteIdTicket, - // }, - // select: { - // collections: true - // }, - // }); - - const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid(ticket.collections[0]); - - // Retrieve the uuid of issue from remote_data - const remote_data = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: ticket.id_tcg_ticket, - }, - }); - const { iid } = JSON.parse(remote_data.data); - - const resp = await axios.post( - `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, - JSON.stringify(data), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - - return { - data: resp.data, - message: 'Gitlab comment created', - statusCode: 201, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.comment, - ActionType.POST, + private readonly utils: Utils; + + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.comment.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + this.utils = new Utils(); + } + + async addComment( + commentData: GitlabCommentInput, + linkedUserId: string, + remoteIdTicket: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + + let uploads = []; + const uuids = commentData.attachment as any[]; + if (uuids && uuids.length > 0) { + const attachmentPromises = uuids.map(async (uuid) => { + const res = await this.prisma.tcg_attachments.findUnique({ + where: { + id_tcg_attachment: uuid.extra, + }, + }); + if (!res) { + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, ); - } + } + // Assuming you want to construct the right binary attachment here + // For now, we'll just return the URL + const stats = fs.statSync(res.file_url); + return { + url: res.file_url, + name: res.file_name, + size: stats.size, + content_type: 'application/pdf', //todo + }; + }); + uploads = await Promise.all(attachmentPromises); + } + + // Assuming you want to modify the comment object here + // For now, we'll just add the uploads to the comment + const data = { + ...commentData, + attachments: uploads, + }; + + const ticket = await this.prisma.tcg_tickets.findFirst({ + where: { + remote_id: remoteIdTicket, + remote_platform: 'gitlab', + }, + select: { + collections: true, + id_tcg_ticket: true, + }, + }); + + // const ticket = await this.prisma.tcg_tickets.findUnique({ + // where: { + // id_tcg_ticket: remoteIdTicket, + // }, + // select: { + // collections: true + // }, + // }); + + const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid( + ticket.collections[0], + ); + + // Retrieve the uuid of issue from remote_data + const remote_data = await this.prisma.remote_data.findFirst({ + where: { + ressource_owner_id: ticket.id_tcg_ticket, + }, + }); + const { iid } = JSON.parse(remote_data.data); + + const resp = await axios.post( + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + JSON.stringify(data), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + return { + data: resp.data, + message: 'Gitlab comment created', + statusCode: 201, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.comment, + ActionType.POST, + ); } - async syncComments( - linkedUserId: string, - id_ticket: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - //retrieve ticket remote id so we can retrieve the comments in the original software - const ticket = await this.prisma.tcg_tickets.findUnique({ - where: { - id_tcg_ticket: id_ticket, - }, - select: { - remote_id: true, - collections: true - }, - }); - - // retrieve the remote_id of project from collections - const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid(ticket.collections[0]) - - // Retrieve the uuid of issue from remote_data - const remote_data = await this.prisma.remote_data.findFirst({ - where: { - ressource_owner_id: id_ticket, - }, - }); - const { iid } = JSON.parse(remote_data.data); - - console.log("Requested URL : ", `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`) - - - const resp = await axios.get( - `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - this.logger.log(`Synced gitlab comments !`); - console.log(resp.data) - - return { - data: resp.data, - message: 'Gitlab comments retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.comment, - ActionType.GET, - ); - } + } + async syncComments( + linkedUserId: string, + id_ticket: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + //retrieve ticket remote id so we can retrieve the comments in the original software + const ticket = await this.prisma.tcg_tickets.findUnique({ + where: { + id_tcg_ticket: id_ticket, + }, + select: { + remote_id: true, + collections: true, + }, + }); + + // retrieve the remote_id of project from collections + const remote_project_id = await this.utils.getCollectionRemoteIdFromUuid( + ticket.collections[0], + ); + + // Retrieve the uuid of issue from remote_data + const remote_data = await this.prisma.remote_data.findFirst({ + where: { + ressource_owner_id: id_ticket, + }, + }); + const { iid } = JSON.parse(remote_data.data); + + console.log( + 'Requested URL : ', + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + ); + + const resp = await axios.get( + `${connection.account_url}/projects/${remote_project_id}/issues/${iid}/notes`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced gitlab comments !`); + console.log(resp.data); + + return { + data: resp.data, + message: 'Gitlab comments retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.comment, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/comment/services/gorgias/index.ts b/packages/api/src/ticketing/comment/services/gorgias/index.ts index 7b19fb25e..6f71d8d13 100644 --- a/packages/api/src/ticketing/comment/services/gorgias/index.ts +++ b/packages/api/src/ticketing/comment/services/gorgias/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { GorgiasCommentInput, GorgiasCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() @@ -53,7 +53,9 @@ export class GorgiasService implements ICommentService { }, }); if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // Assuming you want to construct the right binary attachment here // For now, we'll just return the URL @@ -94,10 +96,10 @@ export class GorgiasService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.comment, ActionType.POST, ); @@ -144,10 +146,10 @@ export class GorgiasService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/jira/index.ts b/packages/api/src/ticketing/comment/services/jira/index.ts index a0d2ed1d1..6e5fc2db0 100644 --- a/packages/api/src/ticketing/comment/services/jira/index.ts +++ b/packages/api/src/ticketing/comment/services/jira/index.ts @@ -4,12 +4,12 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { JiraCommentInput, JiraCommentOutput } from './types'; import { ServiceRegistry } from '../registry.service'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; import * as fs from 'fs'; @Injectable() @@ -70,7 +70,9 @@ export class JiraService implements ICommentService { }, }); if (!attachment) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // TODO: Construct the right binary attachment // Get the AWS S3 right file @@ -112,10 +114,10 @@ export class JiraService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.comment, ActionType.POST, ); @@ -161,10 +163,10 @@ export class JiraService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/services/registry.service.ts b/packages/api/src/ticketing/comment/services/registry.service.ts index dc4d8d6f3..ca54192d7 100644 --- a/packages/api/src/ticketing/comment/services/registry.service.ts +++ b/packages/api/src/ticketing/comment/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ICommentService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/comment/services/zendesk/index.ts b/packages/api/src/ticketing/comment/services/zendesk/index.ts index aabcb472e..0a54d96d8 100644 --- a/packages/api/src/ticketing/comment/services/zendesk/index.ts +++ b/packages/api/src/ticketing/comment/services/zendesk/index.ts @@ -4,7 +4,7 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { EncryptionService } from '@@core/encryption/encryption.service'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ICommentService } from '@ticketing/comment/types'; import { TicketingObject } from '@ticketing/@lib/@types'; import { OriginalCommentOutput } from '@@core/utils/types/original/original.ticketing'; @@ -62,7 +62,9 @@ export class ZendeskService implements ICommentService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO:; fetch the right file from AWS s3 const s3File = ''; @@ -112,10 +114,10 @@ export class ZendeskService implements ICommentService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.comment, ActionType.POST, ); @@ -162,10 +164,10 @@ export class ZendeskService implements ICommentService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.comment, ActionType.GET, ); diff --git a/packages/api/src/ticketing/comment/sync/sync.service.ts b/packages/api/src/ticketing/comment/sync/sync.service.ts index a1ed3423c..10267c1a4 100644 --- a/packages/api/src/ticketing/comment/sync/sync.service.ts +++ b/packages/api/src/ticketing/comment/sync/sync.service.ts @@ -1,6 +1,6 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing comments....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -109,18 +109,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_COMMENT_SYNC_ERROR', + message: 'SyncService.syncComments() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -147,7 +154,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping comments syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -205,7 +211,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -223,7 +229,7 @@ export class SyncService implements OnModuleInit { const originId = comment.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingComment = await this.prisma.tcg_comments.findFirst({ @@ -236,15 +242,15 @@ export class SyncService implements OnModuleInit { let unique_ticketing_comment_id: string; const opts = - (comment.creator_type === 'CONTACT' && comment.contact_id) + comment.creator_type === 'CONTACT' && comment.contact_id + ? { + id_tcg_contact: comment.contact_id, + } + : comment.creator_type === 'USER' && comment.user_id ? { - id_tcg_contact: comment.contact_id, - } - : (comment.creator_type === 'USER' && comment.user_id) - ? { id_tcg_user: comment.user_id, } - : {}; + : {}; //case where nothing is passed for creator or a not authorized value; if (existingComment) { @@ -350,7 +356,7 @@ export class SyncService implements OnModuleInit { file_name: attchmt.file_name, file_url: attchmt.file_url, id_tcg_comment: unique_ticketing_comment_id, - // created_at: new Date(), + created_at: new Date(), modified_at: new Date(), uploader: linkedUserId, //TODO id_tcg_ticket: id_ticket, @@ -403,7 +409,7 @@ export class SyncService implements OnModuleInit { } return comments_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/contact/services/contact.service.ts b/packages/api/src/ticketing/contact/services/contact.service.ts index 907b3d415..20f3ef426 100644 --- a/packages/api/src/ticketing/contact/services/contact.service.ts +++ b/packages/api/src/ticketing/contact/services/contact.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedContactOutput } from '../types/model.unified'; @Injectable() @@ -74,7 +74,13 @@ export class ContactService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_CONTACT_ERROR', + message: 'ContactService.getContact() call failed', + cause: error, + }), + ); } } @@ -83,8 +89,12 @@ export class ContactService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedContactOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedContactOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced let prev_cursor = null; @@ -95,21 +105,23 @@ export class ContactService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_contact: cursor - } + id_tcg_contact: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let contacts = await this.prisma.tcg_contacts.findMany({ + const contacts = await this.prisma.tcg_contacts.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_contact: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_contact: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -117,8 +129,10 @@ export class ContactService { }, }); - if (contacts.length === (pageSize + 1)) { - next_cursor = Buffer.from(contacts[contacts.length - 1].id_tcg_contact).toString('base64'); + if (contacts.length === pageSize + 1) { + next_cursor = Buffer.from( + contacts[contacts.length - 1].id_tcg_contact, + ).toString('base64'); contacts.pop(); } @@ -198,10 +212,16 @@ export class ContactService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_CONTACTS_ERROR', + message: 'ContactService.getContacts() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/contact/services/front/index.ts b/packages/api/src/ticketing/contact/services/front/index.ts index 9b43dd9f7..928f2cc75 100644 --- a/packages/api/src/ticketing/contact/services/front/index.ts +++ b/packages/api/src/ticketing/contact/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; import { FrontContactOutput } from './types'; @@ -30,7 +30,8 @@ export class FrontService implements IContactService { remote_account_id: string, ): Promise> { try { - if (!remote_account_id) throw new Error('remote account id not found'); + if (!remote_account_id) + throw new ReferenceError('remote account id not found'); const connection = await this.prisma.connections.findFirst({ where: { @@ -59,10 +60,10 @@ export class FrontService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/services/gorgias/index.ts b/packages/api/src/ticketing/contact/services/gorgias/index.ts index b788a7d7a..07f92275c 100644 --- a/packages/api/src/ticketing/contact/services/gorgias/index.ts +++ b/packages/api/src/ticketing/contact/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; import { GorgiasContactOutput } from './types'; @@ -30,7 +30,8 @@ export class GorgiasService implements IContactService { remote_account_id: string, ): Promise> { try { - if (!remote_account_id) throw new Error('remote account id not found'); + if (!remote_account_id) + throw new ReferenceError('remote account id not found'); const connection = await this.prisma.connections.findFirst({ where: { @@ -56,10 +57,10 @@ export class GorgiasService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/services/registry.service.ts b/packages/api/src/ticketing/contact/services/registry.service.ts index be0d9c608..bc664c41c 100644 --- a/packages/api/src/ticketing/contact/services/registry.service.ts +++ b/packages/api/src/ticketing/contact/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): IContactService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/contact/services/zendesk/index.ts b/packages/api/src/ticketing/contact/services/zendesk/index.ts index 6881b74e0..14fa79542 100644 --- a/packages/api/src/ticketing/contact/services/zendesk/index.ts +++ b/packages/api/src/ticketing/contact/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IContactService } from '@ticketing/contact/types'; @@ -59,10 +59,10 @@ export class ZendeskService implements IContactService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.contact, ActionType.GET, ); diff --git a/packages/api/src/ticketing/contact/sync/sync.service.ts b/packages/api/src/ticketing/contact/sync/sync.service.ts index c2a9fbe71..66daf5c34 100644 --- a/packages/api/src/ticketing/contact/sync/sync.service.ts +++ b/packages/api/src/ticketing/contact/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing contacts....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -111,18 +111,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_CONTACT_SYNC_ERROR', + message: 'SyncService.syncContacts() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -149,7 +156,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping contacts syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -209,7 +215,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -227,7 +233,7 @@ export class SyncService implements OnModuleInit { const originId = contact.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingContact = await this.prisma.tcg_contacts.findFirst({ @@ -359,7 +365,7 @@ export class SyncService implements OnModuleInit { } return contacts_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/tag/services/front/index.ts b/packages/api/src/ticketing/tag/services/front/index.ts index 204a266a0..f7cd4d5c8 100644 --- a/packages/api/src/ticketing/tag/services/front/index.ts +++ b/packages/api/src/ticketing/tag/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { FrontTagOutput } from './types'; @@ -66,10 +66,10 @@ export class FrontService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/gorgias/index.ts b/packages/api/src/ticketing/tag/services/gorgias/index.ts index 8e476f405..fcccc8166 100644 --- a/packages/api/src/ticketing/tag/services/gorgias/index.ts +++ b/packages/api/src/ticketing/tag/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { GorgiasTagOutput } from './types'; @@ -66,10 +66,10 @@ export class GorgiasService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/jira/index.ts b/packages/api/src/ticketing/tag/services/jira/index.ts index 00c3ef3f5..7096dd994 100644 --- a/packages/api/src/ticketing/tag/services/jira/index.ts +++ b/packages/api/src/ticketing/tag/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; import { JiraTagOutput } from './types'; @@ -67,10 +67,10 @@ export class JiraService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/services/tag.service.ts b/packages/api/src/ticketing/tag/services/tag.service.ts index 70af5063d..0e26a6805 100644 --- a/packages/api/src/ticketing/tag/services/tag.service.ts +++ b/packages/api/src/ticketing/tag/services/tag.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedTagOutput } from '../types/model.unified'; @Injectable() @@ -71,7 +71,13 @@ export class TagService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TAG_ERROR', + message: 'TagService.getTag() call failed', + cause: error, + }), + ); } } @@ -80,8 +86,12 @@ export class TagService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedTagOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedTagOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced @@ -93,21 +103,23 @@ export class TagService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_tag: cursor - } + id_tcg_tag: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let tags = await this.prisma.tcg_tags.findMany({ + const tags = await this.prisma.tcg_tags.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_tag: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_tag: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -115,8 +127,10 @@ export class TagService { }, }); - if (tags.length === (pageSize + 1)) { - next_cursor = Buffer.from(tags[tags.length - 1].id_tcg_tag).toString('base64'); + if (tags.length === pageSize + 1) { + next_cursor = Buffer.from(tags[tags.length - 1].id_tcg_tag).toString( + 'base64', + ); tags.pop(); } @@ -193,10 +207,16 @@ export class TagService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TAGS_ERROR', + message: 'TagService.getTags() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/tag/services/zendesk/index.ts b/packages/api/src/ticketing/tag/services/zendesk/index.ts index 86c2d604b..7a5a5ab23 100644 --- a/packages/api/src/ticketing/tag/services/zendesk/index.ts +++ b/packages/api/src/ticketing/tag/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ITagService } from '@ticketing/tag/types'; @@ -67,10 +67,10 @@ export class ZendeskService implements ITagService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.tag, ActionType.GET, ); diff --git a/packages/api/src/ticketing/tag/sync/sync.service.ts b/packages/api/src/ticketing/tag/sync/sync.service.ts index 5fc9bc83a..37e1726f4 100644 --- a/packages/api/src/ticketing/tag/sync/sync.service.ts +++ b/packages/api/src/ticketing/tag/sync/sync.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing tags....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -109,18 +109,25 @@ export class SyncService implements OnModuleInit { ); } } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TAG_SYNC_ERROR', + message: 'SyncService.syncTags() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -147,7 +154,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tags syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -183,7 +189,6 @@ export class SyncService implements OnModuleInit { //TODO: exceptionally we use the unifiedObject as we might need to get the fake remote ids from Zendesk store in id field - //insert the data in the DB with the fieldMappings (value table) const tag_data = await this.saveTagsInDb( linkedUserId, @@ -212,7 +217,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -263,7 +268,7 @@ export class SyncService implements OnModuleInit { const data = { id_tcg_tag: uuidv4(), name: tag.name, - // created_at: new Date(), + created_at: new Date(), modified_at: new Date(), id_tcg_ticket: id_ticket, id_linked_user: linkedUserId, @@ -336,7 +341,7 @@ export class SyncService implements OnModuleInit { } return tags_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/tag/tag.controller.ts b/packages/api/src/ticketing/tag/tag.controller.ts index 6e133146c..705f1ddf5 100644 --- a/packages/api/src/ticketing/tag/tag.controller.ts +++ b/packages/api/src/ticketing/tag/tag.controller.ts @@ -66,7 +66,7 @@ export class TagController { linkedUserId, pageSize, remote_data, - cursor + cursor, ); } catch (error) { throw new Error(error); diff --git a/packages/api/src/ticketing/team/services/front/index.ts b/packages/api/src/ticketing/team/services/front/index.ts index d2e38be05..13583f0d1 100644 --- a/packages/api/src/ticketing/team/services/front/index.ts +++ b/packages/api/src/ticketing/team/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { FrontTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/gorgias/index.ts b/packages/api/src/ticketing/team/services/gorgias/index.ts index a33a1cd29..466c332d7 100644 --- a/packages/api/src/ticketing/team/services/gorgias/index.ts +++ b/packages/api/src/ticketing/team/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { GorgiasTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class GorgiasService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/jira/index.ts b/packages/api/src/ticketing/team/services/jira/index.ts index 1a0cad3c0..12aa7c627 100644 --- a/packages/api/src/ticketing/team/services/jira/index.ts +++ b/packages/api/src/ticketing/team/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; import { JiraTeamOutput } from './types'; @@ -52,10 +52,10 @@ export class JiraService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/services/team.service.ts b/packages/api/src/ticketing/team/services/team.service.ts index faefa11a5..36fa97cab 100644 --- a/packages/api/src/ticketing/team/services/team.service.ts +++ b/packages/api/src/ticketing/team/services/team.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedTeamOutput } from '../types/model.unified'; @Injectable() @@ -72,7 +72,13 @@ export class TeamService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TEAM_ERROR', + message: 'TeamService.getTeam() call failed', + cause: error, + }), + ); } } @@ -81,8 +87,12 @@ export class TeamService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedTeamOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedTeamOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced @@ -94,21 +104,23 @@ export class TeamService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_team: cursor - } + id_tcg_team: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let teams = await this.prisma.tcg_teams.findMany({ + const teams = await this.prisma.tcg_teams.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_team: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_team: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -116,8 +128,10 @@ export class TeamService { }, }); - if (teams.length === (pageSize + 1)) { - next_cursor = Buffer.from(teams[teams.length - 1].id_tcg_team).toString('base64'); + if (teams.length === pageSize + 1) { + next_cursor = Buffer.from(teams[teams.length - 1].id_tcg_team).toString( + 'base64', + ); teams.pop(); } @@ -195,10 +209,16 @@ export class TeamService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TEAMS_ERROR', + message: 'TeamService.getTeams() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/team/services/zendesk/index.ts b/packages/api/src/ticketing/team/services/zendesk/index.ts index 03d72febf..6bb731cb9 100644 --- a/packages/api/src/ticketing/team/services/zendesk/index.ts +++ b/packages/api/src/ticketing/team/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ITeamService } from '@ticketing/team/types'; @@ -55,10 +55,10 @@ export class ZendeskService implements ITeamService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.team, ActionType.GET, ); diff --git a/packages/api/src/ticketing/team/sync/sync.service.ts b/packages/api/src/ticketing/team/sync/sync.service.ts index cc82edac2..b76d1e977 100644 --- a/packages/api/src/ticketing/team/sync/sync.service.ts +++ b/packages/api/src/ticketing/team/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { OriginalTeamOutput } from '@@core/utils/types/original/original.ticketi import { TICKETING_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { throwTypedError, SyncError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -67,12 +67,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing teams....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TEAM_SYNC_ERROR', + message: 'SyncService.syncTeams() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping teams syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -196,7 +202,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -213,7 +219,7 @@ export class SyncService implements OnModuleInit { const originId = team.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTeam = await this.prisma.tcg_teams.findFirst({ @@ -247,7 +253,7 @@ export class SyncService implements OnModuleInit { id_tcg_team: uuidv4(), name: team.name, description: team.description, - // created_at: new Date(), + created_at: new Date(), modified_at: new Date(), id_linked_user: linkedUserId, remote_id: originId, @@ -319,7 +325,7 @@ export class SyncService implements OnModuleInit { } return teams_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/ticket/services/front/index.ts b/packages/api/src/ticketing/ticket/services/front/index.ts index 5e05808a8..5c472abca 100644 --- a/packages/api/src/ticketing/ticket/services/front/index.ts +++ b/packages/api/src/ticketing/ticket/services/front/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { FrontTicketInput, FrontTicketOutput } from './types'; import { Utils } from '@ticketing/@lib/@utils'; @@ -54,7 +54,9 @@ export class FrontService implements ITicketService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO: construct the right binary attachment //get the AWS s3 right file //TODO: check how to send a stream of a url @@ -152,10 +154,10 @@ export class FrontService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.ticket, ActionType.POST, ); @@ -189,10 +191,10 @@ export class FrontService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/github/index.ts b/packages/api/src/ticketing/ticket/services/github/index.ts index 4437a2f42..f703b293c 100644 --- a/packages/api/src/ticketing/ticket/services/github/index.ts +++ b/packages/api/src/ticketing/ticket/services/github/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GithubTicketInput, GithubTicketOutput } from './types'; @@ -57,10 +57,10 @@ export class GithubService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Github', + 'github', TicketingObject.ticket, ActionType.POST, ); @@ -96,10 +96,10 @@ export class GithubService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Github', + 'github', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/gitlab/index.ts b/packages/api/src/ticketing/ticket/services/gitlab/index.ts index b3552c9df..c3adcb056 100644 --- a/packages/api/src/ticketing/ticket/services/gitlab/index.ts +++ b/packages/api/src/ticketing/ticket/services/gitlab/index.ts @@ -6,101 +6,103 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GitlabTicketInput, GitlabTicketOutput } from './types'; - @Injectable() export class GitlabService implements ITicketService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.ticket.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } - async addTicket( - ticketData: GitlabTicketInput, - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); - const dataBody = ticketData; + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.ticket.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } + async addTicket( + ticketData: GitlabTicketInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); + const dataBody = ticketData; - const resp = await axios.post( - `${connection.account_url}/projects/${ticketData.project_id}/issues`, - JSON.stringify(dataBody), - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }, - ); - return { - data: resp.data, - message: 'Gitlab ticket created', - statusCode: 201, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.ticket, - ActionType.POST, - ); - } + const resp = await axios.post( + `${connection.account_url}/projects/${ticketData.project_id}/issues`, + JSON.stringify(dataBody), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data, + message: 'Gitlab ticket created', + statusCode: 201, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.ticket, + ActionType.POST, + ); } - async syncTickets( - linkedUserId: string, - custom_properties?: string[], - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + } + async syncTickets( + linkedUserId: string, + custom_properties?: string[], + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const resp = await axios.get(`${connection.account_url}/issues?scope=created_by_me&scope=assigned_to_me`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - this.logger.log(`Synced gitlab tickets !`); + const resp = await axios.get( + `${connection.account_url}/issues?scope=created_by_me&scope=assigned_to_me`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced gitlab tickets !`); - return { - data: resp.data, - message: 'Gitlab tickets retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.ticket, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'Gitlab tickets retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.ticket, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/ticket/services/gorgias/index.ts b/packages/api/src/ticketing/ticket/services/gorgias/index.ts index 993b8412e..7c05ee1f0 100644 --- a/packages/api/src/ticketing/ticket/services/gorgias/index.ts +++ b/packages/api/src/ticketing/ticket/services/gorgias/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { GorgiasTicketInput, GorgiasTicketOutput } from './types'; import * as fs from 'fs'; @@ -51,7 +51,9 @@ export class GorgiasService implements ITicketService { }, }); if (!res) { - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); } // Assuming you want to construct the right binary attachment here // For now, we'll just return the URL @@ -94,10 +96,10 @@ export class GorgiasService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.ticket, ActionType.POST, ); @@ -132,10 +134,10 @@ export class GorgiasService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/hubspot/index.ts b/packages/api/src/ticketing/ticket/services/hubspot/index.ts index a4c408319..abf70bc94 100644 --- a/packages/api/src/ticketing/ticket/services/hubspot/index.ts +++ b/packages/api/src/ticketing/ticket/services/hubspot/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { HubspotTicketInput, @@ -58,10 +58,10 @@ export class HubspotService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Hubspot', + 'hubspot', TicketingObject.ticket, ActionType.POST, ); @@ -105,10 +105,10 @@ export class HubspotService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Hubspot', + 'hubspot', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/jira/index.ts b/packages/api/src/ticketing/ticket/services/jira/index.ts index 597bf3673..8d35a7b56 100644 --- a/packages/api/src/ticketing/ticket/services/jira/index.ts +++ b/packages/api/src/ticketing/ticket/services/jira/index.ts @@ -6,10 +6,10 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { JiraTicketInput, JiraTicketOutput } from './types'; -import { Utils } from '@ticketing/@lib/@utils';; +import { Utils } from '@ticketing/@lib/@utils'; @Injectable() export class JiraService implements ITicketService { @@ -64,10 +64,10 @@ export class JiraService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.ticket, ActionType.POST, ); @@ -100,10 +100,10 @@ export class JiraService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/jira/mappers.ts b/packages/api/src/ticketing/ticket/services/jira/mappers.ts index 25716d262..a577cd5c5 100644 --- a/packages/api/src/ticketing/ticket/services/jira/mappers.ts +++ b/packages/api/src/ticketing/ticket/services/jira/mappers.ts @@ -21,7 +21,9 @@ export class JiraTicketMapper implements ITicketMapper { }[], ): Promise { if (!source.project_id) { - throw new Error('a project key/id is mandatory for Jira ticket creation'); + throw new ReferenceError( + 'a project key/id is mandatory for Jira ticket creation', + ); } const result: JiraTicketInput = { fields: { diff --git a/packages/api/src/ticketing/ticket/services/registry.service.ts b/packages/api/src/ticketing/ticket/services/registry.service.ts index bde87a420..9f2f6944b 100644 --- a/packages/api/src/ticketing/ticket/services/registry.service.ts +++ b/packages/api/src/ticketing/ticket/services/registry.service.ts @@ -16,7 +16,9 @@ export class ServiceRegistry { getService(integrationId: string): ITicketService { const service = this.serviceMap.get(integrationId); if (!service) { - throw new Error(`Service not found for integration ID: ${integrationId}`); + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); } return service; } diff --git a/packages/api/src/ticketing/ticket/services/ticket.service.ts b/packages/api/src/ticketing/ticket/services/ticket.service.ts index 9c90f862e..ff0672921 100644 --- a/packages/api/src/ticketing/ticket/services/ticket.service.ts +++ b/packages/api/src/ticketing/ticket/services/ticket.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; import { ApiResponse } from '@@core/utils/types'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { WebhookService } from '@@core/webhook/webhook.service'; import { UnifiedTicketInput, @@ -16,6 +15,7 @@ import { FieldMappingService } from '@@core/field-mapping/field-mapping.service' import { unify } from '@@core/utils/unification/unify'; import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from './registry.service'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; @Injectable() export class TicketService { @@ -48,7 +48,13 @@ export class TicketService { ); return responses; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_TICKETS_ERROR', + message: 'TicketService.batchAddTickets() call failed', + cause: error, + }), + ); } } @@ -65,7 +71,7 @@ export class TicketService { }, }); //CHECKS - if (!linkedUser) throw new Error('Linked User Not Found'); + if (!linkedUser) throw new ReferenceError('Linked User Not Found'); const acc = unifiedTicketData.account_id; //check if contact_id and account_id refer to real uuids if (acc) { @@ -75,7 +81,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted an account_id which does not exist'); + throw new ReferenceError( + 'You inserted an account_id which does not exist', + ); } const contact = unifiedTicketData.contact_id; @@ -87,7 +95,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted a contact_id which does not exist'); + throw new ReferenceError( + 'You inserted a contact_id which does not exist', + ); } const assignees = unifiedTicketData.assigned_to; //CHEK IF assigned_to contains valid Users uuids @@ -99,7 +109,9 @@ export class TicketService { }, }); if (!search) - throw new Error('You inserted an assignee which does not exist'); + throw new ReferenceError( + 'You inserted an assignee which does not exist', + ); }); } // Retrieve custom field mappings @@ -329,7 +341,13 @@ export class TicketService { ); return result_ticket; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'CREATE_TICKET_ERROR', + message: 'TicketService.addTicket() call failed', + cause: error, + }), + ); } } @@ -405,7 +423,13 @@ export class TicketService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TICKET_ERROR', + message: 'TicketService.getTicket() call failed', + cause: error, + }), + ); } } @@ -414,8 +438,12 @@ export class TicketService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedTicketOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedTicketOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced @@ -427,21 +455,23 @@ export class TicketService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_ticket: cursor - } + id_tcg_ticket: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let tickets = await this.prisma.tcg_tickets.findMany({ + const tickets = await this.prisma.tcg_tickets.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_ticket: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_ticket: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -453,8 +483,10 @@ export class TicketService { },*/ }); - if (tickets.length === (pageSize + 1)) { - next_cursor = Buffer.from(tickets[tickets.length - 1].id_tcg_ticket).toString('base64'); + if (tickets.length === pageSize + 1) { + next_cursor = Buffer.from( + tickets[tickets.length - 1].id_tcg_ticket, + ).toString('base64'); tickets.pop(); } @@ -544,10 +576,16 @@ export class TicketService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_TICKETS_ERROR', + message: 'TicketService.getTickets() call failed', + cause: error, + }), + ); } } //TODO @@ -556,9 +594,7 @@ export class TicketService { updateTicketData: Partial, ): Promise { try { - } catch (error) { - handleServiceError(error, this.logger); - } + } catch (error) {} // TODO: fetch the ticket from the database using 'id' // TODO: update the ticket with 'updateTicketData' // TODO: save the updated ticket back to the database diff --git a/packages/api/src/ticketing/ticket/services/zendesk/index.ts b/packages/api/src/ticketing/ticket/services/zendesk/index.ts index 4e1d93f04..ccf57173f 100644 --- a/packages/api/src/ticketing/ticket/services/zendesk/index.ts +++ b/packages/api/src/ticketing/ticket/services/zendesk/index.ts @@ -6,7 +6,7 @@ import { TicketingObject } from '@ticketing/@lib/@types'; import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { ZendeskTicketInput, ZendeskTicketOutput } from './types'; @@ -52,7 +52,9 @@ export class ZendeskService implements ITicketService { }, }); if (!res) - throw new Error(`tcg_attachment not found for uuid ${uuid}`); + throw new ReferenceError( + `tcg_attachment not found for uuid ${uuid}`, + ); //TODO:; fetch the right file from AWS s3 const s3File = ''; @@ -103,10 +105,10 @@ export class ZendeskService implements ITicketService { statusCode: 201, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.ticket, ActionType.POST, ); @@ -141,10 +143,10 @@ export class ZendeskService implements ITicketService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/sync/sync.service.ts b/packages/api/src/ticketing/ticket/sync/sync.service.ts index d63dd428f..3dc67ccbf 100644 --- a/packages/api/src/ticketing/ticket/sync/sync.service.ts +++ b/packages/api/src/ticketing/ticket/sync/sync.service.ts @@ -1,6 +1,6 @@ import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; @@ -35,7 +35,7 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -99,18 +99,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_TICKET_SYNC_ERROR', + message: 'SyncService.syncTickets() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +143,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping tickets syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -194,7 +200,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -211,7 +217,7 @@ export class SyncService implements OnModuleInit { const originId = ticket.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingTicket = await this.prisma.tcg_tickets.findFirst({ @@ -373,7 +379,7 @@ export class SyncService implements OnModuleInit { } return tickets_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/ticket/ticket.controller.ts b/packages/api/src/ticketing/ticket/ticket.controller.ts index a47199c9c..cbbe572b6 100644 --- a/packages/api/src/ticketing/ticket/ticket.controller.ts +++ b/packages/api/src/ticketing/ticket/ticket.controller.ts @@ -70,7 +70,7 @@ export class TicketController { linkedUserId, pageSize, remote_data, - cursor + cursor, ); } catch (error) { throw new Error(error); diff --git a/packages/api/src/ticketing/user/services/front/index.ts b/packages/api/src/ticketing/user/services/front/index.ts index b7f59f8bf..2bdd8c716 100644 --- a/packages/api/src/ticketing/user/services/front/index.ts +++ b/packages/api/src/ticketing/user/services/front/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { FrontUserOutput } from './types'; @@ -52,10 +52,10 @@ export class FrontService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Front', + 'front', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/gitlab/index.ts b/packages/api/src/ticketing/user/services/gitlab/index.ts index 15d1ff254..4ae3af237 100644 --- a/packages/api/src/ticketing/user/services/gitlab/index.ts +++ b/packages/api/src/ticketing/user/services/gitlab/index.ts @@ -5,62 +5,62 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { GitlabUserOutput } from './types'; @Injectable() export class GitlabService implements IUserService { - constructor( - private prisma: PrismaService, - private logger: LoggerService, - private cryptoService: EncryptionService, - private registry: ServiceRegistry, - ) { - this.logger.setContext( - TicketingObject.user.toUpperCase() + ':' + GitlabService.name, - ); - this.registry.registerService('gitlab', this); - } + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.user.toUpperCase() + ':' + GitlabService.name, + ); + this.registry.registerService('gitlab', this); + } - async syncUsers( - linkedUserId: string, - ): Promise> { - try { - const connection = await this.prisma.connections.findFirst({ - where: { - id_linked_user: linkedUserId, - provider_slug: 'gitlab', - vertical: 'ticketing', - }, - }); + async syncUsers( + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'gitlab', + vertical: 'ticketing', + }, + }); - const resp = await axios.get(`${connection.account_url}/users`, { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.cryptoService.decrypt( - connection.access_token, - )}`, - }, - }); - this.logger.log(`Synced gitlab users !`); + const resp = await axios.get(`${connection.account_url}/users`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced gitlab users !`); - // console.log("Users Data : ", resp.data); + // console.log("Users Data : ", resp.data); - return { - data: resp.data, - message: 'gitlab users retrieved', - statusCode: 200, - }; - } catch (error) { - handleServiceError( - error, - this.logger, - 'Gitlab', - TicketingObject.user, - ActionType.GET, - ); - } + return { + data: resp.data, + message: 'gitlab users retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'gitlab', + TicketingObject.user, + ActionType.GET, + ); } + } } diff --git a/packages/api/src/ticketing/user/services/gorgias/index.ts b/packages/api/src/ticketing/user/services/gorgias/index.ts index b04e7ae29..42c2207c5 100644 --- a/packages/api/src/ticketing/user/services/gorgias/index.ts +++ b/packages/api/src/ticketing/user/services/gorgias/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { GorgiasUserOutput } from './types'; @@ -52,10 +52,10 @@ export class GorgiasService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Gorgias', + 'gorgias', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/jira/index.ts b/packages/api/src/ticketing/user/services/jira/index.ts index 61ac6baae..7dd0e687e 100644 --- a/packages/api/src/ticketing/user/services/jira/index.ts +++ b/packages/api/src/ticketing/user/services/jira/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; import { JiraUserOutput } from './types'; @@ -51,10 +51,10 @@ export class JiraService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Jira', + 'jira', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/services/user.service.ts b/packages/api/src/ticketing/user/services/user.service.ts index 8a4493c93..74ad0024b 100644 --- a/packages/api/src/ticketing/user/services/user.service.ts +++ b/packages/api/src/ticketing/user/services/user.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '@@core/prisma/prisma.service'; import { LoggerService } from '@@core/logger/logger.service'; import { v4 as uuidv4 } from 'uuid'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; +import { throwTypedError, UnifiedTicketingError } from '@@core/utils/errors'; import { UnifiedUserOutput } from '../types/model.unified'; @Injectable() @@ -22,6 +22,8 @@ export class UserService { }, }); + if (!user) throw new ReferenceError('User undefined'); + // Fetch field mappings for the ticket const values = await this.prisma.value.findMany({ where: { @@ -73,7 +75,13 @@ export class UserService { return res; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_USER_ERROR', + message: 'UserService.getUser() call failed', + cause: error, + }), + ); } } @@ -82,11 +90,14 @@ export class UserService { linkedUserId: string, pageSize: number, remote_data?: boolean, - cursor?: string - ): Promise<{ data: UnifiedUserOutput[], prev_cursor: null | string, next_cursor: null | string }> { + cursor?: string, + ): Promise<{ + data: UnifiedUserOutput[]; + prev_cursor: null | string; + next_cursor: null | string; + }> { try { //TODO: handle case where data is not there (not synced) or old synced - let prev_cursor = null; let next_cursor = null; @@ -95,21 +106,23 @@ export class UserService { where: { remote_platform: integrationId.toLowerCase(), id_linked_user: linkedUserId, - id_tcg_user: cursor - } + id_tcg_user: cursor, + }, }); if (!isCursorPresent) { - throw new NotFoundError(`The provided cursor does not exist!`); + throw new ReferenceError(`The provided cursor does not exist!`); } } - let users = await this.prisma.tcg_users.findMany({ + const users = await this.prisma.tcg_users.findMany({ take: pageSize + 1, - cursor: cursor ? { - id_tcg_user: cursor - } : undefined, + cursor: cursor + ? { + id_tcg_user: cursor, + } + : undefined, orderBy: { - created_at: 'asc' + created_at: 'asc', }, where: { remote_platform: integrationId.toLowerCase(), @@ -117,8 +130,10 @@ export class UserService { }, }); - if (users.length === (pageSize + 1)) { - next_cursor = Buffer.from(users[users.length - 1].id_tcg_user).toString('base64'); + if (users.length === pageSize + 1) { + next_cursor = Buffer.from(users[users.length - 1].id_tcg_user).toString( + 'base64', + ); users.pop(); } @@ -198,10 +213,16 @@ export class UserService { return { data: res, prev_cursor, - next_cursor + next_cursor, }; } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new UnifiedTicketingError({ + name: 'GET_USERS_ERROR', + message: 'UserService.getUsers() call failed', + cause: error, + }), + ); } } } diff --git a/packages/api/src/ticketing/user/services/zendesk/index.ts b/packages/api/src/ticketing/user/services/zendesk/index.ts index 8a855a9fb..198330c86 100644 --- a/packages/api/src/ticketing/user/services/zendesk/index.ts +++ b/packages/api/src/ticketing/user/services/zendesk/index.ts @@ -5,7 +5,7 @@ import { EncryptionService } from '@@core/encryption/encryption.service'; import { TicketingObject } from '@ticketing/@lib/@types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; -import { ActionType, handleServiceError } from '@@core/utils/errors'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; import { ServiceRegistry } from '../registry.service'; import { IUserService } from '@ticketing/user/types'; @@ -57,10 +57,10 @@ export class ZendeskService implements IUserService { statusCode: 200, }; } catch (error) { - handleServiceError( + handle3rdPartyServiceError( error, this.logger, - 'Zendesk', + 'zendesk', TicketingObject.user, ActionType.GET, ); diff --git a/packages/api/src/ticketing/user/sync/sync.service.ts b/packages/api/src/ticketing/user/sync/sync.service.ts index c017a83ba..5632ec84d 100644 --- a/packages/api/src/ticketing/user/sync/sync.service.ts +++ b/packages/api/src/ticketing/user/sync/sync.service.ts @@ -1,7 +1,6 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/logger/logger.service'; import { PrismaService } from '@@core/prisma/prisma.service'; -import { NotFoundError, handleServiceError } from '@@core/utils/errors'; import { Cron } from '@nestjs/schedule'; import { ApiResponse } from '@@core/utils/types'; import { v4 as uuidv4 } from 'uuid'; @@ -17,6 +16,7 @@ import { UnifiedUserOutput } from '../types/model.unified'; import { TICKETING_PROVIDERS } from '@panora/shared'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; +import { SyncError, throwTypedError } from '@@core/utils/errors'; @Injectable() export class SyncService implements OnModuleInit { @@ -35,28 +35,32 @@ export class SyncService implements OnModuleInit { try { await this.scheduleSyncJob(); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } private async scheduleSyncJob() { - const jobName = 'ticketing-sync-users'; + try { + const jobName = 'ticketing-sync-users'; - // Remove existing jobs to avoid duplicates in case of application restart - const jobs = await this.syncQueue.getRepeatableJobs(); - for (const job of jobs) { - if (job.name === jobName) { - await this.syncQueue.removeRepeatableByKey(job.key); + // Remove existing jobs to avoid duplicates in case of application restart + const jobs = await this.syncQueue.getRepeatableJobs(); + for (const job of jobs) { + if (job.name === jobName) { + await this.syncQueue.removeRepeatableByKey(job.key); + } } + // Add new job to the queue with a CRON expression + await this.syncQueue.add( + jobName, + {}, + { + repeat: { cron: '0 0 * * *' }, // Runs once a day at midnight + }, + ); + } catch (error) { + throw error; } - // Add new job to the queue with a CRON expression - await this.syncQueue.add( - jobName, - {}, - { - repeat: { cron: '0 0 * * *' }, // Runs once a day at midnight - }, - ); } //function used by sync worker which populate our tcg_users table //its role is to fetch all users from providers 3rd parties and save the info inside our db @@ -67,12 +71,12 @@ export class SyncService implements OnModuleInit { this.logger.log(`Syncing users....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -99,18 +103,25 @@ export class SyncService implements OnModuleInit { id_project, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } catch (error) { - handleServiceError(error, this.logger); + throw error; } }); } } } } catch (error) { - handleServiceError(error, this.logger); + throwTypedError( + new SyncError({ + name: 'TICKETING_USER_SYNC_ERROR', + message: 'SyncService.syncUsers() call failed with args', + cause: error, + }), + this.logger, + ); } } @@ -136,7 +147,6 @@ export class SyncService implements OnModuleInit { this.logger.warn( `Skipping users syncing... No ${integrationId} connection was found for linked user ${linkedUserId} `, ); - return; } // get potential fieldMappings and extract the original properties name const customFieldMappings = @@ -168,8 +178,6 @@ export class SyncService implements OnModuleInit { customFieldMappings, })) as UnifiedUserOutput[]; - - //insert the data in the DB with the fieldMappings (value table) const user_data = await this.saveUsersInDb( linkedUserId, @@ -197,7 +205,7 @@ export class SyncService implements OnModuleInit { event.id_event, ); } catch (error) { - handleServiceError(error, this.logger); + throw error; } } @@ -214,7 +222,7 @@ export class SyncService implements OnModuleInit { const originId = user.remote_id; if (!originId || originId == '') { - throw new NotFoundError(`Origin id not there, found ${originId}`); + throw new ReferenceError(`Origin id not there, found ${originId}`); } const existingUser = await this.prisma.tcg_users.findFirst({ @@ -326,7 +334,7 @@ export class SyncService implements OnModuleInit { } return users_results; } catch (error) { - handleServiceError(error, this.logger); + throw error; } } } diff --git a/packages/api/src/ticketing/user/user.controller.ts b/packages/api/src/ticketing/user/user.controller.ts index 089b28dd9..fc612d59b 100644 --- a/packages/api/src/ticketing/user/user.controller.ts +++ b/packages/api/src/ticketing/user/user.controller.ts @@ -55,22 +55,19 @@ export class UserController { @Headers('x-connection-token') connection_token: string, @Query() query: FetchObjectsQueryDto, ) { - try { - const { linkedUserId, remoteSource } = - await this.connectionUtils.getConnectionMetadataFromConnectionToken( - connection_token, - ); - const { remote_data, pageSize, cursor } = query; - return this.userService.getUsers( - remoteSource, - linkedUserId, - pageSize, - remote_data, - cursor + const { linkedUserId, remoteSource } = + await this.connectionUtils.getConnectionMetadataFromConnectionToken( + connection_token, ); - } catch (error) { - throw new Error(error); - } + const { remote_data, pageSize, cursor } = query; + + return this.userService.getUsers( + remoteSource, + linkedUserId, + pageSize, + remote_data, + cursor, + ); } @ApiOperation({ diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index 5e42f2956..cbc4cc7e3 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -5074,20 +5074,6 @@ ] } }, - "/project-connectors/create": { - "post": { - "operationId": "ProjectConnectorsController_createConnectorsToProject", - "parameters": [], - "responses": { - "201": { - "description": "" - } - }, - "tags": [ - "project-connectors" - ] - } - }, "/ticketing/attachments": { "get": { "operationId": "getAttachments", @@ -5702,7 +5688,7 @@ }, "country": { "type": "string", - "description": "The country." + "description": "The country" }, "address_type": { "type": "string", diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index ac4d9922f..f2c25f2c8 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -21,7 +21,7 @@ export const constructAuthUrl = async ({ projectId, linkedUserId, providerName, // console.log('encodedRedirect URL : ', encodedRedirectUrl); // const vertical = findConnectorCategory(providerName); if (vertical == null) { - throw new Error('vertical is null'); + throw new ReferenceError('vertical is null'); } const config = CONNECTORS_METADATA[vertical.toLowerCase()][providerName]; @@ -85,17 +85,17 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { // console.log("Fetched Data ", JSON.stringify(data)) const clientId = data.CLIENT_ID; - if (!clientId) throw new Error(`No client id for type ${type}`) + if (!clientId) throw new ReferenceError(`No client id for type ${type}`) const scopes = data.SCOPE const { urls: urls } = config; const { authBaseUrl: baseUrl } = urls; - if (!baseUrl) throw new Error(`No authBaseUrl found for type ${type}`) + if (!baseUrl) throw new ReferenceError(`No authBaseUrl found for type ${type}`) // construct the baseAuthUrl based on the fact that client may use custom subdomain const BASE_URL: string = providerName === 'gorgias' ? `${apiUrl}${baseUrl}` : - data.SUBDOMAIN ? data.SUBDOMAIN + baseUrl : baseUrl; + data.SUBDOMAIN ? data.SUBDOMAIN + baseUrl : baseUrl; // console.log('BASE URL IS '+ BASE_URL) if (!baseUrl || !BASE_URL) { @@ -117,8 +117,10 @@ const handleOAuth2Url = async (input: HandleOAuth2Url) => { params += '&response_type=code&access_type=offline'; break; case 'jira': + params = `audience=api.atlassian.com&${params}&prompt=consent&response_type=code`; + break; case 'jira_service_mgmt': - params = `audience=api.atlassian.com&${params}&prompt=consent`; + params = `audience=api.atlassian.com&${params}&prompt=consen&response_type=codet`; break; case 'gitlab': params += '&response_type=code&code_challenge=&code_challenge_method='; diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 6257d6866..ea96e6c5f 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -334,7 +334,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { authStrategy: AuthStrategy.oauth2 }, 'jira': { - scopes: 'read:jira-work manage:jira-project manage:jira-data-provider manage:jira-webhook write:jira-work manage:jira-configuration read:jira-user offline_access', + scopes: 'read:jira-work manage:jira-project manage:jira-configuration read:jira-user write:jira-work manage:jira-webhook manage:jira-data-provider offline_access', urls: { docsUrl: '', apiUrl: '/rest/api/3', diff --git a/packages/shared/src/envConfig.ts b/packages/shared/src/envConfig.ts index 4ba8821d6..3d58a5c1b 100644 --- a/packages/shared/src/envConfig.ts +++ b/packages/shared/src/envConfig.ts @@ -72,7 +72,7 @@ export function extractAuthMode(type: string): AuthStrategy { case 'BASIC': return AuthStrategy.basic; default: - throw new Error('Auth mode not found'); + throw new ReferenceError('Auth mode not found'); } }