From cff2a2207869aa0c05bbdb4f28c2b8191e391ce7 Mon Sep 17 00:00:00 2001 From: Michael Woods Date: Mon, 17 Feb 2025 17:42:33 -0500 Subject: [PATCH] Updates to jobs and users client and server apis. --- src/packetserver/client/jobs.py | 3 ++ src/packetserver/client/users.py | 34 ++++++++++++++- src/packetserver/common/__init__.py | 2 +- src/packetserver/server/jobs.py | 22 +++++++++- src/packetserver/server/users.py | 66 ++++++++++++++++++++++++++++- 5 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/packetserver/client/jobs.py b/src/packetserver/client/jobs.py index 52b3f7b..380a0ed 100644 --- a/src/packetserver/client/jobs.py +++ b/src/packetserver/client/jobs.py @@ -125,6 +125,9 @@ def get_job_id(client: Client, bbs_callsign: str, job_id: int, get_data=True) -> raise RuntimeError(f"Sending job failed: {response.status_code}: {response.payload}") return JobWrapper(response.payload) +def get_user_jobs(): # TODO + pass + class JobSession: def __init__(self, client: Client, bbs_callsign: str, default_timeout: int = 300, stutter: int = 2): self.client = client diff --git a/src/packetserver/client/users.py b/src/packetserver/client/users.py index f8badf0..fddf0d7 100644 --- a/src/packetserver/client/users.py +++ b/src/packetserver/client/users.py @@ -3,6 +3,7 @@ import datetime from packetserver.client import Client from packetserver.common import Request, Response, PacketServerConnection from typing import Union, Optional +from packetserver.common.util import email_valid, random_string from uuid import UUID, uuid4 import os.path @@ -75,4 +76,35 @@ def get_users(client: Client, bbs_callsign: str, limit=None): user_list = [] for u in response.payload: user_list.append(UserWrapper(u)) - return user_list \ No newline at end of file + return user_list + +def update_self(client: Client, bbs_callsign: str, email: str = None, bio: str = None, + socials: Union[list[str],str] = None, location: str = None, status: str = None) -> bool: + + payload = {} + + if email is not None: + if not email_valid(email): + raise ValueError(f"{email} is not a valid e-mail address") + payload['email'] = email + + if socials is not None: + payload['social'] = socials + + if status is not None: + payload['status'] = str(status) + + if location is not None: + payload['location'] = str(location) + + if bio is not None: + payload['bio'] = str(bio) + + req = Request.blank() + req.path = "user" + req.method = Request.Method.UPDATE + req.payload = payload + response = client.send_receive_callsign(req, bbs_callsign) + if response.status_code != 200: + raise RuntimeError(f"GET user {username} failed: {response.status_code}: {response.payload}") + return True \ No newline at end of file diff --git a/src/packetserver/common/__init__.py b/src/packetserver/common/__init__.py index fcf7953..eb387d2 100644 --- a/src/packetserver/common/__init__.py +++ b/src/packetserver/common/__init__.py @@ -336,7 +336,7 @@ def send_response(conn: PacketServerConnection, response: Response, original_req logging.warning(f"Attempted to send data, but connection state is {conn.state.name}") def send_blank_response(conn: PacketServerConnection, original_request: Request, status_code: int = 200, - payload: Union[bytes, bytearray, str, dict] = ""): + payload: Union[bytes, bytearray, str, dict, list] = ""): response = Response.blank() response.status_code = status_code response.payload = payload diff --git a/src/packetserver/server/jobs.py b/src/packetserver/server/jobs.py index c40ed1a..1766b93 100644 --- a/src/packetserver/server/jobs.py +++ b/src/packetserver/server/jobs.py @@ -248,8 +248,26 @@ def handle_job_get_id(req: Request, conn: PacketServerConnection, db: ZODB.DB, j def handle_job_get_user(req: Request, conn: PacketServerConnection, db: ZODB.DB): username = ax25.Address(conn.remote_callsign).call.upper().strip() - # TODO finish user job lookup - send_blank_response(conn, req, 404) + jobs = [] + value = "y" + include_data = True + for key in req.vars: + if key.lower().strip() == "data": + value = req.vars[key].lower().strip() + if value in no_values: + include_data = False + id_only = False + if 'id_only' in req.vars: + if req.vars['id_only'] in yes_values: + id_only = True + with db.transaction() as storage: + for jid in storage.root()['user_jobs'][username]: + jobs.append(Job.get_job_by_id(jid, storage.root()).to_dict(include_data=include_data)) + + if id_only: + send_blank_response(conn, req, status_code=200, payload=[x['id'] for x in jobs]) + else: + send_blank_response(conn, req, status_code=200, payload=jobs) def handle_job_get(req: Request, conn: PacketServerConnection, db: ZODB.DB): spl = [x for x in req.path.split("/") if x.strip() != ""] diff --git a/src/packetserver/server/users.py b/src/packetserver/server/users.py index 77f2f14..7eda8de 100644 --- a/src/packetserver/server/users.py +++ b/src/packetserver/server/users.py @@ -11,6 +11,7 @@ from packetserver.common import PacketServerConnection, Request, Response, Messa import ZODB import logging import uuid +from traceback import format_exc from uuid import UUID from packetserver.common.util import email_valid from BTrees.OOBTree import TreeSet @@ -243,8 +244,67 @@ def handle_user_get(req: Request, conn: PacketServerConnection, db: ZODB.DB): response.payload = [x.to_safe_dict() for x in User.get_all_users(db.root(), limit=limit) if not x.hidden] send_response(conn, response, req) -def handle_user_update(req: Request, conn: PacketServerConnection, db: ZODB.DB): # TODO - pass +def handle_user_update(req: Request, conn: PacketServerConnection, db: ZODB.DB): + """ + "status": str 300 cutoff + "bio": str 4k cutoff + "socials": list[str] each 300 cutoff + "email": str (must be an e-mail) validate with valid_email function from util + "location": str 1000 char cutoff + """ + username = ax25.Address(conn.remote_callsign).call.upper().strip() + logging.debug(f"Handling user update request for {username}: {req.payload}") + + email = None + bio = None + socials = None + location = None + status = None + + # set vars + + if 'bio' in req.payload: + bio = str(req.payload['bio']) + + if 'location' in req.payload: + location = str(req.payload['location']) + + if 'status' in req.payload: + status = str(req.payload['stus']) + + if 'email' in req.payload: + email = req.payload['email'] + if not email_valid(email): + send_blank_response(conn, req, status_code=400, payload="email must be valid format") + return + + if 'socials' in req.payload: + var_socials = req.payload['socials'] + socials = [] + if type(var_socials) is list: + for s in var_socials: + socials.append(str(s)) + else: + socials.append(str(var_socials)) + try: + with db.transaction() as db: + user = User.get_user_by_username(username, db.root()) + if email is not None: + user.email = email + if bio is not None: + user.bio = bio + if socials is not None: + user.socials = socials + if location is not None: + user.location = location + if status is not None: + user.status = status + except: + logging.error(f"Error while updating user {username}:\n{format_exc()}") + send_blank_response(conn, req, status_code=500) + return + + send_blank_response(conn, req, status_code=200) def user_root_handler(req: Request, conn: PacketServerConnection, db: ZODB.DB): logging.debug(f"{req} being processed by user_root_handler") @@ -255,5 +315,7 @@ def user_root_handler(req: Request, conn: PacketServerConnection, db: ZODB.DB): logging.debug("user is authorized") if req.method is Request.Method.GET: handle_user_get(req, conn, db) + elif req.method is Request.Method.UPDATE: + handle_user_update(req, conn ,db) else: send_blank_response(conn, req, status_code=404)