adding commit
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -154,3 +154,6 @@ out/
|
|||||||
|
|
||||||
# nix
|
# nix
|
||||||
result
|
result
|
||||||
|
|
||||||
|
# for local dev might dump cred stuff here
|
||||||
|
cred/
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
[general_config]
|
[general_config]
|
||||||
|
backend_base_url = "http://localhost:8080"
|
||||||
|
backend_user = "test@example.com"
|
||||||
|
backend_pw = "test"
|
||||||
|
|||||||
12
scripts/simple_create_user.sh
Executable file
12
scripts/simple_create_user.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuox pipefail
|
||||||
|
|
||||||
|
banner() {
|
||||||
|
echo "========================================================"
|
||||||
|
echo " $*"
|
||||||
|
echo "========================================================"
|
||||||
|
}
|
||||||
|
|
||||||
|
# utility script for easy testing
|
||||||
|
|
||||||
|
uv run taco register --display-name "Display Test" --email "test@example.com" --password "test"
|
||||||
@@ -22,7 +22,6 @@ def parse_args():
|
|||||||
trygo_py_cliclient.cli.register.setup_parser(subparsers)
|
trygo_py_cliclient.cli.register.setup_parser(subparsers)
|
||||||
trygo_py_cliclient.cli.login.setup_parser(subparsers)
|
trygo_py_cliclient.cli.login.setup_parser(subparsers)
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@@ -38,4 +37,6 @@ def main():
|
|||||||
_logger.info(f"Got args {args=}")
|
_logger.info(f"Got args {args=}")
|
||||||
_logger.info(f"Loaded config {config=}")
|
_logger.info(f"Loaded config {config=}")
|
||||||
|
|
||||||
args.func(args)
|
# TODO is there a clean way to hang on to a session for a bit when the cli command has run?
|
||||||
|
# i guess realistically we don't want that
|
||||||
|
args.func(config, args)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
import trygo_py_cliclient.config
|
||||||
import trygo_py_cliclient.client
|
import trygo_py_cliclient.client
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@@ -16,13 +17,13 @@ else:
|
|||||||
def setup_parser(subparsers: _SubparserType) -> None:
|
def setup_parser(subparsers: _SubparserType) -> None:
|
||||||
parser = subparsers.add_parser("login")
|
parser = subparsers.add_parser("login")
|
||||||
|
|
||||||
parser.add_argument("--email", type=str, help="config file location", default="")
|
parser.add_argument("--email", type=str, help="The email to log in with ", default="")
|
||||||
parser.add_argument("--password", type=str, help="config file location", default="")
|
parser.add_argument("--password", type=str, help="Password", default="")
|
||||||
parser.set_defaults(func=run)
|
parser.set_defaults(func=run)
|
||||||
|
|
||||||
def run(args):
|
|
||||||
|
|
||||||
clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/")
|
def run(config: trygo_py_cliclient.config.Config, args):
|
||||||
|
clnt = trygo_py_cliclient.client.BackendService(config)
|
||||||
|
|
||||||
response = clnt.login(args.email, args.password)
|
response = clnt.login(args.email, args.password)
|
||||||
_logger.info(response)
|
_logger.info(response)
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ def setup_parser(subparsers: _SubparserType) -> None:
|
|||||||
parser.add_argument("--password", type=str, help="config file location", default="")
|
parser.add_argument("--password", type=str, help="config file location", default="")
|
||||||
parser.set_defaults(func=run)
|
parser.set_defaults(func=run)
|
||||||
|
|
||||||
def run(args):
|
|
||||||
|
|
||||||
|
def run(args):
|
||||||
clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/")
|
clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/")
|
||||||
response = clnt.register(args.email, args.password, args.display_name)
|
response = clnt.register(args.email, args.password, args.display_name)
|
||||||
_logger.info(response)
|
_logger.info(response)
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import requests
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from trygo_py_cliclient.models import User, RegisterRequest, LoginRequest, AuthResponse
|
from trygo_py_cliclient.models import RegisterRequest, LoginRequest, AuthResponse
|
||||||
|
from trygo_py_cliclient.config import Config
|
||||||
from trygo_py_cliclient.exceptions import (
|
from trygo_py_cliclient.exceptions import (
|
||||||
TryGoAPIError,
|
TryGoAPIError,
|
||||||
AuthenticationError,
|
AuthenticationError,
|
||||||
@@ -15,17 +17,14 @@ from trygo_py_cliclient.exceptions import (
|
|||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TryGoAPIClient:
|
class BackendService:
|
||||||
"""HTTP client for TryGo webapp REST API"""
|
def __init__(self, config: Config):
|
||||||
|
self.base_url = config.general_config.backend_base_url.rstrip("/")
|
||||||
def __init__(self, base_url: str, timeout: int = 30):
|
self.timeout = 30
|
||||||
self.base_url = base_url.rstrip("/")
|
|
||||||
self.timeout = timeout
|
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers.update(
|
self.session.headers.update(
|
||||||
{"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"}
|
{"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"}
|
||||||
)
|
)
|
||||||
self._auth_token: Optional[str] = None
|
|
||||||
|
|
||||||
def _make_request(
|
def _make_request(
|
||||||
self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None
|
self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None
|
||||||
@@ -82,6 +81,7 @@ class TryGoAPIClient:
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise TryGoAPIError(f"Invalid JSON response: {e}")
|
raise TryGoAPIError(f"Invalid JSON response: {e}")
|
||||||
|
|
||||||
|
# claude really fucked this
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
raise NetworkError("Request timeout")
|
raise NetworkError("Request timeout")
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
@@ -92,18 +92,6 @@ class TryGoAPIClient:
|
|||||||
def register(self, email: str, password: str, display_name: str) -> AuthResponse:
|
def register(self, email: str, password: str, display_name: str) -> AuthResponse:
|
||||||
"""
|
"""
|
||||||
Register a new user account
|
Register a new user account
|
||||||
|
|
||||||
Args:
|
|
||||||
email: User's email address
|
|
||||||
password: User's password
|
|
||||||
display_name: User's display name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AuthResponse with registration result
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValidationError: If registration data is invalid
|
|
||||||
TryGoAPIError: If registration fails
|
|
||||||
"""
|
"""
|
||||||
request_data = RegisterRequest(
|
request_data = RegisterRequest(
|
||||||
email=email, password=password, display_name=display_name
|
email=email, password=password, display_name=display_name
|
||||||
@@ -117,42 +105,16 @@ class TryGoAPIClient:
|
|||||||
|
|
||||||
# Parse response and create AuthResponse
|
# Parse response and create AuthResponse
|
||||||
auth_response = AuthResponse(
|
auth_response = AuthResponse(
|
||||||
success=response_data.get("success", False),
|
|
||||||
message=response_data.get("message", ""),
|
message=response_data.get("message", ""),
|
||||||
token=response_data.get("token"),
|
email=response_data.get("email", ""),
|
||||||
user=None,
|
id=response_data.get("id", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parse user data if present
|
|
||||||
if "user" in response_data and response_data["user"]:
|
|
||||||
user_data = response_data["user"]
|
|
||||||
auth_response.user = User(
|
|
||||||
email=user_data.get("email", ""),
|
|
||||||
password="", # Never store password in response
|
|
||||||
display_name=user_data.get("display_name", ""),
|
|
||||||
id=user_data.get("id"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Store auth token on successful registration
|
|
||||||
if auth_response.success and auth_response.token:
|
|
||||||
self.set_auth_token(auth_response.token)
|
|
||||||
|
|
||||||
return auth_response
|
return auth_response
|
||||||
|
|
||||||
def login(self, email: str, password: str) -> AuthResponse:
|
def login(self, email: str, password: str) -> str:
|
||||||
"""
|
"""
|
||||||
Authenticate user and get auth token
|
Authenticate user and get auth token
|
||||||
|
|
||||||
Args:
|
|
||||||
email: User's email address
|
|
||||||
password: User's password
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AuthResponse with login result and token
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
AuthenticationError: If login credentials are invalid
|
|
||||||
TryGoAPIError: If login fails
|
|
||||||
"""
|
"""
|
||||||
request_data = LoginRequest(email=email, password=password)
|
request_data = LoginRequest(email=email, password=password)
|
||||||
|
|
||||||
@@ -162,39 +124,10 @@ class TryGoAPIClient:
|
|||||||
_logger.info(response_data)
|
_logger.info(response_data)
|
||||||
|
|
||||||
# Parse response and create AuthResponse
|
# Parse response and create AuthResponse
|
||||||
auth_response = AuthResponse(
|
token = response_data.get("token", "")
|
||||||
success=response_data.get("success", False),
|
self._store_credential(token)
|
||||||
message=response_data.get("message", ""),
|
|
||||||
token=response_data.get("token"),
|
|
||||||
user=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Parse user data if present
|
return token
|
||||||
if "user" in response_data and response_data["user"]:
|
|
||||||
user_data = response_data["user"]
|
|
||||||
auth_response.user = User(
|
|
||||||
email=user_data.get("email", ""),
|
|
||||||
password="", # Never store password in response
|
|
||||||
display_name=user_data.get("display_name", ""),
|
|
||||||
id=user_data.get("id"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Store auth token on successful login
|
|
||||||
if auth_response.success and auth_response.token:
|
|
||||||
self.set_auth_token(auth_response.token)
|
|
||||||
|
|
||||||
return auth_response
|
|
||||||
|
|
||||||
def set_auth_token(self, token: str) -> None:
|
|
||||||
"""
|
|
||||||
Set authentication token for subsequent requests
|
|
||||||
|
|
||||||
Args:
|
|
||||||
token: JWT or session token for authentication
|
|
||||||
"""
|
|
||||||
self._auth_token = token
|
|
||||||
self.session.headers["Authorization"] = f"Bearer {token}"
|
|
||||||
_logger.debug("Authentication token set")
|
|
||||||
|
|
||||||
def logout(self) -> None:
|
def logout(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -205,16 +138,12 @@ class TryGoAPIClient:
|
|||||||
_logger.info("Logging out user")
|
_logger.info("Logging out user")
|
||||||
|
|
||||||
# Try to call logout endpoint if we have a token
|
# Try to call logout endpoint if we have a token
|
||||||
if self._auth_token:
|
|
||||||
try:
|
|
||||||
self._make_request("POST", "/auth/logout")
|
|
||||||
_logger.debug("Server logout successful")
|
|
||||||
except Exception as e:
|
|
||||||
_logger.warning(f"Server logout failed (continuing anyway): {e}")
|
|
||||||
|
|
||||||
# Clear local auth state
|
|
||||||
self._auth_token = None
|
|
||||||
if "Authorization" in self.session.headers:
|
|
||||||
del self.session.headers["Authorization"]
|
|
||||||
|
|
||||||
_logger.debug("Local authentication cleared")
|
_logger.debug("Local authentication cleared")
|
||||||
|
|
||||||
|
def _store_credential(self, token: str) -> None:
|
||||||
|
|
||||||
|
token_path = Path("cred/token")
|
||||||
|
token_path.parent.mkdir(parents=True,exist_ok=True)
|
||||||
|
with token_path.open(mode="w") as tokenf:
|
||||||
|
tokenf.write(token)
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ class GeneralConfig:
|
|||||||
log_pattern: str = "%(asctime)s | %(process)d | %(levelname)-7s | %(name)s:%(lineno)d | %(message)s"
|
log_pattern: str = "%(asctime)s | %(process)d | %(levelname)-7s | %(name)s:%(lineno)d | %(message)s"
|
||||||
log_file: Optional[str] = None
|
log_file: Optional[str] = None
|
||||||
log_stream: bool = True
|
log_stream: bool = True
|
||||||
|
backend_base_url: str = ""
|
||||||
|
# obviously bad, but for now just keep it
|
||||||
|
backend_user: str = ""
|
||||||
|
backend_pw: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -42,31 +42,7 @@ def serialize_config(config_dict: dict) -> Config:
|
|||||||
Converts a dictionary to a Config object
|
Converts a dictionary to a Config object
|
||||||
|
|
||||||
Makes assumptions about structure of the config_dict, so validation should happen here too if needed.
|
Makes assumptions about structure of the config_dict, so validation should happen here too if needed.
|
||||||
|
|
||||||
:param config_dict: dictionary containing config values
|
|
||||||
:return: Config object
|
|
||||||
"""
|
"""
|
||||||
# generation_config = GenerationConfig(**config_dict["generation_config"])
|
|
||||||
|
|
||||||
# general_config_dict = config_dict["general_config"]
|
|
||||||
# general_config = GeneralConfig(
|
|
||||||
# root_directory=general_config_dict["root_directory"],
|
|
||||||
# out_dir_name=general_config_dict["out_dir_name"],
|
|
||||||
# dots_json_name=general_config_dict["dots_json_name"],
|
|
||||||
# mega_merged_name=general_config_dict["mega_merged_name"],
|
|
||||||
# mega_merged_inferenced_name=general_config_dict["mega_merged_inferenced_name"],
|
|
||||||
# skip_to_stage=general_config_dict["skip_to_stage"],
|
|
||||||
# measurement_type=MeasurementTypeEnum(general_config_dict["measurement_type"]),
|
|
||||||
# indexes_json_name=general_config_dict["indexes_json_name"],
|
|
||||||
# log_pattern=general_config_dict["log_pattern"],
|
|
||||||
# )
|
|
||||||
|
|
||||||
# deepdog_config = DeepdogConfig(**config_dict["deepdog_config"])
|
|
||||||
# config = Config(
|
|
||||||
# generation_config=generation_config,
|
|
||||||
# general_config=general_config,
|
|
||||||
# deepdog_config=deepdog_config,
|
|
||||||
# )
|
|
||||||
config = dacite.from_dict(
|
config = dacite.from_dict(
|
||||||
data_class=Config,
|
data_class=Config,
|
||||||
data=config_dict,
|
data=config_dict,
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ class LoginRequest:
|
|||||||
class AuthResponse:
|
class AuthResponse:
|
||||||
"""Response from authentication endpoints"""
|
"""Response from authentication endpoints"""
|
||||||
|
|
||||||
success: bool
|
|
||||||
message: str
|
message: str
|
||||||
user: Optional[User] = None
|
email: Optional[str] = None
|
||||||
token: Optional[str] = None
|
id: Optional[str] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user