diff --git a/core/models/manage/server_reply_tools.py b/core/models/manage/server_reply_tools.py index 9b6a2e0..6c0e7a0 100644 --- a/core/models/manage/server_reply_tools.py +++ b/core/models/manage/server_reply_tools.py @@ -14,10 +14,12 @@ TRANSLATION_DICT = { ServerError.INVALID: ResultError.INVALID_INPUT, ServerError.MISSING_DATA: ResultError.INVALID_INPUT, ServerError.UNPAID: ResultError.SUBSCRIPTION, - ServerError.SERVER_SIDE: ResultError.SERVER_SIDE + ServerError.SERVER_SIDE: ResultError.SERVER_SIDE, + ServerError.EXPIRED: ResultError.SUBSCRIPTION } def format_server_response(api_response: ApiResponse, desired_type: str) -> Result: + print("Starting format_server_response conversion") """Turn an API response into a Result Object""" # if it's in object format, then convert it, @@ -33,28 +35,35 @@ def format_server_response(api_response: ApiResponse, desired_type: str) -> Resu logger.error("Can't convert server response.") return invalid_server_format() - conversion_worked, server_reply = validate_model(ServerReply, raw_reply) + validated_model, server_reply = validate_model(ServerReply, raw_reply) - if not conversion_worked: + if not validated_model: logger.error(f"Invalid structure for ServerReply. The original raw reply was {raw_reply}") return invalid_server_format(error_msg=server_reply) - # Sanity check: + # Failure: + if not server_reply.valid: + logger.error(f"invalid server reply: {server_reply.error_type}") + logger.error(f"invalid server reply: {server_reply.message}") + final_return = translate_server_failure(server_reply) + + print(f"final_return is {final_return}") + return final_return # result object + + print("Server claims the result was valid, let's check if it has the data type") if not hasattr(server_reply, desired_type): error_msg = "Developer Error: You're asking for a data type that isn't in the object!" logger.error(error_msg) return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) - # Success! Extract and Return Data: - if server_reply.valid: - data = getattr(server_reply, desired_type) + data = getattr(server_reply, desired_type) + if data: return Result(valid=True, data=data) + else: + return Result(valid=False, error_type=ResultError.MISSING_DATA, message="Server Replied it was valid, but failed to provide the data.") - # Failure: - logger.error(f"invalid server reply: {server_reply.error_type}") - logger.error(f"invalid server reply: {server_reply.message}") - error_type = translate_server_failure(server_reply.error_type) - return Result(valid=False, error_type=error_type, message=server_reply.message) + + # return Result(valid=False, error_type=error_type, message=server_reply.message) def translate_server_failure(server_reply: ServerReply) -> Result: diff --git a/core/services/keys_and_verifications/wireguard_keys.py b/core/services/keys_and_verifications/wireguard_keys.py index cba8687..5597c73 100644 --- a/core/services/keys_and_verifications/wireguard_keys.py +++ b/core/services/keys_and_verifications/wireguard_keys.py @@ -60,13 +60,13 @@ def register_wireguard_session( logger.error(error_msg) raise ConnectionError(error_msg) - wireguard_configuration = api_result.data - print(f"wireguard_configuration type is {type(wireguard_configuration)}") - # print(f"wireguard_configuration value is {wireguard_configuration}") - - if isinstance(wireguard_configuration, dict): - logger.error("Server isn't returning a WG config, it's trying to tell you a ServerReply message...") - return handle_config_error(wireguard_configuration) + # 2nd Layer Translation: + result = format_server_response(api_response=api_result, desired_type="data") + if not result.valid: + # KILLING THE RESULT FOR NOW. Later, replace this error raise with the return of data, + raise InvalidSubscriptionError(result.message) + + wireguard_configuration = result.data if wireguard_configuration is None: raise InvalidSubscriptionError() @@ -76,14 +76,25 @@ def register_wireguard_session( return Result(valid=True) +# over-engineering: -def handle_config_error(wireguard_configuration: str|dict) -> Result: - """The server gave the wireguard config in an invalid format. First convert it to get the error.""" - server_reply = format_server_response(wireguard_configuration, "message") - if server_reply.valid: - return server_reply - else: - return Result(valid=False, error_type=ResultError.NODE_ERROR, message="server-side error with wireguard config format.") +# print(f"wireguard_configuration type is {type(wireguard_configuration)}") +# # print(f"wireguard_configuration value is {wireguard_configuration}") + +# if isinstance(wireguard_configuration, dict): +# print(f"wireguard_configuration is {type(wireguard_configuration)}") +# print(f"wireguard_configuration is {wireguard_configuration}") +# logger.error("Server isn't returning a WG config, it's trying to tell you a ServerReply message...") + # return format_server_response(wireguard_configuration, "message") + + +# def handle_config_error(wireguard_configuration: str|dict) -> Result: +# """The server gave the wireguard config in an invalid format. First convert it to get the error.""" +# server_reply = format_server_response(wireguard_configuration, "message") +# if server_reply.valid: +# return server_reply +# else: +# return Result(valid=False, error_type=ResultError.NODE_ERROR, message="server-side error with wireguard config format.") def _generate_wireguard_keys() -> dict: diff --git a/core/services/networking/general_connection_tools/connection_enable.py b/core/services/networking/general_connection_tools/connection_enable.py index 7a8ff72..35e3cd8 100644 --- a/core/services/networking/general_connection_tools/connection_enable.py +++ b/core/services/networking/general_connection_tools/connection_enable.py @@ -194,7 +194,7 @@ def _ensure_wireguard_configured( logger.info(f"[CONNECTION] Checking if the profile already has a wg config..") if not profile.has_wireguard_configuration(): logger.info(f"[CONNECTION] Profile does NOT have WG keys and it needs it. Registering a NEW wg session..") - register_wireguard_session(profile, connection_observer=connection_observer) + registry_result = register_wireguard_session(profile, connection_observer=connection_observer) def _establish_with_renegotiation( diff --git a/core/services/subscriptions/check_payment_loop.py b/core/services/subscriptions/check_payment_loop.py index 74574c4..ececea5 100644 --- a/core/services/subscriptions/check_payment_loop.py +++ b/core/services/subscriptions/check_payment_loop.py @@ -1,5 +1,5 @@ from core.infrastructure.thread_dispatch import MainThreadDispatcher -from core.services.subscriptions.subscriptions_by_protocol import check_paid +from core.services.subscriptions.subscriptions_by_protocol import check_paid, mark_as_paid from core.controllers.tickets.TicketPayController import check_if_ticket_paid @@ -24,13 +24,13 @@ MAX_FAILED_CONNECTIONS = 8 class CheckPaidTimer: _instance = None # CLASS VARIABLE - survives across __init__ calls - def __new__(cls, view_callback: Callable, billing_code: str, ui_dispatcher: MainThreadDispatcher, payment_type: PaymentType, connection_observer: ConnectionObserver): + def __new__(cls, view_callback: Callable, billing_code: str, ui_dispatcher: MainThreadDispatcher, payment_type: PaymentType, connection_observer: ConnectionObserver, profile_id: int): """Called before __init__ — controls instance creation""" if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance - def __init__(self, view_callback: Callable, billing_code: str, ui_dispatcher: MainThreadDispatcher, payment_type: PaymentType, connection_observer: ConnectionObserver): + def __init__(self, view_callback: Callable, billing_code: str, ui_dispatcher: MainThreadDispatcher, payment_type: PaymentType, connection_observer: ConnectionObserver, profile_id: int): # Guard: makes sure this entire class only runs once if hasattr(self, '_initialized'): return @@ -42,6 +42,7 @@ class CheckPaidTimer: self.view_callback = view_callback self.ui_dispatcher = ui_dispatcher self.billing_code = billing_code + self.profile_id = profile_id self.connection_observer = connection_observer self.payment_type = payment_type self.failed_connections = 0 @@ -99,7 +100,15 @@ class CheckPaidTimer: if result.valid: print("PAID! Stopping timer") self.stop_polling() - message = "paid" + + # SAVE: + marked = mark_as_paid(profile_id=self.profile_id) + if not marked.valid: + message = marked.message + else: + message = "Paid! This invoice is paid" + + # UPDATE UI: self.view_callback(paid=True, stopped=True, message=message) # not yet paid: diff --git a/core/services/subscriptions/subscriptions_by_protocol.py b/core/services/subscriptions/subscriptions_by_protocol.py index 52eb75e..18083d7 100644 --- a/core/services/subscriptions/subscriptions_by_protocol.py +++ b/core/services/subscriptions/subscriptions_by_protocol.py @@ -10,6 +10,7 @@ from core.models.pydantic_models.ServerReply import ServerReply, ServerError, Su from core.models.Subscription import Subscription from core.Constants import Constants +from datetime import datetime def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Result: """Evaluates Payment Status from the Server""" @@ -17,7 +18,6 @@ def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Re "billing_code": billing_code } url = f'{Constants.SP_BASE_URL}/v2/check_paid' - print(f"url is {url}") api_response = connect.single_endpoint( method="post", @@ -162,7 +162,24 @@ def get_profile_subscription(profile_id: int) -> Subscription: return profile.subscription -def save_billing_code(profile_id: int, billing_code: str, expires_at: str) -> Result: +def mark_as_paid(profile_id: int) -> Result: + """Conclude the payment phase, by marking the sub as paid""" + + logger.info("Marking as paid..") + + # Get profile object: + profile = Profile.find_by_id(profile_id) + if not profile: + error_msg = f"Invalid profile id of {profile_id}" + logger.error(error_msg) + return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT) + + profile.subscription.billing_pending = False + profile.save() + return Result(valid=True) + + +def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime) -> Result: """Checks if the billing code is already saved, and if not, saves it.""" logger.info("Saving billing code..") @@ -187,32 +204,32 @@ def save_billing_code(profile_id: int, billing_code: str, expires_at: str) -> Re billing_pending=True ) profile.save() + return Result(valid=True) except Exception as e: error_msg = f"Legacy system wouldn't create or save Subscription object with {str(billing_code)} and {str(expires_at)} or type {type(expires_at)}, with error: {str(e)}" logger.error(error_msg) return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT) - return Result(valid=True) - # # get the payment data singleton: - # billing_data = PaymentData(instance_id=profile_id) +# # get the payment data singleton: +# billing_data = PaymentData(instance_id=profile_id) - # # new - # if billing_data.billing_code is None: - # billing_data.billing_code = billing_code - # billing_data.save() - # logger.info("saved billing_code") - # return Result(valid=True) +# # new +# if billing_data.billing_code is None: +# billing_data.billing_code = billing_code +# billing_data.save() +# logger.info("saved billing_code") +# return Result(valid=True) - # # Already exists and is different: - # if regular_billing.billing_code != billing_code: - # error_msg = "Critical Error! Billing codes do NOT match" - # logger.error(error_msg) - # return Result(valid=False, error_type=ResultError.UNKNOWN, message=error_msg) - - # # already saved: - # return Result(valid=True, message="Already saved.") +# # Already exists and is different: +# if regular_billing.billing_code != billing_code: +# error_msg = "Critical Error! Billing codes do NOT match" +# logger.error(error_msg) +# return Result(valid=False, error_type=ResultError.UNKNOWN, message=error_msg) + +# # already saved: +# return Result(valid=True, message="Already saved.") def return_pre_existing_payment_data(profile_id: int, subscription: SubscriptionInvoice) -> Result: