Skip to content

Commit 33d6a05

Browse files
Update serialize/deserialize models in message_service.py:
* Update datetime timezone setting in string_date_transfer.py
1 parent 5687d84 commit 33d6a05

File tree

12 files changed

+198
-36
lines changed

12 files changed

+198
-36
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ build/
3333
*.bak
3434
*.swp
3535
*.swo
36+
debug
3637

3738
# ruff
3839
.ruff_cache/

examples/sms/sms.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from solapi.message_service import SolapiMessageService
22
from solapi.model.message import Message
33

4-
test = SolapiMessageService(api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET")
5-
test2 = Message(from_="01012345678", to="01012345678", text="test")
6-
test.send(test2)
4+
message_service = SolapiMessageService(
5+
api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET"
6+
)
7+
message = Message(from_="등록한 발신번호", to="01000000000", text="Hello World!")
8+
response = message_service.send(message)
9+
print(response)

solapi/lib/fetcher.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,11 @@ def default_fetcher(
4747
)
4848

4949
# 4xx 에러 처리: 클라이언트 오류일 경우
50-
# TODO: Custom Error 만들어야 함
5150
if 400 <= response.status_code < 500:
5251
error_response: dict[str, Any] = response.json()
5352
raise Exception(
5453
error_response.get("errorCode", "UnknownError"),
55-
error_response.get("errorMessage", ""),
54+
error_response.get("errorMessage", "An Error occurred"),
5655
)
5756
# 5xx 에러 처리: 서버 오류일 경우
5857
elif response.status_code >= 500:

solapi/lib/string_date_transfer.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
2-
from datetime import datetime
2+
import time
3+
from datetime import datetime, timedelta, timezone
34
from typing import Union
45

56

@@ -24,7 +25,9 @@ def format_iso(date: datetime) -> str:
2425
Returns:
2526
ISO 8601 형식의 문자열 (예: '2023-01-01T12:00:00Z')
2627
"""
27-
return date.isoformat()
28+
utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone
29+
utc_offset = timedelta(seconds=-utc_offset_sec)
30+
return date.replace(tzinfo=timezone(offset=utc_offset)).isoformat()
2831

2932

3033
def parse_iso(date_string: str) -> datetime:
@@ -83,7 +86,7 @@ def string_date_transfer(value: Union[str, datetime]):
8386
return value
8487

8588

86-
def format_with_transfer(value: str) -> str:
89+
def format_with_transfer(value: Union[str, datetime]) -> str:
8790
"""
8891
string_date_transfer와 format_iso를 한번에 실행하는 함수
8992

solapi/message_service.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime
12
from typing import Optional, Union
23

34
from solapi.lib.authenticator import AuthenticationParameter
@@ -8,12 +9,13 @@
89
SendMessageRequest,
910
SendRequestConfig,
1011
)
12+
from solapi.model.response.send_message_response import SendMessageResponse
1113

1214

1315
class SolapiMessageService:
1416
def __init__(self, api_key: str, api_secret: str):
15-
self.baseUrl = "https://api.solapi.com"
16-
self._auth_info: AuthenticationParameter = {
17+
self.base_url = "https://api.solapi.com"
18+
self.auth_info: AuthenticationParameter = {
1719
"api_key": api_key,
1820
"api_secret": api_secret,
1921
}
@@ -22,34 +24,55 @@ def send(
2224
self,
2325
messages: Union[list[Message], Message],
2426
request_config: Optional[SendRequestConfig] = None,
25-
):
27+
) -> SendMessageResponse:
2628
payload = []
2729
if isinstance(messages, Message):
2830
payload.append(messages)
2931
elif isinstance(messages, list):
32+
for message in messages:
33+
if isinstance(message, Message) is not True:
34+
raise TypeError(
35+
"The messages parameter must be an instance of Message."
36+
)
37+
3038
payload.extend(messages)
39+
else:
40+
raise TypeError("Invalid message type")
3141

3242
if len(payload) == 0:
33-
raise ValueError("데이터가 반드시 1건 이상 기입되어 있어야 합니다.")
43+
raise ValueError("The data must have at least one message.")
3444

3545
request = SendMessageRequest(messages=payload)
3646
if request_config is not None:
37-
request.app_id = request_config["app_id"]
38-
request.allow_duplicates = request_config["allow_duplicates"]
39-
request.show_message_list = request_config["show_message_list"]
47+
request.app_id = request_config.app_id
48+
request.allow_duplicates = request_config.allow_duplicates
49+
request.show_message_list = request_config.show_message_list
4050

41-
if request_config.scheduled_date != "":
51+
if (
52+
request_config.scheduled_date is not None
53+
and request_config.scheduled_date != ""
54+
and isinstance(request_config.scheduled_date, datetime)
55+
):
4256
request.scheduled_date = format_with_transfer(
4357
request_config.scheduled_date
4458
)
4559

