Skip to content
Merged
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
35 changes: 11 additions & 24 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,27 @@ on: push
jobs:
test:
name: Python ${{ matrix.python-version }} tests
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
id: setup-python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 1.3.1
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v5
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: |
poetry install --no-interaction
run: uv sync --group dev
- name: Test with pytest
env:
API_KEY: ${{ secrets.MAILERLITE_API_KEY }}
run: |
source .venv/bin/activate
coverage run -m pytest tests/
coverage report
uv run coverage run -m pytest tests/
uv run coverage report
17 changes: 10 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ on:

jobs:
build-n-publish:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6

- name: Build and publish to pypi
uses: JRubics/poetry-publish@v2.1
with:
pypi_token: ${{ secrets.PYPI_API_TOKEN }}
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39
- name: Build and publish to PyPI
run: |
uv build
uv publish
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
4 changes: 2 additions & 2 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ remotes:
pre-commit:
parallel: true
commands:
black:
ruff:
glob: "*.py"
run: black .
run: ruff format .
1 change: 1 addition & 0 deletions mailerlite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from mailerlite.api_client import ApiClient
from mailerlite.sdk.automations import Automations
from mailerlite.sdk.batch import Batches

# import all api files into api package
from mailerlite.sdk.campaigns import Campaigns
from mailerlite.sdk.fields import Fields
Expand Down
48 changes: 26 additions & 22 deletions mailerlite/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
class HttpMethods(object):
"""Allowed Http methods"""

GET: str = 'get'
POST: str = 'post'
PUT: str = 'put'
DELETE: str = 'delete'
GET: str = "get"
POST: str = "post"
PUT: str = "put"
DELETE: str = "delete"

# Allowed methods
ALLOWED: Tuple[str, ...] = (GET, POST, PUT, DELETE)
Expand All @@ -31,21 +31,21 @@ def _is_method(cls, method: str, target: str) -> bool:

class ApiClient(object):
# Base URL of the API
HOST = 'https://connect.mailerlite.com/'
HOST = "https://connect.mailerlite.com/"

# Base headers
DEFAULT_HEADERS = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'MailerLite-Python-SDK-Client',
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": "MailerLite-Python-SDK-Client",
}

# Base timeout between requests
DEFAULT_TIMEOUT = 120

def __init__(self, config=None) -> None:
self.headers: dict = self.DEFAULT_HEADERS.copy()
self.api_key: str = ''
self.api_key: str = ""
self.api_version: Optional[str] = None
self.timeout: Optional[int] = None
if config is None:
Expand All @@ -56,35 +56,39 @@ def set_config(self, config: dict) -> None:
"""API Client Configuration Setter"""

# Authentication
self.api_key = config.get('api_key', '')
self.headers['Authorization'] = f'Bearer {self.api_key}'
self.api_key = config.get("api_key", "")
self.headers["Authorization"] = f"Bearer {self.api_key}"

# API version
self.api_version = config.get('api_version')
self.api_version = config.get("api_version")
if self.api_version is not None:
self.headers['X-Version'] = self.api_version
self.headers["X-Version"] = self.api_version

# Request timeout
self.timeout = config.get('timeout', self.DEFAULT_TIMEOUT)
self.timeout = config.get("timeout", self.DEFAULT_TIMEOUT)

def request(
self, method: str, path: str, query_params: Optional[dict] = None, body: Optional[dict] = None
self,
method: str,
path: str,
query_params: Optional[dict] = None,
body: Optional[dict] = None,
) -> requests.models.Response:
"""Requests Wrapper"""

method = method.lower()
if method not in HttpMethods.ALLOWED:
raise ValueError('http method must be `POST`, `GET`, `PUT` or `DELETE`.')
raise ValueError("http method must be `POST`, `GET`, `PUT` or `DELETE`.")

kwargs = {
'method': method,
'url': urljoin(self.HOST, path),
'params': query_params,
'headers': self.headers,
'timeout': self.timeout,
"method": method,
"url": urljoin(self.HOST, path),
"params": query_params,
"headers": self.headers,
"timeout": self.timeout,
}
if HttpMethods.is_get(method):
kwargs.update(allow_redirects=True)
if HttpMethods.is_post(method) or HttpMethods.is_put(method):
kwargs.update(data=json.dumps(body))
return requests.request(**kwargs)
return requests.request(**kwargs)
Loading