adding commit
Some checks failed
Nix Tests / nix-test (nix-runner) (push) Failing after 3m41s
Python Tests / python-test (push) Failing after 6m36s

This commit is contained in:
2025-10-22 11:07:55 -05:00
parent c16e970d3f
commit 07fafd8fe7
10 changed files with 56 additions and 128 deletions

3
.gitignore vendored
View File

@@ -154,3 +154,6 @@ out/
# nix
result
# for local dev might dump cred stuff here
cred/

View File

@@ -1 +1,4 @@
[general_config]
backend_base_url = "http://localhost:8080"
backend_user = "test@example.com"
backend_pw = "test"

12
scripts/simple_create_user.sh Executable file
View 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"

View File

@@ -22,7 +22,6 @@ def parse_args():
trygo_py_cliclient.cli.register.setup_parser(subparsers)
trygo_py_cliclient.cli.login.setup_parser(subparsers)
args = parser.parse_args()
return args
@@ -38,4 +37,6 @@ def main():
_logger.info(f"Got args {args=}")
_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)

View File

@@ -2,6 +2,7 @@ import argparse
import logging
import typing
import trygo_py_cliclient.config
import trygo_py_cliclient.client
_logger = logging.getLogger(__name__)
@@ -16,13 +17,13 @@ else:
def setup_parser(subparsers: _SubparserType) -> None:
parser = subparsers.add_parser("login")
parser.add_argument("--email", type=str, help="config file location", default="")
parser.add_argument("--password", 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="Password", default="")
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)
_logger.info(response)

View File

@@ -23,8 +23,8 @@ def setup_parser(subparsers: _SubparserType) -> None:
parser.add_argument("--password", type=str, help="config file location", default="")
parser.set_defaults(func=run)
def run(args):
clnt = trygo_py_cliclient.client.TryGoAPIClient("http://localhost:8080/")
response = clnt.register(args.email, args.password, args.display_name)
_logger.info(response)

View File

@@ -2,8 +2,10 @@ import requests
import logging
from typing import Optional, Dict, Any
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 (
TryGoAPIError,
AuthenticationError,
@@ -15,17 +17,14 @@ from trygo_py_cliclient.exceptions import (
_logger = logging.getLogger(__name__)
class TryGoAPIClient:
"""HTTP client for TryGo webapp REST API"""
def __init__(self, base_url: str, timeout: int = 30):
self.base_url = base_url.rstrip("/")
self.timeout = timeout
class BackendService:
def __init__(self, config: Config):
self.base_url = config.general_config.backend_base_url.rstrip("/")
self.timeout = 30
self.session = requests.Session()
self.session.headers.update(
{"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"}
)
self._auth_token: Optional[str] = None
def _make_request(
self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None
@@ -82,6 +81,7 @@ class TryGoAPIClient:
except ValueError as e:
raise TryGoAPIError(f"Invalid JSON response: {e}")
# claude really fucked this
except requests.exceptions.Timeout:
raise NetworkError("Request timeout")
except requests.exceptions.ConnectionError:
@@ -92,18 +92,6 @@ class TryGoAPIClient:
def register(self, email: str, password: str, display_name: str) -> AuthResponse:
"""
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(
email=email, password=password, display_name=display_name
@@ -117,42 +105,16 @@ class TryGoAPIClient:
# Parse response and create AuthResponse
auth_response = AuthResponse(
success=response_data.get("success", False),
message=response_data.get("message", ""),
token=response_data.get("token"),
user=None,
email=response_data.get("email", ""),
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
def login(self, email: str, password: str) -> AuthResponse:
def login(self, email: str, password: str) -> str:
"""
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)
@@ -162,39 +124,10 @@ class TryGoAPIClient:
_logger.info(response_data)
# Parse response and create AuthResponse
auth_response = AuthResponse(
success=response_data.get("success", False),
message=response_data.get("message", ""),
token=response_data.get("token"),
user=None,
)
token = response_data.get("token", "")
self._store_credential(token)
# 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 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")
return token
def logout(self) -> None:
"""
@@ -205,16 +138,12 @@ class TryGoAPIClient:
_logger.info("Logging out user")
# 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")
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)

View File

@@ -8,6 +8,10 @@ class GeneralConfig:
log_pattern: str = "%(asctime)s | %(process)d | %(levelname)-7s | %(name)s:%(lineno)d | %(message)s"
log_file: Optional[str] = None
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)

View File

@@ -42,31 +42,7 @@ def serialize_config(config_dict: dict) -> Config:
Converts a dictionary to a Config object
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(
data_class=Config,
data=config_dict,

View File

@@ -33,7 +33,6 @@ class LoginRequest:
class AuthResponse:
"""Response from authentication endpoints"""
success: bool
message: str
user: Optional[User] = None
token: Optional[str] = None
email: Optional[str] = None
id: Optional[str] = None