Skip to content

Commit aeb9888

Browse files
authored
chore: created http client, serialiser (#1089)
created request, response, client, http client for RC release which is required by auto generated code.
1 parent 9515dce commit aeb9888

19 files changed

+521
-3
lines changed

.github/workflows/test-and-deploy.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
timeout-minutes: 20
1818
strategy:
1919
matrix:
20-
python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11' ]
20+
python-version: ['3.8', '3.9', '3.10', '3.11' ]
2121
env:
2222
DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }}
2323
steps:
@@ -31,6 +31,11 @@ jobs:
3131
username: ${{ secrets.DOCKER_USERNAME }}
3232
password: ${{ secrets.DOCKER_AUTH_TOKEN }}
3333

34+
- name: Install Docker Compose
35+
run: |
36+
sudo apt-get update
37+
sudo apt-get install -y docker-compose
38+
3439
- name: Build & Test
3540
run: make test-docker version=${{ matrix.python-version }}
3641

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ example.pdf
2727
TODO.txt
2828
twilio.env
2929
prism*
30+
**/.openapi-generator*

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (C) 2023, Twilio SendGrid, Inc. <[email protected]>
3+
Copyright (C) 2024, Twilio SendGrid, Inc. <[email protected]>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy of
66
this software and associated documentation files (the "Software"), to deal in

requirements.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
Flask==1.1.2
1+
#Flask==1.1.2
2+
requests>=2.31.0
3+
#aiohttp>=3.9.4
4+
#aiohttp-retry>=2.8.3
5+
26
PyYAML>=4.2b1
37
python-http-client>=3.2.1
48
six==1.11.0

sendgrid/base/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# __init__.py

sendgrid/base/auth_strategy.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Handle different of authentications, Currently sendgrid authenticate using apikey.
2+
# class AuthStrategy:
3+
# def authenticate(self):
4+
# print('Not yet implemented')
5+
#
6+
#
7+
# class ApiKeyAuthStrategy(AuthStrategy):
8+
# def __init__(self, api_key):
9+
# self.api_key = api_key
10+
# print('init ApiKeyAuthStrategy')
11+
# def authenticate(self, api_key):
12+
# print(f"Authenticating {api_key} using Token Authentication.")

sendgrid/base/client_base.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class ClientBase:
2+
3+
def __init__(self):
4+
print("Creating ClientBase class")
5+
6+
def request(self):
7+
print("Making request")

sendgrid/base/url_builder.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def build_url(url: str, region: str) -> str:
2+
base_url = "https://api.sendgrid.com"
3+
4+
if region and isinstance(region, str):
5+
new_url = f"https://api.{region}.sendgrid.com"
6+
else:
7+
new_url = base_url
8+
9+
# Ensure that there's a '/' before appending the url
10+
if not new_url.endswith('/'):
11+
new_url += '/'
12+
13+
new_url += url.lstrip('/')
14+
15+
return new_url
16+

sendgrid/base/values.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from typing import Dict
2+
3+
unset = object()
4+
5+
6+
def of(d: Dict[str, object]) -> Dict[str, object]:
7+
"""
8+
Remove unset values from a dict.
9+
10+
:param d: A dict to strip.
11+
:return A dict with unset values removed.
12+
"""
13+
return {k: v for k, v in d.items() if v != unset}

sendgrid/client.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import List, Optional
2+
from sendgrid.http.http_client import SendgridHttpClient, HttpClient
3+
from sendgrid.http.request import Request
4+
from sendgrid.base.url_builder import build_url
5+
6+
# class AuthStrategy:
7+
# def authenticate(self):
8+
# pass
9+
#
10+
#
11+
# class ApiKeyAuthStrategy(AuthStrategy):
12+
# def __init__(self, api_key):
13+
# self.api_key = api_key
14+
#
15+
# def authenticate(
16+
# self,
17+
# headers: Optional[Dict[str, str]] = None
18+
# ):
19+
# headers["Authorization"] = f"Bearer {self.api_key}"
20+
#
21+
22+
23+
class Client:
24+
def __init__(
25+
self,
26+
api_key: str,
27+
region: Optional[str] = None,
28+
edge: Optional[str] = None,
29+
http_client: Optional[HttpClient] = None,
30+
user_agent_extensions: Optional[List[str]] = None,
31+
):
32+
self.api_key = api_key
33+
self.region = region
34+
self.edge = edge
35+
self.user_agent_extensions = user_agent_extensions or []
36+
self.http_client: SendgridHttpClient = SendgridHttpClient()
37+
38+
def send(self, request: Request):
39+
url = build_url(request.url, self.region)
40+
response = self.http_client.request(
41+
method=request.method,
42+
url=url,
43+
data=request.data,
44+
headers=request.headers,
45+
api_key=self.api_key,
46+
)
47+
return response

sendgrid/converters/__init__.py

Whitespace-only changes.

sendgrid/converters/serialize.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from enum import Enum
2+
3+
from enum import Enum
4+
5+
6+
def to_serializable(obj):
7+
if isinstance(obj, list):
8+
return [
9+
to_serializable(item) for item in obj if item is not None
10+
] # Remove None from lists
11+
elif isinstance(obj, dict):
12+
return {
13+
key: to_serializable(value)
14+
for key, value in obj.items()
15+
if value is not None
16+
} # Remove None from dicts
17+
elif hasattr(obj, "to_dict"):
18+
return obj.to_dict()
19+
elif isinstance(obj, Enum):
20+
return obj.value
21+
else:
22+
return obj
23+
24+
25+
def from_serializable(data, cls=None):
26+
"""
27+
Converts a dictionary or list into a class instance or a list of instances.
28+
If `cls` is provided, it will instantiate the class using the dictionary values.
29+
"""
30+
if isinstance(data, list):
31+
return [
32+
from_serializable(item, cls) for item in data
33+
] # Recursively handle lists
34+
elif isinstance(data, dict):
35+
if cls:
36+
# If a class is provided, instantiate it using the dictionary
37+
return cls(**{key: from_serializable(value) for key, value in data.items()})
38+
else:
39+
return {
40+
key: from_serializable(value) for key, value in data.items()
41+
} # Recursively handle dicts
42+
else:
43+
return data # Return primitive types as is

sendgrid/exceptions/__init__.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Any, Dict
2+
3+
4+
class SendgridException(Exception):
5+
pass
6+
7+
8+
class ApiException(SendgridException):
9+
def __init__(self, status_code: int, error: Any, headers: Dict[str, Any] = None):
10+
self.status_code = status_code
11+
self.error = error
12+
self.headers = headers or {}
13+
14+
def __str__(self):
15+
return f"ApiException(status_code={self.status_code}, error={self.error}, headers={self.headers})"

sendgrid/http/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)