Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions huntflow_api_client/entities/user_settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from huntflow_api_client.entities.base import BaseEntity
from huntflow_api_client.models.common import StatusResponse
from huntflow_api_client.models.request.user_settings import (
ExchangeEmailAccountRequest,
OtherEmailAccountRequest,
)
from huntflow_api_client.models.response.user_settings import (
CalendarAccountsListResponse,
EmailAccount,
EmailAccountsListResponse,
)

Expand All @@ -23,3 +29,87 @@ async def get_calendar_accounts(self) -> CalendarAccountsListResponse:
"""
response = await self._api.request("GET", "/calendar_accounts")
return CalendarAccountsListResponse.model_validate(response.json())

async def create_exchange_email_account(
self,
data: ExchangeEmailAccountRequest,
) -> EmailAccount:
"""
API method reference https://api.huntflow.ai/v2/docs#post-/email_accounts/exchange

:param data: Exchange user email account data
:return: Created Exchange user email account for API robot.
"""
response = await self._api.request(
"POST",
"/email_accounts/exchange",
json=data.jsonable_dict(exclude_none=True),
)
return EmailAccount.model_validate(response.json())

async def update_exchange_email_account(
self,
data: ExchangeEmailAccountRequest,
account_email_id: int,
) -> EmailAccount:
"""
API method reference
https://api.huntflow.ai/v2/docs#put-/email_accounts/exchange/-account_email_id-

:param account_email_id: Email account ID
:param data: Exchange user email account data
:return: Updated Exchange user email account for API robot.
"""
response = await self._api.request(
"PUT",
f"/email_accounts/exchange/{account_email_id}",
json=data.jsonable_dict(exclude_none=True),
)
return EmailAccount.model_validate(response.json())

async def create_other_email_account(
self,
data: OtherEmailAccountRequest,
) -> EmailAccount:
"""
API method reference https://api.huntflow.ai/v2/docs#post-/email_accounts/other

:param data: SMTP/POP3/IMAP user email account data
:return: Created SMTP/POP3/IMAP user email account for API robot.
"""
response = await self._api.request(
"POST",
"/email_accounts/other",
json=data.jsonable_dict(exclude_none=True),
)
return EmailAccount.model_validate(response.json())

async def update_other_email_account(
self,
data: OtherEmailAccountRequest,
account_email_id: int,
) -> EmailAccount:
"""
API method reference
https://api.huntflow.ai/v2/docs#put-/email_accounts/other/-account_email_id-

:param account_email_id: Email account ID
:param data: SMTP/POP3/IMAP user email account data
:return: Updated SMTP/POP3/IMAP user email account for API robot.
"""
response = await self._api.request(
"PUT",
f"/email_accounts/other/{account_email_id}",
json=data.jsonable_dict(exclude_none=True),
)
return EmailAccount.model_validate(response.json())

async def delete_email_account(self, account_email_id: int) -> StatusResponse:
"""
API method reference
https://api.huntflow.ai/v2/docs#delete-/email_accounts/-account_email_id-

