hats
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -157,3 +157,4 @@ result
|
||||
|
||||
# for local dev might dump cred stuff here
|
||||
cred/
|
||||
local/
|
||||
|
||||
@@ -8,11 +8,12 @@ dependencies = [
|
||||
"requests>=2.31.0",
|
||||
"dacite>=1.9.2",
|
||||
"tomli>=2.2.1",
|
||||
"types-requests>=2.32.4.20250913",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
hello = "hello_world:main"
|
||||
taco = "trygo_py_cliclient.cli:main"
|
||||
taiga = "taiga_pycli.cli:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
@@ -46,6 +47,16 @@ junit_family = "xunit1"
|
||||
log_format = "%(asctime)s | %(levelname)s | %(pathname)s:%(lineno)d | %(message)s"
|
||||
log_level = "WARNING"
|
||||
|
||||
[tool.pyright]
|
||||
executionEnvironments = [
|
||||
{ root = "src" }
|
||||
]
|
||||
exclude = [
|
||||
".venv"
|
||||
]
|
||||
venvPath = "."
|
||||
venv = ".venv"
|
||||
|
||||
# [tool.mypy]
|
||||
# If you need this
|
||||
# plugins = "numpy.typing.mypy_plugin"
|
||||
|
||||
@@ -9,4 +9,4 @@ banner() {
|
||||
|
||||
# utility script for easy testing
|
||||
|
||||
uv run taco register --display-name "Display Test" --email "test@example.com" --password "test"
|
||||
uv run taiga register --display-name "Display Test" --email "test@example.com" --password "test"
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import argparse
|
||||
import pathlib
|
||||
import logging
|
||||
import trygo_py_cliclient.config
|
||||
import trygo_py_cliclient.cli.common
|
||||
import trygo_py_cliclient.cli.register
|
||||
import trygo_py_cliclient.cli.login
|
||||
import taiga_pycli.config
|
||||
import taiga_pycli.cli.common
|
||||
import taiga_pycli.cli.register
|
||||
import taiga_pycli.cli.login
|
||||
import taiga_pycli.cli.hats
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
"trygo-client", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
"taiga_pycli", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -19,8 +20,9 @@ def parse_args():
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="cmd", required=True)
|
||||
trygo_py_cliclient.cli.register.setup_parser(subparsers)
|
||||
trygo_py_cliclient.cli.login.setup_parser(subparsers)
|
||||
taiga_pycli.cli.register.setup_parser(subparsers)
|
||||
taiga_pycli.cli.login.setup_parser(subparsers)
|
||||
taiga_pycli.cli.hats.setup_parser(subparsers)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
@@ -29,9 +31,9 @@ def parse_args():
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
config = trygo_py_cliclient.config.read_config(pathlib.Path(args.config_file))
|
||||
config = taiga_pycli.config.read_config(pathlib.Path(args.config_file))
|
||||
|
||||
trygo_py_cliclient.cli.common.set_up_logging(
|
||||
taiga_pycli.cli.common.set_up_logging(
|
||||
config,
|
||||
)
|
||||
_logger.info(f"Got args {args=}")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import trygo_py_cliclient.config
|
||||
import taiga_pycli.config
|
||||
import typing
|
||||
import logging
|
||||
import pathlib
|
||||
|
||||
|
||||
def set_up_logging(
|
||||
config: trygo_py_cliclient.config.Config,
|
||||
config: taiga_pycli.config.Config,
|
||||
create_logfile_parents: bool = True,
|
||||
):
|
||||
# for convenience
|
||||
|
||||
66
src/taiga_pycli/cli/hats.py
Normal file
66
src/taiga_pycli/cli/hats.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import argparse
|
||||
import logging
|
||||
import typing
|
||||
import taiga_pycli.config
|
||||
import taiga_pycli.models
|
||||
|
||||
import taiga_pycli.service
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
_SubparserType = argparse._SubParsersAction[argparse.ArgumentParser]
|
||||
else:
|
||||
_SubparserType = typing.Any
|
||||
|
||||
|
||||
def setup_parser(subparsers: _SubparserType) -> None:
|
||||
parser = subparsers.add_parser("hat")
|
||||
|
||||
parser.add_argument(
|
||||
"--name", type=str, help="The name of the hat to add", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
help="The description of the hat to add",
|
||||
default=None,
|
||||
)
|
||||
|
||||
parser.set_defaults(func=run)
|
||||
|
||||
|
||||
def run(cfg: taiga_pycli.config.Config, args):
|
||||
# clnt = taiga_pycli.client.TryGoAPIClient("http://localhost:8080/")
|
||||
|
||||
backend = taiga_pycli.service.BackendService(cfg)
|
||||
|
||||
if args.name is not None:
|
||||
if args.description is None:
|
||||
_logger.error("Got a null description, exiting")
|
||||
return
|
||||
# both not None
|
||||
response = backend.add_hat(args.name, args.description)
|
||||
return
|
||||
|
||||
else:
|
||||
_logger.debug("Not creating, just list")
|
||||
if args.description is not None:
|
||||
_logger.error("Provided a description without name")
|
||||
|
||||
response = backend.get_hats()
|
||||
if response is None:
|
||||
_logger.warning("none response here")
|
||||
return
|
||||
real_hats = []
|
||||
for hat in response:
|
||||
rh = taiga_pycli.models.Hat(
|
||||
name=hat["name"],
|
||||
description=hat["description"],
|
||||
user_id=hat["user_id"],
|
||||
)
|
||||
real_hats.append(rh)
|
||||
print(rh)
|
||||
# _logger.info(response)
|
||||
return
|
||||
@@ -1,9 +1,10 @@
|
||||
import argparse
|
||||
import logging
|
||||
import typing
|
||||
import getpass
|
||||
|
||||
import trygo_py_cliclient.config
|
||||
import trygo_py_cliclient.client
|
||||
import taiga_pycli.config
|
||||
import taiga_pycli.service
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,16 +15,38 @@ else:
|
||||
_SubparserType = typing.Any
|
||||
|
||||
|
||||
class Password:
|
||||
DEFAULT = "Prompt if not provided"
|
||||
|
||||
def __init__(self, value):
|
||||
if value == self.DEFAULT:
|
||||
value = getpass.getpass("Password: ")
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
def setup_parser(subparsers: _SubparserType) -> None:
|
||||
parser = subparsers.add_parser("login")
|
||||
|
||||
parser.add_argument("--email", type=str, help="The email to log in with ", default="")
|
||||
parser.add_argument("--password", type=str, help="Password", default="")
|
||||
parser.add_argument(
|
||||
"--email", type=str, help="The email to log in with ", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password", type=Password, help="Password", default=Password.DEFAULT
|
||||
)
|
||||
parser.set_defaults(func=run)
|
||||
|
||||
|
||||
def run(config: trygo_py_cliclient.config.Config, args):
|
||||
clnt = trygo_py_cliclient.client.BackendService(config)
|
||||
def run(config: taiga_pycli.config.Config, args):
|
||||
clnt = taiga_pycli.service.BackendService(config)
|
||||
|
||||
response = clnt.login(args.email, args.password)
|
||||
_logger.info(response)
|
||||
_logger.info(f"using password {args.password}")
|
||||
email_to_use = args.email
|
||||
if args.email is None:
|
||||
email_to_use = config.general_config.backend_user
|
||||
|
||||
response = clnt.login(email_to_use, str(args.password))
|
||||
# _logger.info(response)
|
||||
return
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import argparse
|
||||
import logging
|
||||
import typing
|
||||
import taiga_pycli.config
|
||||
|
||||
import trygo_py_cliclient.client
|
||||
import taiga_pycli.service
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -24,7 +25,9 @@ def setup_parser(subparsers: _SubparserType) -> None:
|
||||
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)
|
||||
def run(cfg: taiga_pycli.config.Config, args):
|
||||
# clnt = taiga_pycli.client.TryGoAPIClient("http://localhost:8080/")
|
||||
|
||||
backend = taiga_pycli.service.BackendService(cfg)
|
||||
response = backend.register(args.email, args.password, args.display_name)
|
||||
_logger.info(response)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from trygo_py_cliclient.config.config import (
|
||||
from taiga_pycli.config.config import (
|
||||
GeneralConfig,
|
||||
Config,
|
||||
)
|
||||
|
||||
from trygo_py_cliclient.config.config_reader import (
|
||||
from taiga_pycli.config.config_reader import (
|
||||
read_config,
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import dacite
|
||||
import pathlib
|
||||
import tomli
|
||||
|
||||
from trygo_py_cliclient.config import GeneralConfig, Config
|
||||
from taiga_pycli.config import GeneralConfig, Config
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -36,3 +36,12 @@ class AuthResponse:
|
||||
message: str
|
||||
email: Optional[str] = None
|
||||
id: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Hat:
|
||||
"""A wearable hat"""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
user_id: int
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import requests
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
|
||||
from trygo_py_cliclient.models import RegisterRequest, LoginRequest, AuthResponse
|
||||
from trygo_py_cliclient.config import Config
|
||||
from trygo_py_cliclient.exceptions import (
|
||||
import requests
|
||||
|
||||
|
||||
from taiga_pycli.models import RegisterRequest, LoginRequest, AuthResponse
|
||||
from taiga_pycli.config import Config
|
||||
from taiga_pycli.exceptions import (
|
||||
TryGoAPIError,
|
||||
AuthenticationError,
|
||||
ValidationError,
|
||||
@@ -26,6 +28,9 @@ class BackendService:
|
||||
{"Content-Type": "application/json", "User-Agent": "trygo-py-client/0.1.0"}
|
||||
)
|
||||
|
||||
self._token_path = Path("cred") / "token"
|
||||
self.token = None
|
||||
|
||||
def _make_request(
|
||||
self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
@@ -77,7 +82,9 @@ class BackendService:
|
||||
|
||||
# Parse JSON response
|
||||
try:
|
||||
return response.json()
|
||||
rsp_json = response.json()
|
||||
_logger.debug(rsp_json)
|
||||
return rsp_json
|
||||
except ValueError as e:
|
||||
raise TryGoAPIError(f"Invalid JSON response: {e}")
|
||||
|
||||
@@ -141,9 +148,45 @@ class BackendService:
|
||||
|
||||
_logger.debug("Local authentication cleared")
|
||||
|
||||
def _store_credential(self, token: str) -> None:
|
||||
def get_hats(self) -> Optional[Dict[str, Any]]:
|
||||
# needs credential
|
||||
|
||||
token_path = Path("cred/token")
|
||||
token_path.parent.mkdir(parents=True,exist_ok=True)
|
||||
with token_path.open(mode="w") as tokenf:
|
||||
cred = self._read_credential()
|
||||
if cred is None:
|
||||
return None
|
||||
|
||||
_logger.debug("Credential was read")
|
||||
response = self._make_request("GET", "/hats")
|
||||
# _logger.debug(response)
|
||||
return response
|
||||
|
||||
def add_hat(self, name: str, description: str) -> Optional[Dict[str, Any]]:
|
||||
cred = self._read_credential()
|
||||
if cred is None:
|
||||
return None
|
||||
|
||||
_logger.debug("Credential was read")
|
||||
response = self._make_request(
|
||||
"POST", "/hats", {"name": name, "description": description}
|
||||
)
|
||||
return response
|
||||
|
||||
def _store_credential(self, token: str) -> None:
|
||||
self._token_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with self._token_path.open(mode="w") as tokenf:
|
||||
tokenf.write(token)
|
||||
self.token = token
|
||||
self.session.headers.update({"Authorization": f"Bearer {token}"})
|
||||
|
||||
def _read_credential(self) -> Optional[str]:
|
||||
try:
|
||||
with open(self._token_path) as token_file:
|
||||
token_in_file = token_file.read()
|
||||
self.token = token_in_file
|
||||
self.session.headers.update(
|
||||
{"Authorization": f"Bearer {token_in_file}"}
|
||||
)
|
||||
return token_in_file
|
||||
except FileNotFoundError:
|
||||
_logger.error("No token file found, try logging in")
|
||||
return None
|
||||
14
uv.lock
generated
14
uv.lock
generated
@@ -132,6 +132,7 @@ dependencies = [
|
||||
{ name = "dacite" },
|
||||
{ name = "requests" },
|
||||
{ name = "tomli" },
|
||||
{ name = "types-requests" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
@@ -149,6 +150,7 @@ requires-dist = [
|
||||
{ name = "dacite", specifier = ">=1.9.2" },
|
||||
{ name = "requests", specifier = ">=2.31.0" },
|
||||
{ name = "tomli", specifier = ">=2.2.1" },
|
||||
{ name = "types-requests", specifier = ">=2.32.4.20250913" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
@@ -367,6 +369,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.32.4.20250913"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
|
||||
Reference in New Issue
Block a user