Skip to content

Commit f6342f2

Browse files
authored
Merge pull request #10 from lightbeem3296/alpha/task9
Alpha/task9 - Done Enigx Service
2 parents 61b822c + 318cb52 commit f6342f2

File tree

8 files changed

+148
-4
lines changed

8 files changed

+148
-4
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ APP_PORT=8000
1010
MONGO_URL=mongodb://host:port
1111
DB_NAME=dbname
1212

13-
JWT_SECRET_KEY=4ab2fcf7a6bc014396215ed36edb1c5d975c6574a2904132c03c
13+
JWT_SECRET_KEY=jwt_secret_key

.env.test.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
TENANT_ID=tenant_id
2+
PROJECT_ID=project_id
3+
BEARER_TOKEN=bearer_token

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
__pycache__
22
.env
3+
.env.test
34
.mypy_cache
45
.pytest_cache
56
.ruff_cache

app/schemas/fetch_config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ class FetchDataType(StrEnum):
2020

2121

2222
class FetchData(BaseModel):
23+
name: str | None
2324
type: FetchDataType
24-
data: dict[str, Any] | list[Any] | str | None
25+
data: bytes | dict[str, Any] | list[Any] | str | None
2526

2627

2728
class FetchTokenType(StrEnum):

app/services/enigx.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import json
2+
import mimetypes
3+
from datetime import UTC, datetime
4+
from http import HTTPStatus
5+
from typing import Any
6+
7+
import requests
8+
from loguru import logger
9+
from requests import Response
10+
11+
from app.core.config.fetch import config as config_fetch
12+
from app.schemas.fetch_config import FetchData, FetchDataType
13+
14+
15+
class EnigxService:
16+
def __init__(self, tenant_id: str, project_id: str, bearer_token: str) -> None:
17+
self.tenant_id = tenant_id
18+
self.project_id = project_id
19+
self.bearer_token = bearer_token
20+
21+
def get_def_headers(self) -> dict[str, str]:
22+
return {
23+
"x-tenant-id": self.tenant_id,
24+
"Authorization": f"Bearer {self.bearer_token}",
25+
}
26+
27+
def put_to_knowledge(self, data: FetchData) -> None:
28+
if data.type is FetchDataType.FILE:
29+
if isinstance(data.data, bytes):
30+
resp = self.put_file(data.name, data.data)
31+
else:
32+
msg = f"Invalid file data type: {type(data.data)}"
33+
raise TypeError(msg)
34+
else:
35+
resp = self.put_text(json.dumps(data.data, default=str))
36+
37+
if resp.status_code == HTTPStatus.OK:
38+
logger.debug(json.dumps(resp.json(), indent=2))
39+
else:
40+
msg = f"Enigx error: {resp.status_code}: {resp.text}"
41+
raise ValueError(msg)
42+
43+
def put_file(self, filename: str | None, file_bytes: bytes, options: dict[str, Any] | None = None) -> Response:
44+
filename = filename or "unknown"
45+
46+
mime_type, _ = mimetypes.guess_type(filename)
47+
mime_type = mime_type or "application/octet-stream"
48+
49+
files = {
50+
"file": (filename, file_bytes, mime_type),
51+
"fileOptionsJson": (None, json.dumps(options), "application/json"),
52+
}
53+
54+
return requests.post(
55+
url=f"https://api.enigx.com/v1/projects/{self.project_id}/knowledge/file",
56+
headers={
57+
**self.get_def_headers(),
58+
},
59+
files=files, # type: ignore # noqa: PGH003
60+
timeout=config_fetch.REQUEST_TIMEOUT,
61+
)
62+
63+
def put_text(self, text: str) -> Response:
64+
return requests.post(
65+
url=f"https://api.enigx.com/v1/projects/{self.project_id}/knowledge/text",
66+
headers={
67+
**self.get_def_headers(),
68+
"Content-Type": "application/json",
69+
},
70+
json={
71+
"name": f"text_{datetime.now(tz=UTC).isoformat()}",
72+
"text": text,
73+
},
74+
timeout=config_fetch.REQUEST_TIMEOUT,
75+
)

app/services/fetcher.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from urllib.parse import unquote
2+
13
import requests
24

35
from app.core.config.fetch import config as config_fetch
@@ -36,17 +38,29 @@ def fetch(self) -> FetchData:
3638
raise ValueError(msg)
3739

3840
resp_data = FetchData(
41+
name=None,
3942
type=self.config.data_type,
4043
data=None,
4144
)
4245

4346
if resp.status_code == self.config.success_code:
4447
if self.config.data_type is FetchDataType.JSON:
4548
resp_data.data = resp.json()
49+
4650
elif self.config.data_type is FetchDataType.FILE:
47-
msg = "Not implemented for FILE type response"
48-
raise NotImplementedError(msg)
51+
content_disposition = resp.headers.get("Content-Disposition")
52+
filename = "downloaded_file"
53+
if content_disposition:
54+
parts = content_disposition.split("filename=")
55+
if len(parts) > 1:
56+
filename = unquote(parts[1].strip('"'))
57+
resp_data.name = filename
58+
resp_data.data = resp.content
59+
4960
elif self.config.data_type is FetchDataType.HTML:
5061
resp_data.data = resp.text
62+
else:
63+
msg = f"Fetch error: {resp.status_code}: {resp.text}"
64+
raise ValueError(msg)
5165

5266
return resp_data

test/core/env.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from pydantic_settings import BaseSettings, SettingsConfigDict
2+
3+
4+
class Config(BaseSettings):
5+
model_config = SettingsConfigDict(env_file=".env.test", extra="allow")
6+
7+
TENANT_ID: str = ""
8+
PROJECT_ID: str = ""
9+
BEARER_TOKEN: str = ""
10+
11+
12+
config = Config()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import json
2+
import time
3+
from http import HTTPStatus
4+
5+
from loguru import logger
6+
7+
from app.services.enigx import EnigxService
8+
from test.core.env import config as config_env_test
9+
10+
11+
def test_enigx_put_file() -> None:
12+
service = EnigxService(
13+
tenant_id=config_env_test.TENANT_ID,
14+
project_id=config_env_test.PROJECT_ID,
15+
bearer_token=config_env_test.BEARER_TOKEN,
16+
)
17+
resp = service.put_file(
18+
filename=f"test_{time.time()}.txt",
19+
file_bytes=b"test",
20+
options={
21+
"description": "this is a test file upload",
22+
},
23+
)
24+
logger.info(json.dumps(resp.json(), indent=2))
25+
26+
assert resp.status_code == HTTPStatus.OK
27+
28+
29+
def test_enigx_put_text() -> None:
30+
service = EnigxService(
31+
tenant_id=config_env_test.TENANT_ID,
32+
project_id=config_env_test.PROJECT_ID,
33+
bearer_token=config_env_test.BEARER_TOKEN,
34+
)
35+
resp = service.put_text("this is a test text upload")
36+
logger.info(json.dumps(resp.json(), indent=2))
37+
38+
assert resp.status_code == HTTPStatus.OK

0 commit comments

Comments
 (0)