Skip to content
Draft
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
20 changes: 20 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Configuration for known file extensions
[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{json,yml,yaml,rst,md}]
indent_size = 2

# Do not configure editor for libs and autogenerated content
[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
charset = unset
end_of_line = unset
indent_size = unset
indent_style = unset
insert_final_newline = false
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions roulier/carriers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from . import chronopost_fr
from . import dpd_fr_soap
from . import dpd_fr
from . import dhl_express
from . import geodis_fr
from . import mondialrelay
from . import mondialrelay_fr
1 change: 1 addition & 0 deletions roulier/carriers/dhl_express/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import carrier
104 changes: 104 additions & 0 deletions roulier/carriers/dhl_express/carrier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Copyright 2026 Akretion (http://www.akretion.com).
# @author Florian Mounier <florian.mounier@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

import requests

from ...carrier import Carrier, action
from ...exception import CarrierError
from .schema import DHLExpressLabelInput, DHLExpressLabelOutput

_logger = logging.getLogger(__name__)


def int_maybe(value):
try:
return int(value)
except ValueError:
return value


class DHLExpress(Carrier):
__key__ = "dhl_express"
__url__ = "https://express.api.dhl.com/mydhlapi"
__url_test__ = "https://express.api.dhl.com/mydhlapi/test"

__ref__ = "https://developer.dhl.com/api-reference/mydhl-api-dhl-express"

def _get_url(self, is_test):
return self.__url_test__ if is_test else self.__url__

def _raise_for_status(self, response):
try:
response.raise_for_status()
except requests.exceptions.HTTPError as e:
try:
json = response.json()
if "message" in json:
id = 0
detail = json.get("detail", "")
if detail and ":" in detail and detail.split(":", 1)[0].isdigit():
id, detail = detail.split(":", 1)
id = int_maybe(id.strip())
detail = detail.strip()
msg = [
{
"id": id,
"message": f"{json['message']} ({detail})",
}
]

if "additionalDetails" in json:
for additional_detail in json["additionalDetails"]:
if (
"message" in additional_detail
and ":" in additional_detail["message"]
and additional_detail["message"]
.split(":", 1)[0]
.isdigit()
):
id, message = additional_detail["message"].split(":", 1)
id = int_maybe(id.strip())
message = message.strip()
else:
id = 0
message = additional_detail.get("message", "")
msg.append(
{
"id": id,
"message": message,
}
)
else:
raise
except Exception:
msg = response.text

raise CarrierError(response, msg) from e

return response

def request(self, url, method, json):
json = json.copy()
headers = {
"Accept": "application/json",
"Authorization": json.pop("authorization"),
"Content-Type": "application/json",
"x-version": "3.1.0",
}
response = requests.post(f"{url}/{method}", json=json, headers=headers)
self._raise_for_status(response)
return response

def _parse_response(self, response):
return response.json()

@action
def get_label(self, input: DHLExpressLabelInput) -> DHLExpressLabelOutput:
url = self._get_url(input.auth.isTest)
params = input.params()
response = self.request(url, "shipments", params)
result = self._parse_response(response)
return DHLExpressLabelOutput.from_params(result, input)
Loading
Loading