From 9de2aefb661734aaffd17f2ce1b5167d0c01cebb Mon Sep 17 00:00:00 2001 From: matv864 Date: Fri, 24 Jan 2025 15:59:00 +1000 Subject: [PATCH] api is connected and ready to fight --- api/README.md | 4 +- api/pyproject.toml | 3 ++ api/src/api/check_imei.py | 18 +++++++-- api/src/app.py | 10 ++++- api/src/schemas/__init__.py | 5 ++- api/src/schemas/checking.py | 2 + api/src/schemas/external_checking.py | 55 ++++++++++++++++++++++++++++ api/src/service/__init__.py | 2 + api/src/service/authentication.py | 5 +++ api/src/service/check_imei.py | 35 ++++++++++++++++++ api/src/settings.py | 10 +++++ api/src/utils/exceptions.py | 1 + 12 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 api/src/schemas/external_checking.py create mode 100644 api/src/service/__init__.py create mode 100644 api/src/service/authentication.py create mode 100644 api/src/service/check_imei.py create mode 100644 api/src/settings.py create mode 100644 api/src/utils/exceptions.py diff --git a/api/README.md b/api/README.md index 856a31a..89b4d7d 100644 --- a/api/README.md +++ b/api/README.md @@ -1,3 +1,5 @@ # api -Describe your project here. +```.env +IMEI_API_TOKEN= +``` diff --git a/api/pyproject.toml b/api/pyproject.toml index ca61e59..4530aac 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -8,6 +8,9 @@ authors = [ dependencies = [ "fastapi[standard]>=0.115.7", "pydantic>=2.10.5", + "pydantic-settings>=2.7.1", + "requests>=2.32.3", + "aiohttp>=3.11.11", ] readme = "README.md" requires-python = ">= 3.12" diff --git a/api/src/api/check_imei.py b/api/src/api/check_imei.py index 64effb1..fb8eaf8 100644 --- a/api/src/api/check_imei.py +++ b/api/src/api/check_imei.py @@ -1,7 +1,11 @@ +from typing import Union + from fastapi import APIRouter, status -from src.schemas import CheckingInput, CheckingOutput +from src.service import Checking_imei_service, Authentication_service +from src.schemas import CheckingInput, CheckingOutput, CheckingReport, DeviceProperties +from src.utils.exceptions import AccessDenied checking_router = APIRouter() @@ -9,7 +13,13 @@ checking_router = APIRouter() @checking_router.post( "/check-imei", status_code=status.HTTP_200_OK, - response_model=CheckingOutput + response_model=Union[CheckingOutput | dict] ) -async def check_imei(body: CheckingInput): - return CheckingOutput(result="OK") +async def check_imei(body: CheckingInput) -> Union[DeviceProperties | dict]: + if not await Authentication_service.token_is_valid(token=body.token): + raise AccessDenied("token_is_not_valid") + report: Union[CheckingReport | dict] = await Checking_imei_service().request_imei_info(body.imei) + if type(report) is dict: + return report + else: + return report.properties \ No newline at end of file diff --git a/api/src/app.py b/api/src/app.py index 9c3519e..1ee6106 100644 --- a/api/src/app.py +++ b/api/src/app.py @@ -1,5 +1,7 @@ -from fastapi import APIRouter, FastAPI +from fastapi import APIRouter, FastAPI, Request from fastapi.middleware.cors import CORSMiddleware +from starlette.responses import JSONResponse +from src.utils.exceptions import AccessDenied from src.api import checking_router @@ -22,3 +24,9 @@ main_app_router.include_router(checking_router, tags=["Check IMEI"]) app.include_router(checking_router) +@app.exception_handler(AccessDenied) +async def login_exception_handler(request: Request, exc: AccessDenied): + return JSONResponse( + status_code=401, + content={}, + ) diff --git a/api/src/schemas/__init__.py b/api/src/schemas/__init__.py index c85d1d9..576c3ba 100644 --- a/api/src/schemas/__init__.py +++ b/api/src/schemas/__init__.py @@ -1,2 +1,5 @@ from .checking import CheckingInput as CheckingInput -from .checking import CheckingOutput as CheckingOutput \ No newline at end of file +from .checking import CheckingOutput as CheckingOutput +from .external_checking import CheckingRequest as CheckingRequest +from .external_checking import CheckingReport as CheckingReport +from .external_checking import DeviceProperties as DeviceProperties diff --git a/api/src/schemas/checking.py b/api/src/schemas/checking.py index 4d7c490..d944065 100644 --- a/api/src/schemas/checking.py +++ b/api/src/schemas/checking.py @@ -1,6 +1,8 @@ from typing import Any from pydantic import BaseModel, ConfigDict + + class CheckingOutput(BaseModel): model_config = ConfigDict(from_attributes=True) diff --git a/api/src/schemas/external_checking.py b/api/src/schemas/external_checking.py new file mode 100644 index 0000000..ca57fbf --- /dev/null +++ b/api/src/schemas/external_checking.py @@ -0,0 +1,55 @@ +from typing import Optional, Union +from pydantic import BaseModel, ConfigDict + +class CheckingRequest(BaseModel): + model_config = ConfigDict(from_attributes=True) + + deviceId: str + serviceId: int + + +class CheckingReport(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + type: str + status: str + orderId: Optional[str] # in test mode is null + service: "ServiceInfo" + amount: str + deviceId: str + processedAt: int + properties: Union["DeviceProperties", dict] # can be empty dict + +class DeviceProperties(BaseModel): + model_config = ConfigDict(from_attributes=True) + + deviceName: str + image: str + imei: str + meid: Optional[str] = None # parameter isn't exist in documentation + imei2: Optional[str] = None # parameter isn't exist in documentation + serial: Optional[str] = None # parameter isn't exist in documentation + estPurchaseDate: int + simLock: Optional[bool] = None # parameter is exist only in documentation + warrantyStatus: str + repairCoverage: Union[bool, str] # I got bool, in documentation - str + technicalSupport: Union[bool, str] # I got bool, in documentation - str + replacement: Optional[bool] = None # parameter isn't exist in documentation + demoUnit: bool + refurbished: bool + purchaseCountry: str + fmiOn: bool + lostMode: Union[bool, str] # I got bool, in documentation - str + loaner: bool # parameter isn't exist in documentation + usaBlockStatus: Optional[str] = None # parameter is exist only in documentation + network: Optional[str] = None # parameter is exist only in documentation + + + +class ServiceInfo(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: int + title: str + diff --git a/api/src/service/__init__.py b/api/src/service/__init__.py new file mode 100644 index 0000000..dbd93a1 --- /dev/null +++ b/api/src/service/__init__.py @@ -0,0 +1,2 @@ +from .check_imei import Checking_imei_service as Checking_imei_service +from .authentication import Authentication_service as Authentication_service \ No newline at end of file diff --git a/api/src/service/authentication.py b/api/src/service/authentication.py new file mode 100644 index 0000000..91a1713 --- /dev/null +++ b/api/src/service/authentication.py @@ -0,0 +1,5 @@ +class Authentication_service: + + @staticmethod + async def token_is_valid(token: str) -> bool: + return True diff --git a/api/src/service/check_imei.py b/api/src/service/check_imei.py new file mode 100644 index 0000000..1ccc6bc --- /dev/null +++ b/api/src/service/check_imei.py @@ -0,0 +1,35 @@ +from aiohttp import ClientSession + +from src.settings import settings +from src.schemas import CheckingRequest, CheckingReport + + +class Checking_imei_service: + def __init__(self): + self.service_url = "https://api.imeicheck.net/v1/checks" + self.service_id = 12 # TODO: change service id after company response + self.headers = { + "Authorization": f"Bearer {settings.IMEI_API_TOKEN}", + "Accept-Language": "en" + } + + + async def request_imei_info(self, imei: str) -> CheckingReport: + data = CheckingRequest( + deviceId=imei, + serviceId=self.service_id + ) + async with ClientSession() as session: + async with session.post( + self.service_url, + headers=self.headers, + data=data.model_dump() + ) as response: + try: + raw_response = await response.json() + return CheckingReport(**raw_response) + except Exception as e: + print(type(e), str(e)) + # TODO: make good handling of request errors + + return dict() \ No newline at end of file diff --git a/api/src/settings.py b/api/src/settings.py new file mode 100644 index 0000000..7043a26 --- /dev/null +++ b/api/src/settings.py @@ -0,0 +1,10 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", extra="ignore") + + IMEI_API_TOKEN: str + + +settings = Settings() diff --git a/api/src/utils/exceptions.py b/api/src/utils/exceptions.py new file mode 100644 index 0000000..7c24d2b --- /dev/null +++ b/api/src/utils/exceptions.py @@ -0,0 +1 @@ +class AccessDenied(Exception): ... \ No newline at end of file