diff --git a/packages/hoppscotch-sh-admin/src/helpers/errors.ts b/packages/hoppscotch-sh-admin/src/helpers/errors.ts index 5473ec528..f66559ea3 100644 --- a/packages/hoppscotch-sh-admin/src/helpers/errors.ts +++ b/packages/hoppscotch-sh-admin/src/helpers/errors.ts @@ -7,3 +7,21 @@ export const UNAUTHORIZED = 'Unauthorized' as const; // Sometimes the backend returns Unauthorized error message as follows: export const GRAPHQL_UNAUTHORIZED = '[GraphQL] Unauthorized' as const; + +/** + * Email not found + */ +export const EMAIL_NOT_FOUND = 'email/not_found'; + +/** + * Request Failed + */ +export const REQ_FAILED = 'request/failed'; + +export const INVALID_EMAIL = 'invalid/email' as const; + +/** + * Subscriber already exists + * (NewsletterService) + */ +export const SUBSCRIBER_ALREADY_EXISTS = 'newsletter/subscriber_already_exists'; diff --git a/packages/hoppscotch-sh-admin/src/helpers/listmonk.ts b/packages/hoppscotch-sh-admin/src/helpers/listmonk.ts new file mode 100644 index 000000000..31a019a25 --- /dev/null +++ b/packages/hoppscotch-sh-admin/src/helpers/listmonk.ts @@ -0,0 +1,147 @@ +import * as E from 'fp-ts/Either'; +import { EmailCodec } from '~/helpers/Email'; +import axios, { AxiosResponse } from 'axios'; +import { + REQ_FAILED, + EMAIL_NOT_FOUND, + INVALID_EMAIL, + SUBSCRIBER_ALREADY_EXISTS, +} from '~/helpers/errors'; + +enum HttpStatus { + BAD_REQUEST = 400, + CONFLICT = 409, + NOT_FOUND = 404, +} + +export type ResponseError = { + message: string; + statusCode: HttpStatus; +}; + +const handleResponse = (response: AxiosResponse) => { + if (response.status >= 200 && response.status < 300) { + return E.right(response.data); + } else { + return E.left({ + statusCode: HttpStatus.BAD_REQUEST, + message: REQ_FAILED, + }); + } +}; + +const listmonkConfig = { + baseURL: `sdsd`, + apiURL: `sfs`, + listmonkUsername: `fsdfs`, + listmonkPassword: `fdsf`, + newsletterListID: parseInt('fasf', 10), +}; + +const listmonk = axios.create({ + baseURL: listmonkConfig.apiURL, + auth: { + username: listmonkConfig.listmonkUsername, + password: listmonkConfig.listmonkPassword, + }, +}); +const castToSubscriber = (subscriberResponse: any) => { + return { + id: subscriberResponse.id, + created_at: subscriberResponse.created_at, + updated_at: subscriberResponse.updated_at, + uuid: subscriberResponse.uuid, + email: subscriberResponse.email, + name: subscriberResponse.name, + status: subscriberResponse.status, + lists: subscriberResponse.lists.map((list: any) => ({ + subscription_status: list.subscription_status, + subscription_created_at: list.subscription_created_at, + subscription_updated_at: list.subscription_updated_at, + id: list.id, + })), + }; +}; + +const fetchSubscriberDetailsByEmail = async (emailID: string) => { + const subscribersURL = `${listmonkConfig.apiURL}/subscribers`; + const params = { + query: `email = '${emailID}'`, + }; + try { + const response = await listmonk.get(subscribersURL, { + params: params, + }); + if (response.status >= 200 && response.status < 300) { + // check length of response results array + if (!response.data.data.results.length) { + return E.left({ + statusCode: HttpStatus.NOT_FOUND, + message: EMAIL_NOT_FOUND, + }); + } + + // fetch subscriber data from response results array object and typecast to `Subscriber` type. + const subscriberRes = response.data.data.results[0]; + const subscriber = castToSubscriber(subscriberRes); + return E.right(subscriber); + } else { + return E.left({ + statusCode: HttpStatus.BAD_REQUEST, + message: REQ_FAILED, + }); + } + } catch (err: any) { + return E.left({ + message: err.response.statusText, + statusCode: err.response.status, + }); + } +}; + +export const addSubscriberNewsletterList = async ( + emailID: string, + subscriberName: string +) => { + // Validate email + const isEmailValid = EmailCodec.is(emailID); + if (!isEmailValid) + return E.left({ + message: INVALID_EMAIL, + statusCode: HttpStatus.BAD_REQUEST, + }); + + // Check if subscriber already exists + const subscriber = await fetchSubscriberDetailsByEmail(emailID); + if (E.isRight(subscriber)) { + return E.left({ + message: SUBSCRIBER_ALREADY_EXISTS, + statusCode: HttpStatus.CONFLICT, + }); + } + + // Add a new subscriber to newsletter + const addSubscriberURL = `${listmonkConfig.apiURL}/subscribers`; + const reqData = { + email: `${emailID}`, + name: `${subscriberName}`, + status: 'enabled', + lists: [listmonkConfig.newsletterListID], + preconfirm_subscriptions: true, + }; + + try { + const response = await listmonk.post(addSubscriberURL, reqData, { + headers: { + 'Content-Type': 'application/json', + }, + }); + + return handleResponse(response); + } catch (err: any) { + return E.left({ + message: err.response.statusText, + statusCode: err.response.status, + }); + } +};