From f51a8c60b485c19d618d08721048f7485c356b45 Mon Sep 17 00:00:00 2001 From: mihir-4116 Date: Fri, 15 Sep 2023 15:55:15 +0530 Subject: [PATCH 1/3] feat(iterable): user deletion support --- src/v0/destinations/iterable/deleteUsers.js | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/v0/destinations/iterable/deleteUsers.js diff --git a/src/v0/destinations/iterable/deleteUsers.js b/src/v0/destinations/iterable/deleteUsers.js new file mode 100644 index 00000000000..d539f9b5a8f --- /dev/null +++ b/src/v0/destinations/iterable/deleteUsers.js @@ -0,0 +1,67 @@ +const { httpDELETE } = require('../../../adapters/network'); +const { processAxiosResponse } = require('../../../adapters/utils/networkUtils'); +const { isHttpStatusSuccess } = require('../../util'); +const { getDynamicErrorType } = require('../../../adapters/utils/networkUtils'); +const { NetworkError, ConfigurationError } = require('../../util/errorTypes'); +const { executeCommonValidations } = require('../../util/regulation-api'); +const tags = require('../../util/tags'); +const { JSON_MIME_TYPE } = require('../../util/constant'); + +// Ref-> https://developers.intercom.com/intercom-api-reference/v1.3/reference/permanently-delete-a-user +const userDeletionHandler = async (userAttributes, config) => { + if (!config) { + throw new ConfigurationError('Config for deletion not present'); + } + const { apiKey } = config; + if (!apiKey) { + throw new ConfigurationError('api key for deletion not present'); + } + const validUserIds = []; + userAttributes.forEach((userAttribute) => { + // Dropping the user if userId is not present + if (userAttribute.userId) { + validUserIds.push(userAttribute.userId); + } + }); + const failedUserDeletions = []; + await Promise.all( + validUserIds.map(async (uId) => { + const url = `https://api.iterable.com/api/users/byUserId/${uId}`; + const requestOptions = { + headers: { + 'Content-Type': JSON_MIME_TYPE, + api_key: apiKey, + }, + }; + const resp = await httpDELETE(url, requestOptions, { + destType: 'iterable', + feature: 'deleteUsers', + }); + const handledDelResponse = processAxiosResponse(resp); + if (!isHttpStatusSuccess(handledDelResponse.status) && handledDelResponse.status !== 404) { + failedUserDeletions.push(uId); + } + }), + ); + + if (failedUserDeletions.length > 0) { + throw new NetworkError( + `User deletion request failed for userIds : ${failedUserDeletions}`, + 400, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(400), + }, + failedUserDeletions, + ); + } + + return { statusCode: 200, status: 'successful' }; +}; +const processDeleteUsers = async (event) => { + const { userAttributes, config } = event; + executeCommonValidations(userAttributes); + const resp = await userDeletionHandler(userAttributes, config); + return resp; +}; + +module.exports = { processDeleteUsers }; From 35ea46a4f211cee5e20059b86fb875c583c9dae5 Mon Sep 17 00:00:00 2001 From: mihir-4116 Date: Mon, 18 Sep 2023 18:06:00 +0530 Subject: [PATCH 2/3] feat(iterable): user deletion improvements --- src/v0/destinations/iterable/deleteUsers.js | 15 +- .../data/iterable/handler_input.json | 93 ++++++++++++ .../data/iterable/handler_output.json | 32 ++++ .../data/iterable/nw_client_data.json | 138 ++++++++++++++++++ 4 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 test/deleteUsers/data/iterable/handler_input.json create mode 100644 test/deleteUsers/data/iterable/handler_output.json create mode 100644 test/deleteUsers/data/iterable/nw_client_data.json diff --git a/src/v0/destinations/iterable/deleteUsers.js b/src/v0/destinations/iterable/deleteUsers.js index d539f9b5a8f..96901c08626 100644 --- a/src/v0/destinations/iterable/deleteUsers.js +++ b/src/v0/destinations/iterable/deleteUsers.js @@ -39,14 +39,25 @@ const userDeletionHandler = async (userAttributes, config) => { }); const handledDelResponse = processAxiosResponse(resp); if (!isHttpStatusSuccess(handledDelResponse.status) && handledDelResponse.status !== 404) { - failedUserDeletions.push(uId); + if (handledDelResponse.status !== 400) { + throw new NetworkError( + `User deletion request failed : ${handledDelResponse.response.msg}`, + handledDelResponse.status, + { + [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(handledDelResponse.status), + }, + handledDelResponse, + ); + } else { + failedUserDeletions.push({ userId: uId, Reason: handledDelResponse.response.msg }); + } } }), ); if (failedUserDeletions.length > 0) { throw new NetworkError( - `User deletion request failed for userIds : ${failedUserDeletions}`, + `User deletion request failed for userIds : ${JSON.stringify(failedUserDeletions)}`, 400, { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(400), diff --git a/test/deleteUsers/data/iterable/handler_input.json b/test/deleteUsers/data/iterable/handler_input.json new file mode 100644 index 00000000000..8ebc859089a --- /dev/null +++ b/test/deleteUsers/data/iterable/handler_input.json @@ -0,0 +1,93 @@ +[ + { + "request": { + "body": [ + { + "destType": "ITERABLE", + "userAttributes": [ + { + "userId": "rudder1" + } + ] + } + ] + } + }, + { + "request": { + "body": [ + { + "destType": "ITERABLE", + "userAttributes": [ + { + "userId": "rudder2" + } + ], + "config": { + "apiToken": "dummyApiKey" + } + } + ] + } + }, + { + "request": { + "body": [ + { + "destType": "ITERABLE", + "userAttributes": [ + { + "userId": "rudder1" + }, + { + "userId": "rudder2" + } + ], + "config": { + "apiKey": "dummyApiKey" + } + } + ] + } + }, + { + "request": { + "body": [ + { + "destType": "ITERABLE", + "userAttributes": [ + { + "userId": "rudder3" + }, + { + "userId": "rudder4" + } + ], + "config": { + "apiKey": "invalidKey" + } + } + ] + } + }, + { + "request": { + "body": [ + { + "destType": "ITERABLE", + "userAttributes": [ + { + "userId": "rudder5" + }, + { + "userId": "rudder6" + } + ], + "config": { + "apiKey": "dummyApiKey" + } + } + ] + } + } +] diff --git a/test/deleteUsers/data/iterable/handler_output.json b/test/deleteUsers/data/iterable/handler_output.json new file mode 100644 index 00000000000..b053d04df4a --- /dev/null +++ b/test/deleteUsers/data/iterable/handler_output.json @@ -0,0 +1,32 @@ +[ + [ + { + "statusCode": 400, + "error": "Config for deletion not present" + } + ], + [ + { + "statusCode": 400, + "error": "api key for deletion not present" + } + ], + [ + { + "statusCode": 400, + "error": "User deletion request failed for userIds : [{\"userId\":\"rudder2\",\"Reason\":\"User does not exist. Email: UserId: rudder2\"}]" + } + ], + [ + { + "error": "User deletion request failed : Invalid API key", + "statusCode": 401 + } + ], + [ + { + "statusCode": 200, + "status": "successful" + } + ] +] diff --git a/test/deleteUsers/data/iterable/nw_client_data.json b/test/deleteUsers/data/iterable/nw_client_data.json new file mode 100644 index 00000000000..159c301b115 --- /dev/null +++ b/test/deleteUsers/data/iterable/nw_client_data.json @@ -0,0 +1,138 @@ +[ + [ + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder1", + {}, + { + "Accept": "application/json", + "api_key": "dummyApiKey" + } + ], + "response": { + "response": { + "data": { + "msg": "All users associated with rudder1 were successfully deleted", + "code": "Success", + "params": null + }, + "status": 200 + } + } + }, + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder2", + {}, + { + "Accept": "application/json", + "api_key": "dummyApiKey" + } + ], + "response": { + "response": { + "data": { + "msg": "User does not exist. Email: UserId: rudder2", + "code": "BadParams", + "params": null + }, + "status": 400 + } + } + } + ], + [ + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder3", + {}, + { + "Accept": "application/json", + "api_key": "invalidKey" + } + ], + "response": { + "response": { + "data": { + "msg": "Invalid API key", + "code": "Success", + "params": { + "endpoint": "/api/users/byUserId/rudder3" + } + }, + "status": 401 + } + } + }, + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder4", + {}, + { + "Accept": "application/json", + "api_key": "invalidKey" + } + ], + "response": { + "response": { + "data": { + "msg": "Invalid API key", + "code": "Success", + "params": { + "endpoint": "/api/users/byUserId/rudder3" + } + }, + "status": 401 + } + } + } + ], + [ + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder5", + {}, + { + "Accept": "application/json", + "api_key": "dummyApiKey" + } + ], + "response": { + "response": { + "data": { + "msg": "All users associated with rudder5 were successfully deleted", + "code": "Success", + "params": null + }, + "status": 200 + } + } + }, + { + "type": "delete", + "reqParams": [ + "https://api.iterable.com/api/users/byUserId/rudder6", + {}, + { + "Accept": "application/json", + "api_key": "dummyApiKey" + } + ], + "response": { + "response": { + "data": { + "msg": "All users associated with rudder6 were successfully deleted", + "code": "Success", + "params": null + }, + "status": 200 + } + } + } + ] +] From e5f0f75bdd36feaa08f68bb27970e98cf7deba39 Mon Sep 17 00:00:00 2001 From: mihir-4116 Date: Fri, 22 Sep 2023 11:15:42 +0530 Subject: [PATCH 3/3] chore: added comments --- src/v0/destinations/iterable/deleteUsers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v0/destinations/iterable/deleteUsers.js b/src/v0/destinations/iterable/deleteUsers.js index 96901c08626..834cccd4cb3 100644 --- a/src/v0/destinations/iterable/deleteUsers.js +++ b/src/v0/destinations/iterable/deleteUsers.js @@ -40,6 +40,7 @@ const userDeletionHandler = async (userAttributes, config) => { const handledDelResponse = processAxiosResponse(resp); if (!isHttpStatusSuccess(handledDelResponse.status) && handledDelResponse.status !== 404) { if (handledDelResponse.status !== 400) { + // Generic errors such as invalid api key throw new NetworkError( `User deletion request failed : ${handledDelResponse.response.msg}`, handledDelResponse.status, @@ -49,6 +50,7 @@ const userDeletionHandler = async (userAttributes, config) => { handledDelResponse, ); } else { + // Specific errors such as user is not found failedUserDeletions.push({ userId: uId, Reason: handledDelResponse.response.msg }); } }