46-
print(request.model_dump(exclude_none=True, by_alias=True))
47-
48-
return default_fetcher(
49-
self._auth_info,
60+
response = default_fetcher(
61+
self.auth_info,
5062
request={
5163
"method": RequestMethod.POST,
52-
"url": f"{self.baseUrl}/messages/v4/send-many/detail",
64+
"url": f"{self.base_url}/messages/v4/send-many/detail",
5365
},
5466
data=request.model_dump(exclude_none=True, by_alias=True),
5567
)
68+
deserialized_response: SendMessageResponse = SendMessageResponse.model_validate(
69+
response
70+
)
71+
72+
count = deserialized_response.count
73+
failed_messages = deserialized_response.failed_message_list
74+
registered_failed_count = count.registered_failed
75+
if len(failed_messages) > 0 and count.total == registered_failed_count:
76+
raise ValueError("an error occurred")
77+
78+
return deserialized_response

solapi/model/kakao/__init__.py

Whitespace-only changes.

solapi/model/kakao/kakao_option.py

Whitespace-only changes.

solapi/model/message.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from enum import Enum
33
from typing import Any, Optional
44

5-
from pydantic import BaseModel, Field
5+
from pydantic import BaseModel, ConfigDict, Field
66

77

88
class MessageType(Enum):
@@ -47,37 +47,55 @@ class FileIdsType(BaseModel):
4747

4848

4949
class Message(BaseModel):
50-
from_: Optional[str] = Field(default=None, serialization_alias="from")
50+
from_: Optional[str] = Field(
51+
default=None, serialization_alias="from", validation_alias="from"
52+
)
5153
to: str
5254
text: Optional[str] = None
53-
image_id: Optional[str] = Field(default=None, serialization_alias="imageId")
55+
image_id: Optional[str] = Field(
56+
default=None, serialization_alias="imageId", validation_alias="imageId"
57+
)
5458
country: str = "82"
55-
message_id: Optional[str] = Field(default=None, serialization_alias="messageId")
56-
group_id: Optional[str] = Field(default=None, serialization_alias="groupId")
59+
message_id: Optional[str] = Field(
60+
default=None, serialization_alias="messageId", validation_alias="messageId"
61+
)
62+
group_id: Optional[str] = Field(
63+
default=None, serialization_alias="groupId", validation_alias="groupId"
64+
)
5765
type: Optional[MessageType] = None
5866
auto_type_detect: Optional[bool] = Field(
59-
default=True, serialization_alias="autoTypeDetect"
67+
default=True,
68+
serialization_alias="autoTypeDetect",
69+
validation_alias="autoTypeDetect",
6070
)
6171
date_created: Optional[datetime] = Field(
62-
default=None, serialization_alias="dateCreated"
72+
default=None, serialization_alias="dateCreated", validation_alias="dateCreated"
6373
)
6474
date_updated: Optional[datetime] = Field(
65-
default=None, serialization_alias="dateUpdated"
75+
default=None, serialization_alias="dateUpdated", validation_alias="dateUpdated"
6676
)
6777
subject: Optional[str] = None
6878
log: Optional[list[dict[str, Any]]] = None
6979
replacements: Optional[list[dict[str, Any]]] = None
70-
status_code: Optional[str] = Field(default=None, serialization_alias="statusCode")
80+
status_code: Optional[str] = Field(
81+
default=None, serialization_alias="statusCode", validation_alias="statusCode"
82+
)
7183
custom_fields: Optional[dict[str, str]] = Field(
72-
default=None, serialization_alias="customFields"
84+
default=None,
85+
serialization_alias="customFields",
86+
validation_alias="customFields",
7387
)
7488
# TODO: kakaoOptions Model 정의해야 함..
7589
kakao_options: Optional[dict[str, Any]] = Field(
76-
default=None, serialization_alias="kakaoOptions"
90+
default=None,
91+
serialization_alias="kakaoOptions",
92+
validation_alias="kakaoOptions",
7793
)
7894
rcs_options: Optional[dict[str, Any]] = Field(
79-
default=None, serialization_alias="rcsOptions"
95+
default=None, serialization_alias="rcsOptions", validation_alias="rcsOptions"
8096
)
8197
fax_options: Optional[FileIdsType] = Field(
82-
default=None, serialization_alias="faxOptions"
98+
default=None, serialization_alias="faxOptions", validation_alias="faxOptions"
8399
)
100+
101+
model_config = ConfigDict(extra="ignore")

solapi/model/request/send_message_request.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import platform
2-
from typing import Optional
2+
from datetime import datetime
3+
from typing import Optional, Union
34

45
from pydantic import BaseModel, Field
56

@@ -12,7 +13,7 @@ class SendRequestConfig(BaseModel):
1213
show_message_list: bool = Field(
1314
default=False, serialization_alias="showMessageList"
1415
)
15-
scheduled_date: Optional[str] = Field(
16+
scheduled_date: Optional[Union[str, datetime]] = Field(
1617
default=None, serialization_alias="scheduledDate"
1718
)
1819

solapi/model/response/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)