hiptech-toolkit 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hiptech_toolkit-1.0.0/LICENSE +29 -0
- hiptech_toolkit-1.0.0/PKG-INFO +93 -0
- hiptech_toolkit-1.0.0/README.md +75 -0
- hiptech_toolkit-1.0.0/hiptech/__init__.py +2 -0
- hiptech_toolkit-1.0.0/hiptech/haravan/__init__.py +7 -0
- hiptech_toolkit-1.0.0/hiptech/haravan/connector.py +142 -0
- hiptech_toolkit-1.0.0/hiptech/http/__init__.py +7 -0
- hiptech_toolkit-1.0.0/hiptech/http/auth.py +119 -0
- hiptech_toolkit-1.0.0/hiptech/http/connector.py +109 -0
- hiptech_toolkit-1.0.0/hiptech/http/manager.py +45 -0
- hiptech_toolkit-1.0.0/hiptech_toolkit.egg-info/PKG-INFO +93 -0
- hiptech_toolkit-1.0.0/hiptech_toolkit.egg-info/SOURCES.txt +15 -0
- hiptech_toolkit-1.0.0/hiptech_toolkit.egg-info/dependency_links.txt +1 -0
- hiptech_toolkit-1.0.0/hiptech_toolkit.egg-info/requires.txt +1 -0
- hiptech_toolkit-1.0.0/hiptech_toolkit.egg-info/top_level.txt +1 -0
- hiptech_toolkit-1.0.0/pyproject.toml +32 -0
- hiptech_toolkit-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
HIPTECH PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 – Present
|
|
4
|
+
HIPTECH Solution Company Limited
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
This software and its accompanying documentation (the "Software")
|
|
8
|
+
are the exclusive and proprietary property of HIPTECH Solution
|
|
9
|
+
Company Limited.
|
|
10
|
+
|
|
11
|
+
No part of this Software may be copied, modified, reproduced,
|
|
12
|
+
distributed, sublicensed, or transmitted in any form or by any
|
|
13
|
+
means, without the prior written permission of HIPTECH Solution
|
|
14
|
+
Company Limited.
|
|
15
|
+
|
|
16
|
+
The Software is provided "AS IS", without warranty of any kind,
|
|
17
|
+
express or implied, including but not limited to warranties of
|
|
18
|
+
merchantability, fitness for a particular purpose, and
|
|
19
|
+
non-infringement.
|
|
20
|
+
|
|
21
|
+
In no event shall HIPTECH Solution Company Limited be liable for any
|
|
22
|
+
claim, damages, or other liability, whether in an action of contract,
|
|
23
|
+
tort, or otherwise, arising from, out of, or in connection with the
|
|
24
|
+
Software or its use.
|
|
25
|
+
|
|
26
|
+
For licensing or usage inquiries, please contact:
|
|
27
|
+
|
|
28
|
+
Email: hiptech@hiptechvn.com
|
|
29
|
+
Website: https://hiptechvn.com
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hiptech-toolkit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Reusable HTTP and authentication toolkit by HIPTECH
|
|
5
|
+
Author-email: HIPTECH Solution Company Limited <hiptech@hiptechvn.com>
|
|
6
|
+
Maintainer-email: Bùi Đức Tuấn <tuanbd@hiptechvn.com>
|
|
7
|
+
Project-URL: Homepage, https://hiptechvn.com
|
|
8
|
+
Project-URL: Repository, https://github.com/buiductuan-hiptech/toolkit.git
|
|
9
|
+
Project-URL: Issues, https://github.com/buiductuan-hiptech/toolkit.git/issues
|
|
10
|
+
Keywords: http,api,toolkit,integration,hiptech
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: requests>=2.28
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="./assets/icon.png" width="160" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<h1 align="center">HIPTECH Toolkit</h1>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
Internal Python toolkit by HIPTECH Solution Company Limited
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
[](https://www.odoo.com)
|
|
30
|
+
[]()
|
|
31
|
+
[]()
|
|
32
|
+
|
|
33
|
+
**HIPTECH Toolkit** is a proprietary Python utility library developed and owned by
|
|
34
|
+
**HIPTECH Solution Company Limited**.
|
|
35
|
+
|
|
36
|
+
The toolkit provides standardized, reusable components for internal software
|
|
37
|
+
development, including Odoo modules, backend services, and third-party system
|
|
38
|
+
integrations.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Purpose
|
|
43
|
+
|
|
44
|
+
The purpose of this library is to:
|
|
45
|
+
|
|
46
|
+
- Centralize common technical utilities
|
|
47
|
+
- Enforce consistent API integration patterns
|
|
48
|
+
- Reduce duplicated code across projects
|
|
49
|
+
- Improve maintainability and code quality
|
|
50
|
+
|
|
51
|
+
This package is intended **strictly for internal or licensed use**.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- HTTP client abstraction with retry and timeout handling
|
|
58
|
+
- Authentication helpers (Bearer, custom headers, signatures)
|
|
59
|
+
- Manager and service layers for external API integrations
|
|
60
|
+
- Designed for enterprise and Odoo-based architectures
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Package Structure
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
hiptech/
|
|
68
|
+
└── http/
|
|
69
|
+
├── __init__.py
|
|
70
|
+
├── auth.py
|
|
71
|
+
├── connector.py
|
|
72
|
+
└── manager.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 🧩 Usage Example
|
|
76
|
+
|
|
77
|
+
```py
|
|
78
|
+
|
|
79
|
+
from hiptech.http import ConnectorRegistry
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# New Connection
|
|
83
|
+
conn = ConnectorRegistry(
|
|
84
|
+
base_url="https://api.example.com",
|
|
85
|
+
token="YOUR_TOKEN"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
response = manager.get("/v1/users")
|
|
89
|
+
|
|
90
|
+
# Cleanup connection
|
|
91
|
+
ConnectorRegistry.cleanup()
|
|
92
|
+
|
|
93
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./assets/icon.png" width="160" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">HIPTECH Toolkit</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Internal Python toolkit by HIPTECH Solution Company Limited
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
[](https://www.odoo.com)
|
|
12
|
+
[]()
|
|
13
|
+
[]()
|
|
14
|
+
|
|
15
|
+
**HIPTECH Toolkit** is a proprietary Python utility library developed and owned by
|
|
16
|
+
**HIPTECH Solution Company Limited**.
|
|
17
|
+
|
|
18
|
+
The toolkit provides standardized, reusable components for internal software
|
|
19
|
+
development, including Odoo modules, backend services, and third-party system
|
|
20
|
+
integrations.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Purpose
|
|
25
|
+
|
|
26
|
+
The purpose of this library is to:
|
|
27
|
+
|
|
28
|
+
- Centralize common technical utilities
|
|
29
|
+
- Enforce consistent API integration patterns
|
|
30
|
+
- Reduce duplicated code across projects
|
|
31
|
+
- Improve maintainability and code quality
|
|
32
|
+
|
|
33
|
+
This package is intended **strictly for internal or licensed use**.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- HTTP client abstraction with retry and timeout handling
|
|
40
|
+
- Authentication helpers (Bearer, custom headers, signatures)
|
|
41
|
+
- Manager and service layers for external API integrations
|
|
42
|
+
- Designed for enterprise and Odoo-based architectures
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Package Structure
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
hiptech/
|
|
50
|
+
└── http/
|
|
51
|
+
├── __init__.py
|
|
52
|
+
├── auth.py
|
|
53
|
+
├── connector.py
|
|
54
|
+
└── manager.py
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 🧩 Usage Example
|
|
58
|
+
|
|
59
|
+
```py
|
|
60
|
+
|
|
61
|
+
from hiptech.http import ConnectorRegistry
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# New Connection
|
|
65
|
+
conn = ConnectorRegistry(
|
|
66
|
+
base_url="https://api.example.com",
|
|
67
|
+
token="YOUR_TOKEN"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
response = manager.get("/v1/users")
|
|
71
|
+
|
|
72
|
+
# Cleanup connection
|
|
73
|
+
ConnectorRegistry.cleanup()
|
|
74
|
+
|
|
75
|
+
```
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Proprietary and Confidential
|
|
2
|
+
# Copyright (c) HIPTECH Solution Company Limited
|
|
3
|
+
|
|
4
|
+
# -*- coding: utf-8 -*-
|
|
5
|
+
import time
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Optional, Dict
|
|
8
|
+
import requests
|
|
9
|
+
from ..http.connector import BaseAPIConnector
|
|
10
|
+
|
|
11
|
+
_logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HaravanAPIConnector(BaseAPIConnector):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
base_url: str,
|
|
18
|
+
timeout: int = 30,
|
|
19
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
20
|
+
retries: int = 3,
|
|
21
|
+
backoff_factor: float = 0.5,
|
|
22
|
+
auth=None,
|
|
23
|
+
rate_limit_retry: bool = True,
|
|
24
|
+
rate_limit_max_wait: int = 60,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Initialize Haravan API Connector
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
base_url: Base URL for the API
|
|
31
|
+
timeout: Request timeout in seconds
|
|
32
|
+
default_headers: Default headers for all requests
|
|
33
|
+
retries: Number of retries on error
|
|
34
|
+
backoff_factor: Backoff factor for retry
|
|
35
|
+
auth: Authentication strategy
|
|
36
|
+
rate_limit_retry: Whether to retry on rate limit (429)
|
|
37
|
+
rate_limit_max_wait: Maximum wait time for rate limit (seconds)
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(
|
|
40
|
+
base_url=base_url,
|
|
41
|
+
timeout=timeout,
|
|
42
|
+
default_headers=default_headers,
|
|
43
|
+
retries=retries,
|
|
44
|
+
backoff_factor=backoff_factor,
|
|
45
|
+
auth=auth,
|
|
46
|
+
)
|
|
47
|
+
self.rate_limit_retry = rate_limit_retry
|
|
48
|
+
self.rate_limit_max_wait = rate_limit_max_wait
|
|
49
|
+
|
|
50
|
+
def _handle_rate_limit(self, response: requests.Response) -> Optional[int]:
|
|
51
|
+
"""
|
|
52
|
+
Handle rate limit response by reading headers from 429 response
|
|
53
|
+
"""
|
|
54
|
+
if response.status_code != 429:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
if not self.rate_limit_retry:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
retry_after = response.headers.get("Retry-After")
|
|
61
|
+
if retry_after:
|
|
62
|
+
try:
|
|
63
|
+
wait_time = int(retry_after)
|
|
64
|
+
# Cap at max_wait
|
|
65
|
+
wait_time = min(wait_time, self.rate_limit_max_wait)
|
|
66
|
+
_logger.warning(
|
|
67
|
+
"Rate limit hit (429), waiting %d seconds from Retry-After header: %s",
|
|
68
|
+
wait_time,
|
|
69
|
+
retry_after
|
|
70
|
+
)
|
|
71
|
+
return wait_time
|
|
72
|
+
except (ValueError, TypeError) as e:
|
|
73
|
+
_logger.warning(
|
|
74
|
+
"Invalid Retry-After header value: %s, error: %s",
|
|
75
|
+
retry_after,
|
|
76
|
+
e
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Default wait time if no header available
|
|
80
|
+
default_wait = 5
|
|
81
|
+
_logger.warning(
|
|
82
|
+
"Rate limit hit (429), no Retry-After header found. "
|
|
83
|
+
"Using default wait time: %d seconds. Headers: %s",
|
|
84
|
+
default_wait,
|
|
85
|
+
dict(response.headers)
|
|
86
|
+
)
|
|
87
|
+
return default_wait
|
|
88
|
+
|
|
89
|
+
def request(
|
|
90
|
+
self,
|
|
91
|
+
method: str,
|
|
92
|
+
endpoint: str,
|
|
93
|
+
*,
|
|
94
|
+
params=None,
|
|
95
|
+
data=None,
|
|
96
|
+
json=None,
|
|
97
|
+
headers=None,
|
|
98
|
+
timeout=None,
|
|
99
|
+
max_rate_limit_retries: int = 3,
|
|
100
|
+
):
|
|
101
|
+
"""
|
|
102
|
+
Make HTTP request with rate limit handling
|
|
103
|
+
"""
|
|
104
|
+
# Retry loop for rate limiting
|
|
105
|
+
rate_limit_retries = 0
|
|
106
|
+
resp = None
|
|
107
|
+
while rate_limit_retries <= max_rate_limit_retries:
|
|
108
|
+
resp = super().request(
|
|
109
|
+
method=method,
|
|
110
|
+
endpoint=endpoint,
|
|
111
|
+
params=params,
|
|
112
|
+
data=data,
|
|
113
|
+
json=json,
|
|
114
|
+
headers=headers,
|
|
115
|
+
timeout=timeout,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Handle rate limit - read wait time from headers
|
|
119
|
+
if resp.status_code == 429:
|
|
120
|
+
wait_time = self._handle_rate_limit(resp)
|
|
121
|
+
if wait_time and rate_limit_retries < max_rate_limit_retries:
|
|
122
|
+
time.sleep(wait_time)
|
|
123
|
+
rate_limit_retries += 1
|
|
124
|
+
continue
|
|
125
|
+
else:
|
|
126
|
+
# Max retries reached or should not retry
|
|
127
|
+
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
128
|
+
_logger.error(
|
|
129
|
+
"Rate limit exceeded after %d retries: %s %s",
|
|
130
|
+
rate_limit_retries,
|
|
131
|
+
method.upper(),
|
|
132
|
+
url
|
|
133
|
+
)
|
|
134
|
+
# Raise error for rate limit
|
|
135
|
+
resp.raise_for_status()
|
|
136
|
+
return resp
|
|
137
|
+
|
|
138
|
+
return resp
|
|
139
|
+
|
|
140
|
+
if resp is None:
|
|
141
|
+
raise RuntimeError("No response received after rate limit retries")
|
|
142
|
+
return resp
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Proprietary and Confidential
|
|
2
|
+
# Copyright (c) HIPTECH Solution Company Limited
|
|
3
|
+
|
|
4
|
+
# -*- coding: utf-8 -*-
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
import base64
|
|
7
|
+
from typing import Dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseAuthStrategy(ABC):
|
|
11
|
+
"""
|
|
12
|
+
Base authentication strategy interface.
|
|
13
|
+
|
|
14
|
+
All authentication strategies must implement `get_headers`.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def get_headers(self) -> Dict[str, str]:
|
|
19
|
+
"""
|
|
20
|
+
Return HTTP headers used for authentication.
|
|
21
|
+
"""
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NoAuthStrategy(BaseAuthStrategy):
|
|
26
|
+
"""
|
|
27
|
+
No authentication.
|
|
28
|
+
Useful for public or internal APIs.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def get_headers(self) -> Dict[str, str]:
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BasicAuthStrategy(BaseAuthStrategy):
|
|
36
|
+
"""
|
|
37
|
+
HTTP Basic Authentication strategy.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, username: str, password: str):
|
|
41
|
+
self.username = username
|
|
42
|
+
self.password = password
|
|
43
|
+
|
|
44
|
+
def get_headers(self) -> Dict[str, str]:
|
|
45
|
+
raw = f"{self.username}:{self.password}".encode("utf-8")
|
|
46
|
+
token = base64.b64encode(raw).decode("utf-8")
|
|
47
|
+
return {
|
|
48
|
+
"Authorization": f"Basic {token}",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BearerAuthStrategy(BaseAuthStrategy):
|
|
53
|
+
"""
|
|
54
|
+
Bearer token authentication strategy (JWT, OAuth access token, etc).
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, token: str):
|
|
58
|
+
self.token = token
|
|
59
|
+
|
|
60
|
+
def get_headers(self) -> Dict[str, str]:
|
|
61
|
+
return {
|
|
62
|
+
"Authorization": f"Bearer {self.token}",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ApiKeyAuthStrategy(BaseAuthStrategy):
|
|
67
|
+
"""
|
|
68
|
+
API Key authentication strategy.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, api_key: str, header_name: str = "X-API-KEY"):
|
|
72
|
+
self.api_key = api_key
|
|
73
|
+
self.header_name = header_name
|
|
74
|
+
|
|
75
|
+
def get_headers(self) -> Dict[str, str]:
|
|
76
|
+
return {
|
|
77
|
+
self.header_name: self.api_key,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class OAuth2AuthStrategy(BaseAuthStrategy):
|
|
82
|
+
"""
|
|
83
|
+
OAuth2 authentication strategy.
|
|
84
|
+
|
|
85
|
+
Note:
|
|
86
|
+
Token refresh / grant flow should be implemented internally
|
|
87
|
+
and NOT exposed in the public toolkit API.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, access_token: str):
|
|
91
|
+
self.access_token = access_token
|
|
92
|
+
|
|
93
|
+
def get_headers(self) -> Dict[str, str]:
|
|
94
|
+
return {
|
|
95
|
+
"Authorization": f"Bearer {self.access_token}",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class CustomHeaderAuthStrategy(BaseAuthStrategy):
|
|
100
|
+
"""
|
|
101
|
+
Custom header-based authentication strategy.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, headers: Dict[str, str]):
|
|
105
|
+
self.headers = dict(headers)
|
|
106
|
+
|
|
107
|
+
def get_headers(self) -> Dict[str, str]:
|
|
108
|
+
return dict(self.headers)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
__all__ = [
|
|
112
|
+
"BaseAuthStrategy",
|
|
113
|
+
"NoAuthStrategy",
|
|
114
|
+
"BasicAuthStrategy",
|
|
115
|
+
"BearerAuthStrategy",
|
|
116
|
+
"ApiKeyAuthStrategy",
|
|
117
|
+
"OAuth2AuthStrategy",
|
|
118
|
+
"CustomHeaderAuthStrategy",
|
|
119
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Proprietary and Confidential
|
|
2
|
+
# Copyright (c) HIPTECH Solution Company Limited
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# -*- coding: utf-8 -*-
|
|
6
|
+
import time
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
import requests
|
|
10
|
+
from requests.adapters import HTTPAdapter
|
|
11
|
+
from urllib3.util.retry import Retry
|
|
12
|
+
|
|
13
|
+
_logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseAPIConnector:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
base_url: str,
|
|
20
|
+
timeout: int = 30,
|
|
21
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
22
|
+
retries: int = 3,
|
|
23
|
+
backoff_factor: float = 0.5,
|
|
24
|
+
auth=None,
|
|
25
|
+
):
|
|
26
|
+
self.base_url = base_url.rstrip("/")
|
|
27
|
+
self.timeout = timeout
|
|
28
|
+
self.default_headers = default_headers or {}
|
|
29
|
+
self.auth = auth
|
|
30
|
+
|
|
31
|
+
self.session = requests.Session()
|
|
32
|
+
self._init_retry(retries, backoff_factor)
|
|
33
|
+
|
|
34
|
+
def _init_retry(self, retries: int, backoff_factor: float):
|
|
35
|
+
retry = Retry(
|
|
36
|
+
total=retries,
|
|
37
|
+
read=retries,
|
|
38
|
+
connect=retries,
|
|
39
|
+
status=retries,
|
|
40
|
+
backoff_factor=backoff_factor,
|
|
41
|
+
status_forcelist=(429, 500, 502, 503, 504),
|
|
42
|
+
allowed_methods=frozenset(
|
|
43
|
+
["GET", "POST", "PUT", "DELETE", "PATCH"]),
|
|
44
|
+
raise_on_status=False,
|
|
45
|
+
)
|
|
46
|
+
adapter = HTTPAdapter(
|
|
47
|
+
max_retries=retry, pool_connections=10, pool_maxsize=10)
|
|
48
|
+
self.session.mount("http://", adapter)
|
|
49
|
+
self.session.mount("https://", adapter)
|
|
50
|
+
|
|
51
|
+
def build_headers(self) -> Dict[str, str]:
|
|
52
|
+
headers = {}
|
|
53
|
+
headers.update(self.default_headers)
|
|
54
|
+
|
|
55
|
+
if self.auth:
|
|
56
|
+
headers.update(self.auth.get_headers())
|
|
57
|
+
|
|
58
|
+
return headers
|
|
59
|
+
|
|
60
|
+
def request(
|
|
61
|
+
self,
|
|
62
|
+
method: str,
|
|
63
|
+
endpoint: str,
|
|
64
|
+
*,
|
|
65
|
+
params=None,
|
|
66
|
+
data=None,
|
|
67
|
+
json=None,
|
|
68
|
+
headers=None,
|
|
69
|
+
timeout=None,
|
|
70
|
+
):
|
|
71
|
+
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
72
|
+
|
|
73
|
+
req_headers = self.build_headers()
|
|
74
|
+
if headers:
|
|
75
|
+
req_headers.update(headers)
|
|
76
|
+
|
|
77
|
+
start = time.time()
|
|
78
|
+
resp = self.session.request(
|
|
79
|
+
method=method.upper(),
|
|
80
|
+
url=url,
|
|
81
|
+
params=params,
|
|
82
|
+
data=data,
|
|
83
|
+
json=json,
|
|
84
|
+
headers=req_headers,
|
|
85
|
+
timeout=timeout or self.timeout,
|
|
86
|
+
)
|
|
87
|
+
elapsed = time.time() - start
|
|
88
|
+
|
|
89
|
+
_logger.info(
|
|
90
|
+
"API %s %s -> %s (%.2fs)",
|
|
91
|
+
method.upper(),
|
|
92
|
+
url,
|
|
93
|
+
resp.status_code,
|
|
94
|
+
elapsed,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return resp
|
|
98
|
+
|
|
99
|
+
def get(self, endpoint, **kw):
|
|
100
|
+
return self.request("GET", endpoint, **kw)
|
|
101
|
+
|
|
102
|
+
def post(self, endpoint, **kw):
|
|
103
|
+
return self.request("POST", endpoint, **kw)
|
|
104
|
+
|
|
105
|
+
def put(self, endpoint, **kw):
|
|
106
|
+
return self.request("PUT", endpoint, **kw)
|
|
107
|
+
|
|
108
|
+
def delete(self, endpoint, **kw):
|
|
109
|
+
return self.request("DELETE", endpoint, **kw)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Proprietary and Confidential
|
|
2
|
+
# Copyright (c) HIPTECH Solution Company Limited
|
|
3
|
+
|
|
4
|
+
# -*- coding: utf-8 -*-
|
|
5
|
+
import time
|
|
6
|
+
from threading import Lock
|
|
7
|
+
from .connector import BaseAPIConnector
|
|
8
|
+
from .auth import BasicAuthStrategy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConnectorRegistry:
|
|
12
|
+
_lock = Lock()
|
|
13
|
+
_cache = {}
|
|
14
|
+
_last_used = {}
|
|
15
|
+
_TTL = 60 * 30
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def factory(cls, base_url, username, password):
|
|
19
|
+
key = (base_url, username)
|
|
20
|
+
|
|
21
|
+
now = time.time()
|
|
22
|
+
with cls._lock:
|
|
23
|
+
conn = cls._cache.get(key)
|
|
24
|
+
|
|
25
|
+
if not conn:
|
|
26
|
+
auth = BasicAuthStrategy(username, password)
|
|
27
|
+
conn = BaseAPIConnector(base_url=base_url, auth=auth)
|
|
28
|
+
cls._cache[key] = conn
|
|
29
|
+
|
|
30
|
+
cls._last_used[key] = now
|
|
31
|
+
return conn
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def cleanup(cls):
|
|
35
|
+
now = time.time()
|
|
36
|
+
with cls._lock:
|
|
37
|
+
for key in list(cls._cache.keys()):
|
|
38
|
+
last = cls._last_used.get(key, 0)
|
|
39
|
+
if now - last > cls._TTL:
|
|
40
|
+
conn = cls._cache.pop(key)
|
|
41
|
+
cls._last_used.pop(key, None)
|
|
42
|
+
try:
|
|
43
|
+
conn.session.close()
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hiptech-toolkit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Reusable HTTP and authentication toolkit by HIPTECH
|
|
5
|
+
Author-email: HIPTECH Solution Company Limited <hiptech@hiptechvn.com>
|
|
6
|
+
Maintainer-email: Bùi Đức Tuấn <tuanbd@hiptechvn.com>
|
|
7
|
+
Project-URL: Homepage, https://hiptechvn.com
|
|
8
|
+
Project-URL: Repository, https://github.com/buiductuan-hiptech/toolkit.git
|
|
9
|
+
Project-URL: Issues, https://github.com/buiductuan-hiptech/toolkit.git/issues
|
|
10
|
+
Keywords: http,api,toolkit,integration,hiptech
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: requests>=2.28
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="./assets/icon.png" width="160" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<h1 align="center">HIPTECH Toolkit</h1>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
Internal Python toolkit by HIPTECH Solution Company Limited
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
[](https://www.odoo.com)
|
|
30
|
+
[]()
|
|
31
|
+
[]()
|
|
32
|
+
|
|
33
|
+
**HIPTECH Toolkit** is a proprietary Python utility library developed and owned by
|
|
34
|
+
**HIPTECH Solution Company Limited**.
|
|
35
|
+
|
|
36
|
+
The toolkit provides standardized, reusable components for internal software
|
|
37
|
+
development, including Odoo modules, backend services, and third-party system
|
|
38
|
+
integrations.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Purpose
|
|
43
|
+
|
|
44
|
+
The purpose of this library is to:
|
|
45
|
+
|
|
46
|
+
- Centralize common technical utilities
|
|
47
|
+
- Enforce consistent API integration patterns
|
|
48
|
+
- Reduce duplicated code across projects
|
|
49
|
+
- Improve maintainability and code quality
|
|
50
|
+
|
|
51
|
+
This package is intended **strictly for internal or licensed use**.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- HTTP client abstraction with retry and timeout handling
|
|
58
|
+
- Authentication helpers (Bearer, custom headers, signatures)
|
|
59
|
+
- Manager and service layers for external API integrations
|
|
60
|
+
- Designed for enterprise and Odoo-based architectures
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Package Structure
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
hiptech/
|
|
68
|
+
└── http/
|
|
69
|
+
├── __init__.py
|
|
70
|
+
├── auth.py
|
|
71
|
+
├── connector.py
|
|
72
|
+
└── manager.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 🧩 Usage Example
|
|
76
|
+
|
|
77
|
+
```py
|
|
78
|
+
|
|
79
|
+
from hiptech.http import ConnectorRegistry
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# New Connection
|
|
83
|
+
conn = ConnectorRegistry(
|
|
84
|
+
base_url="https://api.example.com",
|
|
85
|
+
token="YOUR_TOKEN"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
response = manager.get("/v1/users")
|
|
89
|
+
|
|
90
|
+
# Cleanup connection
|
|
91
|
+
ConnectorRegistry.cleanup()
|
|
92
|
+
|
|
93
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
hiptech/__init__.py
|
|
5
|
+
hiptech/haravan/__init__.py
|
|
6
|
+
hiptech/haravan/connector.py
|
|
7
|
+
hiptech/http/__init__.py
|
|
8
|
+
hiptech/http/auth.py
|
|
9
|
+
hiptech/http/connector.py
|
|
10
|
+
hiptech/http/manager.py
|
|
11
|
+
hiptech_toolkit.egg-info/PKG-INFO
|
|
12
|
+
hiptech_toolkit.egg-info/SOURCES.txt
|
|
13
|
+
hiptech_toolkit.egg-info/dependency_links.txt
|
|
14
|
+
hiptech_toolkit.egg-info/requires.txt
|
|
15
|
+
hiptech_toolkit.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.28
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hiptech
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hiptech-toolkit"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Reusable HTTP and authentication toolkit by HIPTECH"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "HIPTECH Solution Company Limited", email = "hiptech@hiptechvn.com" }]
|
|
13
|
+
maintainers = [
|
|
14
|
+
{name = "Bùi Đức Tuấn", email="tuanbd@hiptechvn.com"},
|
|
15
|
+
]
|
|
16
|
+
keywords = ["http", "api", "toolkit", "integration", "hiptech"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"requests>=2.28",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
where = ["."]
|
|
27
|
+
include = ["hiptech*"]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://hiptechvn.com"
|
|
31
|
+
Repository = "https://github.com/buiductuan-hiptech/toolkit.git"
|
|
32
|
+
Issues = "https://github.com/buiductuan-hiptech/toolkit.git/issues"
|