datagsm-openapi-sdk 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.
- datagsm_openapi_sdk-1.0.0/LICENSE +21 -0
- datagsm_openapi_sdk-1.0.0/PKG-INFO +55 -0
- datagsm_openapi_sdk-1.0.0/README.md +20 -0
- datagsm_openapi_sdk-1.0.0/pyproject.toml +107 -0
- datagsm_openapi_sdk-1.0.0/setup.cfg +4 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/__init__.py +61 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/_http.py +291 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/_json.py +53 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/__init__.py +18 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/_base.py +73 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/club.py +106 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/neis.py +141 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/project.py +103 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/api/student.py +125 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/client.py +118 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/exceptions.py +213 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/__init__.py +41 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/_common.py +37 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/club.py +76 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/enums.py +82 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/neis.py +102 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/project.py +76 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/models/student.py +75 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi/py.typed +1 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi_sdk.egg-info/PKG-INFO +55 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi_sdk.egg-info/SOURCES.txt +27 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi_sdk.egg-info/dependency_links.txt +1 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi_sdk.egg-info/requires.txt +7 -0
- datagsm_openapi_sdk-1.0.0/src/datagsm_openapi_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 themoment-team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datagsm-openapi-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for DataGSM OpenAPI
|
|
5
|
+
Author-email: themoment-team <datagsm.oauth@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/themoment-team/datagsm-openapi-sdk-python
|
|
8
|
+
Project-URL: Documentation, https://docs.themoment.io/datagsm/openapi/sdk/python
|
|
9
|
+
Project-URL: Repository, https://github.com/themoment-team/datagsm-openapi-sdk-python
|
|
10
|
+
Project-URL: Issues, https://github.com/themoment-team/datagsm-openapi-sdk-python/issues
|
|
11
|
+
Project-URL: API Documentation, https://openapi.data.hellogsm.kr
|
|
12
|
+
Keywords: datagsm,openapi,sdk,api-client
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: httpx<1.0,>=0.27.0
|
|
29
|
+
Requires-Dist: pydantic<3.0,>=2.0.0
|
|
30
|
+
Requires-Dist: typing-extensions>=4.5.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy>=1.8.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
## DataGSM OpenAPI SDK for Python
|
|
37
|
+
[](https://pypi.org/project/datagsm-openapi-sdk/)
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
[](https://www.python.org/)
|
|
40
|
+
|
|
41
|
+
DataGSM의 OpenAPI를 추상화된 환경에서 제공합니다.
|
|
42
|
+
|
|
43
|
+
### 설치 - pip
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install datagsm-openapi-sdk
|
|
47
|
+
````
|
|
48
|
+
|
|
49
|
+
### 설치 - uv
|
|
50
|
+
```bash
|
|
51
|
+
uv pip install datagsm-openapi-sdk
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 사용법
|
|
55
|
+
자세한 사용법은 [기술 문서](https://docs.themoment.io/datagsm/openapi/sdk/python)를 참고하십시오.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## DataGSM OpenAPI SDK for Python
|
|
2
|
+
[](https://pypi.org/project/datagsm-openapi-sdk/)
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/)
|
|
5
|
+
|
|
6
|
+
DataGSM의 OpenAPI를 추상화된 환경에서 제공합니다.
|
|
7
|
+
|
|
8
|
+
### 설치 - pip
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install datagsm-openapi-sdk
|
|
12
|
+
````
|
|
13
|
+
|
|
14
|
+
### 설치 - uv
|
|
15
|
+
```bash
|
|
16
|
+
uv pip install datagsm-openapi-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 사용법
|
|
20
|
+
자세한 사용법은 [기술 문서](https://docs.themoment.io/datagsm/openapi/sdk/python)를 참고하십시오.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "datagsm-openapi-sdk"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python SDK for DataGSM OpenAPI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "themoment-team", email = "datagsm.oauth@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["datagsm", "openapi", "sdk", "api-client"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
dependencies = [
|
|
32
|
+
"httpx>=0.27.0,<1.0",
|
|
33
|
+
"pydantic>=2.0.0,<3.0",
|
|
34
|
+
"typing-extensions>=4.5.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"mypy>=1.8.0",
|
|
40
|
+
"ruff>=0.3.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/themoment-team/datagsm-openapi-sdk-python"
|
|
45
|
+
Documentation = "https://docs.themoment.io/datagsm/openapi/sdk/python"
|
|
46
|
+
Repository = "https://github.com/themoment-team/datagsm-openapi-sdk-python"
|
|
47
|
+
Issues = "https://github.com/themoment-team/datagsm-openapi-sdk-python/issues"
|
|
48
|
+
"API Documentation" = "https://openapi.data.hellogsm.kr"
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
datagsm_openapi = ["py.typed"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
python_version = "3.9"
|
|
58
|
+
strict = true
|
|
59
|
+
warn_return_any = true
|
|
60
|
+
warn_unused_configs = true
|
|
61
|
+
disallow_untyped_defs = true
|
|
62
|
+
disallow_any_generics = true
|
|
63
|
+
disallow_subclassing_any = true
|
|
64
|
+
disallow_untyped_calls = true
|
|
65
|
+
disallow_incomplete_defs = true
|
|
66
|
+
check_untyped_defs = true
|
|
67
|
+
disallow_untyped_decorators = true
|
|
68
|
+
no_implicit_optional = true
|
|
69
|
+
warn_redundant_casts = true
|
|
70
|
+
warn_unused_ignores = true
|
|
71
|
+
warn_no_return = true
|
|
72
|
+
follow_imports = "normal"
|
|
73
|
+
show_error_codes = true
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
target-version = "py39"
|
|
77
|
+
line-length = 100
|
|
78
|
+
src = ["src"]
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint]
|
|
81
|
+
select = [
|
|
82
|
+
"E", # pycodestyle errors
|
|
83
|
+
"W", # pycodestyle warnings
|
|
84
|
+
"F", # pyflakes
|
|
85
|
+
"I", # isort
|
|
86
|
+
"N", # pep8-naming
|
|
87
|
+
"UP", # pyupgrade
|
|
88
|
+
"ANN", # flake8-annotations
|
|
89
|
+
"B", # flake8-bugbear
|
|
90
|
+
"C4", # flake8-comprehensions
|
|
91
|
+
"DTZ", # flake8-datetimez
|
|
92
|
+
"T10", # flake8-debugger
|
|
93
|
+
"RUF", # Ruff-specific rules
|
|
94
|
+
]
|
|
95
|
+
ignore = [
|
|
96
|
+
"ANN101", # Missing type annotation for self
|
|
97
|
+
"ANN102", # Missing type annotation for cls
|
|
98
|
+
"ANN401", # Dynamically typed expressions (Any) - needed for *args in context managers
|
|
99
|
+
"N818", # Exception naming - DataGsmException is the intended name
|
|
100
|
+
"UP035", # typing.Type vs type - Python 3.9 compatibility
|
|
101
|
+
"UP006", # Type[T] vs type[T] - Python 3.9 compatibility
|
|
102
|
+
"N812", # Lowercase imported as non-lowercase - intentional (date as Date)
|
|
103
|
+
"DTZ011", # date.today() without timezone - acceptable for our use case
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[tool.ruff.lint.isort]
|
|
107
|
+
known-first-party = ["datagsm_openapi"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""DataGSM OpenAPI SDK for Python.
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the DataGSM OpenAPI service.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
Basic usage::
|
|
7
|
+
|
|
8
|
+
from datagsm_openapi import DataGsmClient
|
|
9
|
+
|
|
10
|
+
with DataGsmClient(api_key="your-api-key") as client:
|
|
11
|
+
students = client.students.get_students()
|
|
12
|
+
print(f"Total students: {students.total_elements}")
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .api import (
|
|
16
|
+
ClubApi,
|
|
17
|
+
ClubRequest,
|
|
18
|
+
MealRequest,
|
|
19
|
+
NeisApi,
|
|
20
|
+
ProjectApi,
|
|
21
|
+
ProjectRequest,
|
|
22
|
+
ScheduleRequest,
|
|
23
|
+
StudentApi,
|
|
24
|
+
StudentRequest,
|
|
25
|
+
)
|
|
26
|
+
from .client import DataGsmClient
|
|
27
|
+
from .exceptions import (
|
|
28
|
+
BadRequestException,
|
|
29
|
+
DataGsmException,
|
|
30
|
+
ForbiddenException,
|
|
31
|
+
NetworkException,
|
|
32
|
+
NotFoundException,
|
|
33
|
+
RateLimitException,
|
|
34
|
+
ServerErrorException,
|
|
35
|
+
UnauthorizedException,
|
|
36
|
+
ValidationException,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"BadRequestException",
|
|
43
|
+
"ClubApi",
|
|
44
|
+
"ClubRequest",
|
|
45
|
+
"DataGsmClient",
|
|
46
|
+
"DataGsmException",
|
|
47
|
+
"ForbiddenException",
|
|
48
|
+
"MealRequest",
|
|
49
|
+
"NeisApi",
|
|
50
|
+
"NetworkException",
|
|
51
|
+
"NotFoundException",
|
|
52
|
+
"ProjectApi",
|
|
53
|
+
"ProjectRequest",
|
|
54
|
+
"RateLimitException",
|
|
55
|
+
"ScheduleRequest",
|
|
56
|
+
"ServerErrorException",
|
|
57
|
+
"StudentApi",
|
|
58
|
+
"StudentRequest",
|
|
59
|
+
"UnauthorizedException",
|
|
60
|
+
"ValidationException",
|
|
61
|
+
]
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""HTTP client abstraction layer for DataGSM OpenAPI SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .exceptions import (
|
|
8
|
+
BadRequestException,
|
|
9
|
+
DataGsmException,
|
|
10
|
+
ForbiddenException,
|
|
11
|
+
NetworkException,
|
|
12
|
+
NotFoundException,
|
|
13
|
+
RateLimitException,
|
|
14
|
+
ServerErrorException,
|
|
15
|
+
UnauthorizedException,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HttpClient:
|
|
20
|
+
"""HTTP client wrapper around httpx with error handling.
|
|
21
|
+
|
|
22
|
+
This class provides a clean interface for making HTTP requests to the DataGSM API
|
|
23
|
+
with automatic error handling and exception mapping.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
base_url: Base URL for the API
|
|
27
|
+
api_key: API key for authentication
|
|
28
|
+
timeout: Request timeout in seconds
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
base_url: str,
|
|
34
|
+
api_key: str,
|
|
35
|
+
timeout: float = 30.0,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialize the HTTP client.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
base_url: Base URL for the API
|
|
41
|
+
api_key: API key for authentication
|
|
42
|
+
timeout: Request timeout in seconds (default: 30.0)
|
|
43
|
+
"""
|
|
44
|
+
self.base_url = base_url.rstrip("/")
|
|
45
|
+
self.api_key = api_key
|
|
46
|
+
self.timeout = timeout
|
|
47
|
+
self._client: Optional[httpx.Client] = None
|
|
48
|
+
|
|
49
|
+
def __enter__(self) -> "HttpClient":
|
|
50
|
+
"""Enter context manager."""
|
|
51
|
+
self._client = httpx.Client(
|
|
52
|
+
base_url=self.base_url,
|
|
53
|
+
timeout=self.timeout,
|
|
54
|
+
headers=self._get_headers(),
|
|
55
|
+
)
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def __exit__(self, *args: Any) -> None:
|
|
59
|
+
"""Exit context manager and close the client."""
|
|
60
|
+
self.close()
|
|
61
|
+
|
|
62
|
+
def _get_headers(self) -> dict[str, str]:
|
|
63
|
+
"""Get common headers for all requests.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary of HTTP headers
|
|
67
|
+
"""
|
|
68
|
+
return {
|
|
69
|
+
"X-API-KEY": self.api_key,
|
|
70
|
+
"Accept": "application/json",
|
|
71
|
+
"User-Agent": "datagsm-openapi-sdk-python/0.1.0",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def _get_client(self) -> httpx.Client:
|
|
75
|
+
"""Get or create the HTTP client.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The httpx.Client instance
|
|
79
|
+
"""
|
|
80
|
+
if self._client is None:
|
|
81
|
+
self._client = httpx.Client(
|
|
82
|
+
base_url=self.base_url,
|
|
83
|
+
timeout=self.timeout,
|
|
84
|
+
headers=self._get_headers(),
|
|
85
|
+
)
|
|
86
|
+
return self._client
|
|
87
|
+
|
|
88
|
+
def _handle_error(self, response: httpx.Response) -> None:
|
|
89
|
+
"""Handle HTTP error responses.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
response: The HTTP response object
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
BadRequestException: For 400 errors
|
|
96
|
+
UnauthorizedException: For 401 errors
|
|
97
|
+
ForbiddenException: For 403 errors
|
|
98
|
+
NotFoundException: For 404 errors
|
|
99
|
+
RateLimitException: For 429 errors
|
|
100
|
+
ServerErrorException: For 5xx errors
|
|
101
|
+
DataGsmException: For other errors
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
error_body = response.json()
|
|
105
|
+
error_message = error_body.get("message", response.text)
|
|
106
|
+
except Exception:
|
|
107
|
+
error_body = None
|
|
108
|
+
error_message = response.text or f"HTTP {response.status_code} error"
|
|
109
|
+
|
|
110
|
+
status_code = response.status_code
|
|
111
|
+
|
|
112
|
+
if status_code == 400:
|
|
113
|
+
raise BadRequestException(message=error_message, response_body=error_body)
|
|
114
|
+
elif status_code == 401:
|
|
115
|
+
raise UnauthorizedException(message=error_message, response_body=error_body)
|
|
116
|
+
elif status_code == 403:
|
|
117
|
+
raise ForbiddenException(message=error_message, response_body=error_body)
|
|
118
|
+
elif status_code == 404:
|
|
119
|
+
raise NotFoundException(message=error_message, response_body=error_body)
|
|
120
|
+
elif status_code == 429:
|
|
121
|
+
raise RateLimitException(message=error_message, response_body=error_body)
|
|
122
|
+
elif 500 <= status_code < 600:
|
|
123
|
+
raise ServerErrorException(
|
|
124
|
+
message=error_message,
|
|
125
|
+
status_code=status_code,
|
|
126
|
+
response_body=error_body,
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
raise DataGsmException(
|
|
130
|
+
message=error_message,
|
|
131
|
+
status_code=status_code,
|
|
132
|
+
response_body=error_body,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def get(
|
|
136
|
+
self,
|
|
137
|
+
path: str,
|
|
138
|
+
params: Optional[dict[str, Any]] = None,
|
|
139
|
+
) -> dict[str, Any]:
|
|
140
|
+
"""Make a GET request.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
path: API endpoint path (e.g., "/students")
|
|
144
|
+
params: Query parameters
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
JSON response as a dictionary
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
NetworkException: For connection or timeout errors
|
|
151
|
+
DataGsmException: For API errors
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
client = self._get_client()
|
|
155
|
+
response = client.get(path, params=params)
|
|
156
|
+
response.raise_for_status()
|
|
157
|
+
return response.json() # type: ignore[no-any-return]
|
|
158
|
+
except httpx.HTTPStatusError as e:
|
|
159
|
+
self._handle_error(e.response)
|
|
160
|
+
raise # This line will never be reached, but makes mypy happy
|
|
161
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
162
|
+
raise NetworkException(
|
|
163
|
+
message=f"Network error: {e!s}",
|
|
164
|
+
original_exception=e,
|
|
165
|
+
) from e
|
|
166
|
+
except httpx.HTTPError as e:
|
|
167
|
+
raise NetworkException(
|
|
168
|
+
message=f"HTTP error: {e!s}",
|
|
169
|
+
original_exception=e,
|
|
170
|
+
) from e
|
|
171
|
+
|
|
172
|
+
def post(
|
|
173
|
+
self,
|
|
174
|
+
path: str,
|
|
175
|
+
json: Optional[dict[str, Any]] = None,
|
|
176
|
+
params: Optional[dict[str, Any]] = None,
|
|
177
|
+
) -> dict[str, Any]:
|
|
178
|
+
"""Make a POST request.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
path: API endpoint path
|
|
182
|
+
json: JSON request body
|
|
183
|
+
params: Query parameters
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
JSON response as a dictionary
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
NetworkException: For connection or timeout errors
|
|
190
|
+
DataGsmException: For API errors
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
client = self._get_client()
|
|
194
|
+
response = client.post(path, json=json, params=params)
|
|
195
|
+
response.raise_for_status()
|
|
196
|
+
return response.json() # type: ignore[no-any-return]
|
|
197
|
+
except httpx.HTTPStatusError as e:
|
|
198
|
+
self._handle_error(e.response)
|
|
199
|
+
raise
|
|
200
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
201
|
+
raise NetworkException(
|
|
202
|
+
message=f"Network error: {e!s}",
|
|
203
|
+
original_exception=e,
|
|
204
|
+
) from e
|
|
205
|
+
except httpx.HTTPError as e:
|
|
206
|
+
raise NetworkException(
|
|
207
|
+
message=f"HTTP error: {e!s}",
|
|
208
|
+
original_exception=e,
|
|
209
|
+
) from e
|
|
210
|
+
|
|
211
|
+
def put(
|
|
212
|
+
self,
|
|
213
|
+
path: str,
|
|
214
|
+
json: Optional[dict[str, Any]] = None,
|
|
215
|
+
params: Optional[dict[str, Any]] = None,
|
|
216
|
+
) -> dict[str, Any]:
|
|
217
|
+
"""Make a PUT request.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
path: API endpoint path
|
|
221
|
+
json: JSON request body
|
|
222
|
+
params: Query parameters
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
JSON response as a dictionary
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
NetworkException: For connection or timeout errors
|
|
229
|
+
DataGsmException: For API errors
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
client = self._get_client()
|
|
233
|
+
response = client.put(path, json=json, params=params)
|
|
234
|
+
response.raise_for_status()
|
|
235
|
+
return response.json() # type: ignore[no-any-return]
|
|
236
|
+
except httpx.HTTPStatusError as e:
|
|
237
|
+
self._handle_error(e.response)
|
|
238
|
+
raise
|
|
239
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
240
|
+
raise NetworkException(
|
|
241
|
+
message=f"Network error: {e!s}",
|
|
242
|
+
original_exception=e,
|
|
243
|
+
) from e
|
|
244
|
+
except httpx.HTTPError as e:
|
|
245
|
+
raise NetworkException(
|
|
246
|
+
message=f"HTTP error: {e!s}",
|
|
247
|
+
original_exception=e,
|
|
248
|
+
) from e
|
|
249
|
+
|
|
250
|
+
def delete(
|
|
251
|
+
self,
|
|
252
|
+
path: str,
|
|
253
|
+
params: Optional[dict[str, Any]] = None,
|
|
254
|
+
) -> dict[str, Any]:
|
|
255
|
+
"""Make a DELETE request.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
path: API endpoint path
|
|
259
|
+
params: Query parameters
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
JSON response as a dictionary
|
|
263
|
+
|
|
264
|
+
Raises:
|
|
265
|
+
NetworkException: For connection or timeout errors
|
|
266
|
+
DataGsmException: For API errors
|
|
267
|
+
"""
|
|
268
|
+
try:
|
|
269
|
+
client = self._get_client()
|
|
270
|
+
response = client.delete(path, params=params)
|
|
271
|
+
response.raise_for_status()
|
|
272
|
+
return response.json() # type: ignore[no-any-return]
|
|
273
|
+
except httpx.HTTPStatusError as e:
|
|
274
|
+
self._handle_error(e.response)
|
|
275
|
+
raise
|
|
276
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
277
|
+
raise NetworkException(
|
|
278
|
+
message=f"Network error: {e!s}",
|
|
279
|
+
original_exception=e,
|
|
280
|
+
) from e
|
|
281
|
+
except httpx.HTTPError as e:
|
|
282
|
+
raise NetworkException(
|
|
283
|
+
message=f"HTTP error: {e!s}",
|
|
284
|
+
original_exception=e,
|
|
285
|
+
) from e
|
|
286
|
+
|
|
287
|
+
def close(self) -> None:
|
|
288
|
+
"""Close the HTTP client and release resources."""
|
|
289
|
+
if self._client is not None:
|
|
290
|
+
self._client.close()
|
|
291
|
+
self._client = None
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""JSON serialization utilities for DataGSM OpenAPI SDK."""
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def serialize_date(value: date) -> str:
|
|
8
|
+
"""Serialize a date to ISO format string.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
value: Date object to serialize
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
ISO format date string (YYYY-MM-DD)
|
|
15
|
+
"""
|
|
16
|
+
return value.isoformat()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def serialize_datetime(value: datetime) -> str:
|
|
20
|
+
"""Serialize a datetime to ISO format string.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
value: Datetime object to serialize
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
ISO format datetime string
|
|
27
|
+
"""
|
|
28
|
+
return value.isoformat()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clean_params(params: dict[str, Any]) -> dict[str, Any]:
|
|
32
|
+
"""Clean query parameters by removing None values and serializing dates.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
params: Dictionary of query parameters
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Cleaned dictionary with None values removed and dates serialized
|
|
39
|
+
"""
|
|
40
|
+
cleaned: dict[str, Any] = {}
|
|
41
|
+
for key, value in params.items():
|
|
42
|
+
if value is None:
|
|
43
|
+
continue
|
|
44
|
+
if isinstance(value, datetime):
|
|
45
|
+
cleaned[key] = serialize_datetime(value)
|
|
46
|
+
elif isinstance(value, date):
|
|
47
|
+
cleaned[key] = serialize_date(value)
|
|
48
|
+
elif isinstance(value, bool):
|
|
49
|
+
# Convert boolean to lowercase string for query params
|
|
50
|
+
cleaned[key] = str(value).lower()
|
|
51
|
+
else:
|
|
52
|
+
cleaned[key] = value
|
|
53
|
+
return cleaned
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""API modules for DataGSM OpenAPI SDK."""
|
|
2
|
+
|
|
3
|
+
from .club import ClubApi, ClubRequest
|
|
4
|
+
from .neis import MealRequest, NeisApi, ScheduleRequest
|
|
5
|
+
from .project import ProjectApi, ProjectRequest
|
|
6
|
+
from .student import StudentApi, StudentRequest
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"ClubApi",
|
|
10
|
+
"ClubRequest",
|
|
11
|
+
"MealRequest",
|
|
12
|
+
"NeisApi",
|
|
13
|
+
"ProjectApi",
|
|
14
|
+
"ProjectRequest",
|
|
15
|
+
"ScheduleRequest",
|
|
16
|
+
"StudentApi",
|
|
17
|
+
"StudentRequest",
|
|
18
|
+
]
|