:param account_email_id: Email account ID
"""
response = await self._api.request("DELETE", f"/email_accounts/{account_email_id}")
return StatusResponse.model_validate(response.json())
10 changes: 10 additions & 0 deletions huntflow_api_client/models/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,13 @@ class RecommendationStatus(str, Enum):
class InterviewType(str, Enum):
USER = "user"
INTERVIEW = "interview"


class ExchangeAccessType(str, Enum):
DEFAULT = "DEFAULT"
IMPERSONATION = "IMPERSONATION"


class EmailInboundType(str, Enum):
DEFAULT = "IMAP"
IMPERSONATION = "POP3"
35 changes: 35 additions & 0 deletions huntflow_api_client/models/request/user_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Optional

from pydantic import AnyHttpUrl, EmailStr, Field, PositiveInt

from huntflow_api_client.models.common import JsonRequestModel
from huntflow_api_client.models.consts import EmailInboundType, ExchangeAccessType


class ExchangeEmailAccountRequest(JsonRequestModel):
"""
Model for EXCHANGE email connection
"""

access_type: ExchangeAccessType = Field(..., description="Exchange access type")
email: EmailStr = Field(..., description="Email address")
ews_url: AnyHttpUrl = Field(..., description="Exchange server URL")
password: str = Field(..., description="Exchange password")
user: Optional[str] = Field(None, description="Exchange user name")


class OtherEmailAccountRequest(JsonRequestModel):
"""
Model for IMAP/POP3 & SMTP email connection
"""

email: EmailStr = Field(..., description="Email address")
password: str = Field(..., description="Mailbox password")

inbound_host: str = Field(..., description="Inbound mail server URL without protocol")
inbound_port: Optional[PositiveInt] = Field(None, description="Inbound mail port")
inbound_ssl: bool = Field(False, description="Inbound mail server uses SSL")
outbound_host: str = Field(..., description="Outbound mail server URL without protocol")
outbound_port: Optional[PositiveInt] = Field(None, description="Outbound mail port")
outbound_ssl: bool = Field(False, description="Outbound mail server uses SSL")
type: EmailInboundType = Field(..., description="Type of inbound mail server")
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

[project]
name = "huntflow-api-client"
version = "2.7.0"
version = "2.8.0"
description = "Huntflow API Client for Python"
authors = [
{name = "Developers huntflow", email = "[email protected]"},
Expand Down
133 changes: 122 additions & 11 deletions tests/test_entities/test_user_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,30 @@

from huntflow_api_client import HuntflowAPI
from huntflow_api_client.entities.user_settings import UserSettings
from huntflow_api_client.models.common import StatusResponse
from huntflow_api_client.models.request.user_settings import (
ExchangeEmailAccountRequest,
OtherEmailAccountRequest,
)
from huntflow_api_client.models.response.user_settings import (
CalendarAccountsListResponse,
EmailAccount,
EmailAccountsListResponse,
)
from huntflow_api_client.tokens.proxy import HuntflowTokenProxy
from tests.api import BASE_URL, VERSIONED_BASE_URL

GET_USER_EMAIL_ACCOUNTS_RESPONSE: Dict[str, Any] = {
"items": [
{
"id": 10,
"name": "[email protected]",
"email": "[email protected]",
"receive": False,
"send": False,
"last_sync": "2020-01-01T00:00:00+03:00",
},
],
EMAIL_ACCOUNT: Dict[str, Any] = {
"id": 10,
"name": "[email protected]",
"email": "[email protected]",
"receive": False,
"send": False,
"last_sync": "2020-01-01T00:00:00+03:00",
}

GET_USER_EMAIL_ACCOUNTS_RESPONSE: Dict[str, Any] = {"items": [EMAIL_ACCOUNT]}

GET_USER_CALENDAR_ACCOUNTS_RESPONSE: Dict[str, Any] = {
"items": [
{
Expand All @@ -43,6 +47,30 @@
],
}

EXCHANGE_EMAIL_ACCOUNT_CREATE_REQUEST: Dict[str, Any] = {
"access_type": "DEFAULT",
"email": "[email protected]",
"ews_url": "https://your-company.org/ews/exchange.asmx",
"password": "string",
"user": "string",
}

ACCOUNT_EMAIL_ID = 1

IMAP_EMAIL_ACCOUNT_CREATE_REQUEST: Dict[str, Any] = {
"email": "[email protected]",
"password": "string",
"inbound_host": "imap.your-company.org",
"inbound_port": 1,
"inbound_ssl": False,
"outbound_host": "smtp.your-company.org",
"outbound_port": 1,
"outbound_ssl": False,
"type": "IMAP",
}

DELETE_ACCOUNT_EMAIL_RESPONSE: Dict[str, Any] = {"status": True}


async def test_get_email_accounts(
httpx_mock: HTTPXMock,
Expand Down Expand Up @@ -72,3 +100,86 @@ async def test_get_calendar_accounts(

response = await settings.get_calendar_accounts()
assert response == CalendarAccountsListResponse(**GET_USER_CALENDAR_ACCOUNTS_RESPONSE)


async def test_create_exchange_email_account(
httpx_mock: HTTPXMock,
token_proxy: HuntflowTokenProxy,
) -> None:
httpx_mock.add_response(
url=f"{VERSIONED_BASE_URL}/email_accounts/exchange",
json=EMAIL_ACCOUNT,
)
api_client = HuntflowAPI(BASE_URL, token_proxy=token_proxy)

api_request = ExchangeEmailAccountRequest(**EXCHANGE_EMAIL_ACCOUNT_CREATE_REQUEST)
settings = UserSettings(api_client)

response = await settings.create_exchange_email_account(api_request)
assert response == EmailAccount(**EMAIL_ACCOUNT)


async def test_update_exchange_email_account(
httpx_mock: HTTPXMock,
token_proxy: HuntflowTokenProxy,
) -> None:
httpx_mock.add_response(
url=f"{VERSIONED_BASE_URL}/email_accounts/exchange/{ACCOUNT_EMAIL_ID}",
json=EMAIL_ACCOUNT,
)
api_client = HuntflowAPI(BASE_URL, token_proxy=token_proxy)

api_request = ExchangeEmailAccountRequest(**EXCHANGE_EMAIL_ACCOUNT_CREATE_REQUEST)
settings = UserSettings(api_client)

response = await settings.update_exchange_email_account(api_request, ACCOUNT_EMAIL_ID)
assert response == EmailAccount(**EMAIL_ACCOUNT)


async def test_create_other_email_account(
httpx_mock: HTTPXMock,
token_proxy: HuntflowTokenProxy,
) -> None:
httpx_mock.add_response(
url=f"{VERSIONED_BASE_URL}/email_accounts/other",
json=EMAIL_ACCOUNT,
)
api_client = HuntflowAPI(BASE_URL, token_proxy=token_proxy)

api_request = OtherEmailAccountRequest(**IMAP_EMAIL_ACCOUNT_CREATE_REQUEST)
settings = UserSettings(api_client)

response = await settings.create_other_email_account(api_request)
assert response == EmailAccount(**EMAIL_ACCOUNT)


async def test_update_other_email_account(
httpx_mock: HTTPXMock,
token_proxy: HuntflowTokenProxy,
) -> None:
httpx_mock.add_response(
url=f"{VERSIONED_BASE_URL}/email_accounts/other/{ACCOUNT_EMAIL_ID}",
json=EMAIL_ACCOUNT,
)
api_client = HuntflowAPI(BASE_URL, token_proxy=token_proxy)

api_request = OtherEmailAccountRequest(**IMAP_EMAIL_ACCOUNT_CREATE_REQUEST)
settings = UserSettings(api_client)

response = await settings.update_other_email_account(api_request, ACCOUNT_EMAIL_ID)
assert response == EmailAccount(**EMAIL_ACCOUNT)


async def test_delete_email_account(
httpx_mock: HTTPXMock,
token_proxy: HuntflowTokenProxy,
) -> None:
httpx_mock.add_response(
url=f"{VERSIONED_BASE_URL}/email_accounts/{ACCOUNT_EMAIL_ID}",
json=DELETE_ACCOUNT_EMAIL_RESPONSE,
)
api_client = HuntflowAPI(BASE_URL, token_proxy=token_proxy)
settings = UserSettings(api_client)

response = await settings.delete_email_account(ACCOUNT_EMAIL_ID)
assert response == StatusResponse(**DELETE_ACCOUNT_EMAIL_RESPONSE)