Ticket Refactor to accept ServerReply objects, and keep the internal flow returning Result objects
This commit is contained in:
parent
2208cedb7e
commit
af368db8b7
8 changed files with 642 additions and 342 deletions
|
|
@ -5,13 +5,14 @@ if TYPE_CHECKING:
|
|||
from core.essentials.observers.ConnectionObserver import ConnectionObserver
|
||||
from core.observers.TicketObserver import TicketObserver
|
||||
|
||||
from core.utils.type_checker import type_checker
|
||||
from core.services.prepare_tickets.ticket_prep_orchestrator import ticket_prep_orchestrator
|
||||
from core.services.prepare_tickets.make_sure_pub_key_exists import make_sure_pub_key_exists
|
||||
from core.services.prepare_tickets import ticket_tracker
|
||||
from core.services.helpers.get_how_many_profiles_were_ordered import (
|
||||
get_how_many_profiles_were_ordered,
|
||||
)
|
||||
from core.observers.BaseObserver import BaseObserver
|
||||
# from core.observers.BaseObserver import BaseObserver
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.errors.logger import logger
|
||||
|
||||
|
|
@ -33,13 +34,15 @@ If it doesn't have a config file, then it uses the temp billing id to get the pu
|
|||
"""
|
||||
|
||||
# Bulk Prep:
|
||||
|
||||
@type_checker
|
||||
def prepare_tickets(
|
||||
how_many_profiles: int,
|
||||
ticket_observer: TicketObserver,
|
||||
connection_observer: ConnectionObserver,
|
||||
which_ticket: Optional[int] = None,
|
||||
billing_code: Optional[int] = None
|
||||
) -> dict:
|
||||
) -> Result:
|
||||
|
||||
# these two are for preparation of a single ticket to a profile.
|
||||
# can't have one blank without the other. (profile or ticket)
|
||||
|
|
@ -82,35 +85,48 @@ def prepare_tickets(
|
|||
billing_code=billing_code # only relevant for single ticket prep
|
||||
)
|
||||
|
||||
# rest of this function is evaluating the results:
|
||||
|
||||
if prep_results == False or prep_results == None:
|
||||
return {"valid": False, "message": "error"}
|
||||
|
||||
if "valid" not in prep_results:
|
||||
return {"valid": False, "message": "error"}
|
||||
|
||||
if prep_results["valid"] == True:
|
||||
notification = f"Done! All Tickets Ready!"
|
||||
if prep_results.valid:
|
||||
notification = "Done! All Tickets Ready!"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
else:
|
||||
notification = "Ticket Preparation Failed"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
return prep_results
|
||||
|
||||
if "how_many_failed" in prep_results:
|
||||
how_many_failed = prep_results.get("how_many_failed", 0)
|
||||
failed_validations = prep_results.get("failed_validations", None)
|
||||
if failed_validations:
|
||||
notification = f"Error with Ticket Preparation or Verification!"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
return prep_results
|
||||
|
||||
notification = f"Error with Ticket Preparation or Verification!"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
return prep_results
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# if prep_results == False or prep_results == None:
|
||||
# return {"valid": False, "message": "error"}
|
||||
|
||||
# if "valid" not in prep_results:
|
||||
# return {"valid": False, "message": "error"}
|
||||
|
||||
# if prep_results["valid"] == True:
|
||||
# notification = f"Done! All Tickets Ready!"
|
||||
# if ticket_observer is not None:
|
||||
# ticket_observer.notify("preparing", subject=notification)
|
||||
# return prep_results
|
||||
|
||||
# if "how_many_failed" in prep_results:
|
||||
# how_many_failed = prep_results.get("how_many_failed", 0)
|
||||
# failed_validations = prep_results.get("failed_validations", None)
|
||||
# if failed_validations:
|
||||
# notification = f"Error with Ticket Preparation or Verification!"
|
||||
# if ticket_observer is not None:
|
||||
# ticket_observer.notify("preparing", subject=notification)
|
||||
# return prep_results
|
||||
|
||||
# notification = f"Error with Ticket Preparation or Verification!"
|
||||
# if ticket_observer is not None:
|
||||
# ticket_observer.notify("preparing", subject=notification)
|
||||
# return prep_results
|
||||
|
||||
|
||||
# No profile required
|
||||
def respawn_billing_code_into_ticket(
|
||||
billing_code: str,
|
||||
|
|
@ -167,7 +183,7 @@ def respawn_billing_code_into_ticket(
|
|||
valid = ticket_result.get('valid', False)
|
||||
|
||||
if not valid:
|
||||
notification = f"Error in Respawn Prep!"
|
||||
notification = "Error in Respawn Prep!"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
return prep_error_result(ticket_result)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class ApiCall(Enum):
|
|||
TRANSLATION_DICT = {
|
||||
ServerError.WRONG_LOCATION: ResultError.WRONG_LOCATION,
|
||||
ServerError.NODE_ERROR: ResultError.WRONG_LOCATION,
|
||||
ServerError.BTCPAY: ResultError.SERVER_SIDE,
|
||||
ServerError.CRYPTO_NODE: ResultError.SERVER_SIDE,
|
||||
ServerError.INVALID: ResultError.INVALID_INPUT,
|
||||
ServerError.MISSING_DATA: ResultError.INVALID_INPUT,
|
||||
ServerError.UNPAID: ResultError.SUBSCRIPTION,
|
||||
|
|
@ -71,7 +71,6 @@ def format_server_response(api_response: ApiResponse|dict, desired_type: str) ->
|
|||
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="Server Replied it was valid, but failed to provide the data.")
|
||||
|
||||
|
||||
# return Result(valid=False, error_type=error_type, message=server_reply.message)
|
||||
|
||||
|
||||
def translate_server_failure(server_reply: ServerReply) -> Result:
|
||||
|
|
@ -89,12 +88,13 @@ def invalid_server_format(error_msg: Optional[str] = None) -> Result:
|
|||
|
||||
|
||||
def api_call_and_extraction(
|
||||
desired_type: str,
|
||||
desired_type: Optional[str],
|
||||
method: ApiCall,
|
||||
url: str,
|
||||
connection_observer: ConnectionObserver,
|
||||
payload: dict,
|
||||
billing_code: Optional[str] = None
|
||||
billing_code: Optional[str] = None,
|
||||
return_server_reply: bool = False,
|
||||
) -> Result:
|
||||
"""Do an API call, extract the data, and if there's an error, then translate the server's reply to a Result Object"""
|
||||
|
||||
|
|
@ -152,6 +152,11 @@ def api_call_and_extraction(
|
|||
logger.error(error_msg)
|
||||
return invalid_server_format()
|
||||
|
||||
# Opt into getting the ServerReply object (instead of Result object):
|
||||
if return_server_reply and desired_type is None:
|
||||
logger.info("Validation successfull. Returning a raw ServerReply object")
|
||||
return server_reply
|
||||
|
||||
logger.info("Validation successfull. Checking if the server's reply says the operation worked..")
|
||||
if not server_reply.valid:
|
||||
error_message = f"Server reply was correctly structured, but the actual invoice failed to be created. The raw data is {server_reply}"
|
||||
|
|
@ -169,12 +174,3 @@ def api_call_and_extraction(
|
|||
data = getattr(server_reply, desired_type)
|
||||
logger.info(f"Successfully returning the data of {data}")
|
||||
return Result(valid=True, data=data)
|
||||
|
||||
|
||||
|
||||
# else:
|
||||
# error_msg = f"Server Replied it was valid, but failed to provide the data for the desired type of {desired_type}."
|
||||
# logger.error(error_msg)
|
||||
# return Result(valid=False, error_type=ResultError.MISSING_DATA, message=error_msg)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ class ServerError(str, Enum):
|
|||
QUANTITY = "quantity"
|
||||
UNPAID = "unpaid"
|
||||
INVALID = "invalid"
|
||||
ALREADY_USED = "already_used"
|
||||
MISSING_DATA = "missing_data"
|
||||
WRONG_LOCATION = "wrong_location"
|
||||
NODE_ERROR = "node_error"
|
||||
BTCPAY = "btcpay"
|
||||
CRYPTO_NODE = "crypto_node"
|
||||
SERVER_SIDE = "server_side"
|
||||
|
||||
|
||||
|
|
@ -37,6 +38,8 @@ class ServerReply():
|
|||
boolean_result: Optional[bool] = None
|
||||
conf: Optional[str] = None
|
||||
payment_status: Optional[bool] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
|
||||
|
||||
@dataclass_json
|
||||
|
|
|
|||
|
|
@ -4,22 +4,25 @@ from typing import TYPE_CHECKING
|
|||
if TYPE_CHECKING:
|
||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||
from core.observers.TicketObserver import TicketObserver
|
||||
|
||||
# services
|
||||
from core.services.networking.httpx import connect
|
||||
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
||||
# from core.services.networking.httpx import connect
|
||||
# from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
||||
from core.services.networking.make_url import make_url
|
||||
# from core.services.payment_phase.ticket_config_tools import get_which_billing_key
|
||||
|
||||
# models & their controllers
|
||||
from core.models.session.SessionProfile import SessionProfile
|
||||
from core.models.system.SystemProfile import SystemProfile
|
||||
from core.models.Subscription import Subscription # not sure if needed
|
||||
from core.models.manage.server_reply_tools import api_call_and_extraction, ApiCall, translate_server_failure
|
||||
from core.models.pydantic_models.ServerReply import ServerReply, ServerError
|
||||
# from core.models.session.SessionProfile import SessionProfile
|
||||
# from core.models.system.SystemProfile import SystemProfile
|
||||
|
||||
# utils
|
||||
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
|
||||
# constants & errors
|
||||
from core.Constants import Constants
|
||||
from core.errors.exceptions import InvalidData, ServerSideError
|
||||
# from core.errors.exceptions import InvalidData, ServerSideError
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.errors.logger import logger
|
||||
|
||||
from typing import Optional
|
||||
|
|
@ -76,12 +79,24 @@ def prep_single_ticket_payload(billing_code: str, list_of_all_blinded_data: list
|
|||
return payload
|
||||
|
||||
|
||||
def confirmed_data_result(blind_signatures: list) -> Result:
|
||||
if (
|
||||
blind_signatures == "error"
|
||||
or blind_signatures == None
|
||||
or blind_signatures == False
|
||||
):
|
||||
error_msg = f"The server's blind signature was blank or invalid. It said: {blind_signatures}"
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||
|
||||
return Result(valid=True, data=blind_signatures)
|
||||
|
||||
|
||||
def send_blind_commitments(
|
||||
list_of_all_blinded_data: list,
|
||||
ticket_observer: TicketObserver,
|
||||
connection_observer: ConnectionObserver,
|
||||
billing_code: Optional[int] = None
|
||||
) -> list | None:
|
||||
) -> Result:
|
||||
"""
|
||||
Purpose:
|
||||
1) Oversee payload prep
|
||||
|
|
@ -105,61 +120,121 @@ def send_blind_commitments(
|
|||
|
||||
# send it:
|
||||
url = make_url(which_endpoint)
|
||||
try:
|
||||
api_reply_object = connect.single_endpoint(
|
||||
method="post",
|
||||
url=url,
|
||||
observer=connection_observer,
|
||||
payload=payload
|
||||
)
|
||||
|
||||
# # did API call work?
|
||||
if not api_reply_object.valid:
|
||||
logger.error(f"[SEND BLIND COMMITMENTS] Second Post request failed. {api_reply_object.error_type} {api_reply_object.message}")
|
||||
ticket_observer.notify("error", subject=api_reply_object.message)
|
||||
return None
|
||||
|
||||
# assuming it worked, extract the data:
|
||||
reply = api_reply_object.data
|
||||
server_reply = api_call_and_extraction(
|
||||
desired_type=None, # get it all
|
||||
method=ApiCall.POST,
|
||||
url=url,
|
||||
connection_observer=connection_observer,
|
||||
payload=payload,
|
||||
return_server_reply=True # get it all
|
||||
)
|
||||
|
||||
# is it in valid format?
|
||||
if not isinstance(reply, dict):
|
||||
final_error_msg = f"API returned an invalid format {reply}"
|
||||
logger.error(final_error_msg)
|
||||
ticket_observer.notify("error", subject=final_error_msg)
|
||||
return None
|
||||
# this is a connection error or invalid format itself:
|
||||
if isinstance(server_reply, Result) or not isinstance(server_reply, ServerReply):
|
||||
ticket_observer.notify("error", subject=server_reply.message)
|
||||
return server_reply
|
||||
|
||||
if reply.get("valid") == True:
|
||||
# this 'signed_data' variable is a list,
|
||||
signed_data = reply.get("signed_data")
|
||||
# it's then sending it to the other functions,
|
||||
# to iterate through, when they save and unblind,
|
||||
return signed_data
|
||||
# Worked:
|
||||
if server_reply.valid:
|
||||
return confirmed_data_result(blind_signatures=server_reply.signed_data)
|
||||
|
||||
# but what if it didn't work?
|
||||
elif reply.get("valid") == False:
|
||||
error_msg = reply.get("message")
|
||||
error_msg_with_context = (
|
||||
f"Issues with Signing. Server's error message said: {error_msg}"
|
||||
error_type = server_reply.error_type
|
||||
message = server_reply.message
|
||||
ticket_observer.notify("error", subject=message)
|
||||
|
||||
logger.error(f"Send blind commitments failed. {str(error_type)} {message}")
|
||||
|
||||
# WRONG QUANTITY:
|
||||
if error_type == ServerError.QUANTITY:
|
||||
quantity_required = server_reply.quantity
|
||||
if not quantity_required or quantity_required < 1:
|
||||
error_msg = f"Server claims it got an invalid quantity, but yet it suggested to give it just {quantity_required} items."
|
||||
ticket_observer.notify("error", subject=error_msg)
|
||||
return Result(
|
||||
valid=False,
|
||||
error_type=ResultError.INVALID_INPUT,
|
||||
message=error_msg
|
||||
)
|
||||
ticket_observer.notify("error", subject=error_msg_with_context)
|
||||
return None
|
||||
|
||||
if reply == False:
|
||||
error_msg = f"It did not send. There's an error with sending the data to server. The payload either was or was going to be: {payload}"
|
||||
ticket_observer.notify("error", subject=error_msg)
|
||||
return None
|
||||
else:
|
||||
error_msg = "The JSON returned from the server is not formatted correctly to even be able to figure why it did not work. This is a critical error. Contact customer support"
|
||||
ticket_observer.notify("error", subject=error_msg)
|
||||
return None
|
||||
logger.info(f"Retrying with a different size of {quantity_required}.")
|
||||
resized_list = list_of_all_blinded_data[:quantity_required]
|
||||
resized_payload = prep_payload_for_bulk(resized_list)
|
||||
second_server_reply = api_call_and_extraction(
|
||||
desired_type="signed_data",
|
||||
method=ApiCall.POST,
|
||||
url=url,
|
||||
connection_observer=connection_observer,
|
||||
payload=resized_payload,
|
||||
return_server_reply=False
|
||||
)
|
||||
if not second_server_reply.valid:
|
||||
return second_server_reply
|
||||
return confirmed_data_result(blind_signatures=server_reply.signed_data)
|
||||
|
||||
except InvalidData as e:
|
||||
logger.error(f"InvalidData error: {e}", exc_info=True)
|
||||
return None
|
||||
except ServerSideError as e:
|
||||
logger.error(f"ServerSideError: {e}", exc_info=True)
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Generic Python Error {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
return translate_server_failure(server_reply=server_reply)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# # # did API call work?
|
||||
# if not api_reply_object.valid:
|
||||
# ticket_observer.notify("error", subject=api_reply_object.message)
|
||||
# return None
|
||||
|
||||
# # assuming it worked, extract the data:
|
||||
# reply = api_reply_object.data
|
||||
|
||||
# # is it in valid format?
|
||||
# if not isinstance(reply, dict):
|
||||
# final_error_msg = f"API returned an invalid format {reply}"
|
||||
# logger.error(final_error_msg)
|
||||
# ticket_observer.notify("error", subject=final_error_msg)
|
||||
# return None
|
||||
|
||||
# if reply.get("valid") == True:
|
||||
# # this 'signed_data' variable is a list,
|
||||
# signed_data = reply.get("signed_data")
|
||||
# # it's then sending it to the other functions,
|
||||
# # to iterate through, when they save and unblind,
|
||||
# return signed_data
|
||||
|
||||
# # but what if it didn't work?
|
||||
# elif reply.get("valid") == False:
|
||||
# error_msg = reply.get("message")
|
||||
# error_msg_with_context = (
|
||||
# f"Issues with Signing. Server's error message said: {error_msg}"
|
||||
# )
|
||||
# ticket_observer.notify("error", subject=error_msg_with_context)
|
||||
# return None
|
||||
|
||||
# if reply == False:
|
||||
# error_msg = f"It did not send. There's an error with sending the data to server. The payload either was or was going to be: {payload}"
|
||||
# ticket_observer.notify("error", subject=error_msg)
|
||||
# return None
|
||||
# else:
|
||||
# error_msg = "The JSON returned from the server is not formatted correctly to even be able to figure why it did not work. This is a critical error. Contact customer support"
|
||||
# ticket_observer.notify("error", subject=error_msg)
|
||||
# return None
|
||||
|
||||
|
||||
# except InvalidData as e:
|
||||
# logger.error(f"InvalidData error: {e}", exc_info=True)
|
||||
# return None
|
||||
# except ServerSideError as e:
|
||||
# logger.error(f"ServerSideError: {e}", exc_info=True)
|
||||
# return None
|
||||
# except Exception as e:
|
||||
# logger.error(f"Generic Python Error {e}", exc_info=True)
|
||||
# return None
|
||||
|
||||
|
||||
# api_reply_object = connect.single_endpoint(
|
||||
# method="post",
|
||||
# url=url,
|
||||
# observer=connection_observer,
|
||||
# payload=payload
|
||||
# )
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ from typing import TYPE_CHECKING
|
|||
if TYPE_CHECKING:
|
||||
from core.observers.TicketObserver import TicketObserver
|
||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||
# services
|
||||
|
||||
|
||||
from core.services.crypto.make_commitments import make_ALL_commitments
|
||||
from core.services.prepare_tickets.unblind_all_tickets import unblind_ALL_tickets
|
||||
from core.services.prepare_tickets.setup_ticket_tracker import setup_ticket_tracker
|
||||
from core.services.prepare_tickets import ticket_tracker
|
||||
# from core.services.prepare_tickets import ticket_tracker
|
||||
from core.services.prepare_tickets.send_blind_commitments import send_blind_commitments
|
||||
from core.services.prepare_tickets.validate_blind_signatures import validate_blind_signatures
|
||||
from core.services.prepare_tickets.save_ALL_blind_sigs import save_ALL_blind_sigs
|
||||
from core.utils.basic_operations.does_file_exist import does_file_exist
|
||||
from core.controllers.ConfigurationController import ConfigurationController
|
||||
|
||||
# errors & constants
|
||||
from core.utils.type_checker import type_checker
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.Constants import Constants
|
||||
from core.errors.exceptions import MissingData, CriticalFailure
|
||||
from core.errors.logger import logger
|
||||
|
||||
from typing import Optional
|
||||
|
|
@ -34,156 +34,159 @@ TicketPrepController's respawn_billing_code_into_ticket
|
|||
with which_ticket and billing_code. (and how_many_profiles is one)
|
||||
"""
|
||||
|
||||
REJECTED_INPUTS = [False, None, "error"]
|
||||
|
||||
|
||||
"""
|
||||
Called by:
|
||||
TicketPrepController's prepare_tickets = For Bulk Tickets
|
||||
TicketPrepController's respawn_billing_code_into_ticket = For a Single Ticket
|
||||
"""
|
||||
|
||||
@type_checker
|
||||
def ticket_prep_orchestrator(
|
||||
how_many_profiles: int,
|
||||
ticket_observer: TicketObserver,
|
||||
connection_observer: ConnectionObserver,
|
||||
which_ticket: Optional[int] = None,
|
||||
billing_code: Optional[int] = None
|
||||
) -> dict:
|
||||
"""
|
||||
Purpose:
|
||||
Orchestrate the entire ticket preparation operation.
|
||||
) -> Result:
|
||||
"""Orchestrate the entire ticket preparation operation."""
|
||||
|
||||
Rank:
|
||||
Feature's Primary Cross-Module Orchestrator
|
||||
|
||||
Called by:
|
||||
TicketPrepController's prepare_tickets = For Bulk Tickets
|
||||
TicketPrepController's respawn_billing_code_into_ticket = For a Single Ticket
|
||||
|
||||
Returns:
|
||||
Dicts. Shouldn't raise errors on it's own.
|
||||
"""
|
||||
if not billing_code and which_ticket:
|
||||
logger.error("Can't have no billing_code, if you have a ticket to replace")
|
||||
return {
|
||||
"valid": False,
|
||||
"message": "missing_data",
|
||||
}
|
||||
error_msg = "Can't have no billing_code, if you have a ticket to replace"
|
||||
logger.error(error_msg)
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||
|
||||
try:
|
||||
#####################################################
|
||||
# MAKE BLIND COMMITMENTS
|
||||
#####################################################
|
||||
# make all commitments, but get the blinded ones:
|
||||
list_of_all_blinded_commitments = make_ALL_commitments(how_many_profiles, which_ticket)
|
||||
#####################################################
|
||||
# MAKE BLIND COMMITMENTS
|
||||
#####################################################
|
||||
# make all commitments, but get the blinded ones:
|
||||
list_of_all_blinded_commitments = make_ALL_commitments(how_many_profiles, which_ticket)
|
||||
|
||||
# did we actually save them? if not, try again,
|
||||
if list_of_all_blinded_commitments is None:
|
||||
list_of_all_blinded_commitments = make_ALL_commitments(how_many_profiles, which_ticket)
|
||||
# still failed to save?!
|
||||
if list_of_all_blinded_commitments is None:
|
||||
return {
|
||||
"valid": False,
|
||||
"message": "cant_save",
|
||||
}
|
||||
if not list_of_all_blinded_commitments:
|
||||
error_msg = "We failed to make and save all the blinded commitments"
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||
|
||||
#####################################################
|
||||
# SEND BLIND TO GET SIGNED
|
||||
#####################################################
|
||||
# assuming we actually saved the unblinding factors,
|
||||
notification = "Sending Blinded Package to the Server.."
|
||||
#####################################################
|
||||
# SEND BLIND TO GET SIGNED
|
||||
#####################################################
|
||||
# assuming we actually saved the unblinding factors,
|
||||
notification = "Sending Blinded Package to the Server.."
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
|
||||
# then send the entire blinded list to the server to sign:
|
||||
bling_sig_result = send_blind_commitments(
|
||||
list_of_all_blinded_commitments, ticket_observer, connection_observer, billing_code
|
||||
)
|
||||
|
||||
if not bling_sig_result.valid:
|
||||
return bling_sig_result
|
||||
|
||||
# Recieve signatures:
|
||||
blind_signatures = bling_sig_result.data
|
||||
|
||||
#####################################################
|
||||
# EVALUATE AND SAVE THE SERVER'S REPLIES
|
||||
#####################################################
|
||||
# regardless of the outcome of the verification, save all blind sigs, just in case, because the user can't get them again,
|
||||
notification = "Saving the Server's Blind Replies"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
did_they_ALL_save = save_ALL_blind_sigs(blind_signatures, which_ticket)
|
||||
if not did_they_ALL_save:
|
||||
error_msg = "Failed to save the blind signatures locally! We continue with it in memory.."
|
||||
logger.error(error_msg)
|
||||
ticket_observer.notify("preparing", subject=error_msg)
|
||||
|
||||
#####################################################
|
||||
# VERIFY BLIND SIGS
|
||||
#####################################################
|
||||
# verify the server's blind signatures against the public key,
|
||||
notification = "Evaluating the Server's Blind Replies"
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
failed_validations = validate_blind_signatures(
|
||||
blind_signatures, ticket_observer, connection_observer, which_ticket
|
||||
)
|
||||
|
||||
logger.debug(f"failed_validations is {failed_validations}")
|
||||
|
||||
#####################################################
|
||||
# FAILED BLIND SIG VERIFICATION
|
||||
#####################################################
|
||||
# did verification of any of the blind sigs fail?
|
||||
how_many_failed = len(failed_validations)
|
||||
if how_many_failed >= 1:
|
||||
notification = f"Verification failed for {how_many_failed} blind signatures. You still have to unblind them but its paused for the error."
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
|
||||
# then send the entire blinded list to the server to sign:
|
||||
blind_signatures = send_blind_commitments(
|
||||
list_of_all_blinded_commitments, ticket_observer, connection_observer, billing_code
|
||||
logger.error(notification)
|
||||
return Result(
|
||||
valid=False,
|
||||
error_type=ResultError.MATH,
|
||||
data=failed_validations,
|
||||
message=notification
|
||||
)
|
||||
|
||||
# Recieve signatures:
|
||||
if (
|
||||
blind_signatures == "error"
|
||||
or blind_signatures == None
|
||||
or blind_signatures == False
|
||||
):
|
||||
return {
|
||||
"valid": False,
|
||||
"message": f"The server's blind signature was blank or invalid. It said: {blind_signatures}",
|
||||
"error_code": "invalid_reply"
|
||||
}
|
||||
#####################################################
|
||||
# PAID
|
||||
#####################################################
|
||||
# saving on config that it's paid:
|
||||
config_marked = ConfigurationController.mark_as_paid(True)
|
||||
if not config_marked:
|
||||
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message="Failed to mark as paid in the config. You still have the tickets locally, just can't use them till this file system error is resolved.")
|
||||
|
||||
else:
|
||||
#####################################################
|
||||
# EVALUATE AND SAVE THE SERVER'S REPLIES
|
||||
#####################################################
|
||||
# regardless of the outcome of the verification, save all blind sigs, just in case, because the user can't get them again,
|
||||
notification = "Saving the Server's Blind Replies"
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
did_they_ALL_save = save_ALL_blind_sigs(blind_signatures, which_ticket)
|
||||
#####################################################
|
||||
# UNBLIND
|
||||
#####################################################
|
||||
# Unblind the signatures & combine with unblinded commitment:
|
||||
notification = "Unblinding Signatures & Preparing Tickets..."
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
did_prep_work = unblind_ALL_tickets(
|
||||
ALL_signed_blind_signatures=blind_signatures,
|
||||
ticket_observer=ticket_observer,
|
||||
connection_observer=connection_observer,
|
||||
which_ticket=which_ticket
|
||||
)
|
||||
|
||||
# verify the server's blind signatures against the public key,
|
||||
notification = "Evaluating the Server's Blind Replies"
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
failed_validations = validate_blind_signatures(
|
||||
blind_signatures, ticket_observer, connection_observer, which_ticket
|
||||
)
|
||||
#####################################################
|
||||
# TICKET TRACKER & APP-WIDE CONFIG
|
||||
#####################################################
|
||||
if not does_file_exist(Constants.TICKET_TRACKER_PATH):
|
||||
setup_tracker = setup_ticket_tracker(how_many_profiles)
|
||||
if not setup_tracker:
|
||||
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message="Failed to setup the ticket tracker. You still have the tickets locally, just can't use them till this file system error is resolved.")
|
||||
|
||||
logger.debug(f"failed_validations is {failed_validations}")
|
||||
#####################################################
|
||||
# FINAL EVALUATION
|
||||
#####################################################
|
||||
if did_prep_work:
|
||||
# this means it unblinded, & setup the tracker without erroring out,
|
||||
# but it does NOT mean that verification of the blind sigs worked.
|
||||
ConfigurationController.tickets_setup(True)
|
||||
return Result(valid=True, message="Setup worked.")
|
||||
|
||||
# did verification of any of the blind sigs fail?
|
||||
how_many_failed = len(failed_validations)
|
||||
if how_many_failed >= 1:
|
||||
notification = f"Verification failed for {how_many_failed} blind signatures."
|
||||
if ticket_observer is not None:
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
logger.debug(
|
||||
f"Verification failed for {how_many_failed} blind signatures."
|
||||
)
|
||||
return {
|
||||
"valid": False,
|
||||
"message": "verification_failed",
|
||||
"how_many_failed": how_many_failed,
|
||||
"failed_validations": failed_validations,
|
||||
}
|
||||
|
||||
#####################################################
|
||||
# UNBLIND
|
||||
#####################################################
|
||||
# Unblind the signatures & combine with unblinded commitment:
|
||||
notification = f"Unblinding Signatures & Preparing Tickets..."
|
||||
ticket_observer.notify("preparing", subject=notification)
|
||||
did_prep_work = unblind_ALL_tickets(
|
||||
ALL_signed_blind_signatures=blind_signatures,
|
||||
ticket_observer=ticket_observer,
|
||||
connection_observer=connection_observer,
|
||||
which_ticket=which_ticket
|
||||
)
|
||||
else:
|
||||
error_msg = "You have the blinded data, but we were unable to unblind it."
|
||||
logger.error(error_msg)
|
||||
return Result(valid=False, error_type=ResultError.MATH, message=error_msg)
|
||||
|
||||
#####################################################
|
||||
# TICKET TRACKER & APP-WIDE CONFIG
|
||||
#####################################################
|
||||
if not does_file_exist(Constants.TICKET_TRACKER_PATH):
|
||||
setup_tracker = setup_ticket_tracker(how_many_profiles)
|
||||
if not setup_tracker:
|
||||
return {"valid": False, "message": "failed"}
|
||||
|
||||
# saving on config that it's setup:
|
||||
config_marked = ConfigurationController.mark_as_paid(True)
|
||||
if not config_marked:
|
||||
return {'valid': False, 'message': 'file_system'}
|
||||
|
||||
#####################################################
|
||||
# FINAL EVALUATION
|
||||
#####################################################
|
||||
if did_prep_work:
|
||||
# this means it unblinded, & setup the tracker without erroring out,
|
||||
# but it does NOT mean that verification of the blind sigs worked.
|
||||
return {"valid": True, "message": "worked"}
|
||||
else:
|
||||
return {"valid": False, "message": "failed"}
|
||||
|
||||
except CriticalFailure as e:
|
||||
error_msg = f"CriticalFailure error: {e}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
return {"valid": False, "message": error_msg}
|
||||
except MissingData as e:
|
||||
error_msg = f"MissingData: {e}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
return {"valid": False, "message": error_msg}
|
||||
except:
|
||||
error_msg = f"Generic Python Error"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
return {"valid": False, "message": error_msg}
|
||||
|
||||
|
||||
# except CriticalFailure as e:
|
||||
# error_msg = f"CriticalFailure error: {e}"
|
||||
# logger.error(error_msg, exc_info=True)
|
||||
# return {"valid": False, "message": error_msg}
|
||||
# except MissingData as e:
|
||||
# error_msg = f"MissingData: {e}"
|
||||
# logger.error(error_msg, exc_info=True)
|
||||
# return {"valid": False, "message": error_msg}
|
||||
# except:
|
||||
# error_msg = f"Generic Python Error"
|
||||
# logger.error(error_msg, exc_info=True)
|
||||
# return {"valid": False, "message": error_msg}
|
||||
|
|
|
|||
|
|
@ -314,13 +314,14 @@ def destroy_profile(
|
|||
# RESPAWN TICKET
|
||||
####################################
|
||||
which_ticket = profile.ticket
|
||||
respawn_result = respawn_profile(
|
||||
profile=profile,
|
||||
ticket_observer=ticket_observer,
|
||||
connection_observer=connection_observer
|
||||
)
|
||||
if which_ticket and not respawn_result.valid:
|
||||
logger.error(f"Unable to respawn the ticket. {respawn_result.message} {respawn_result.error_type}")
|
||||
if which_ticket:
|
||||
respawn_result = respawn_profile(
|
||||
profile=profile,
|
||||
ticket_observer=ticket_observer,
|
||||
connection_observer=connection_observer
|
||||
)
|
||||
if which_ticket and not respawn_result.valid:
|
||||
logger.error(f"Unable to respawn the ticket. {respawn_result.message} {respawn_result.error_type}")
|
||||
|
||||
####################################
|
||||
# DESTROY PROFILE
|
||||
|
|
|
|||
|
|
@ -573,7 +573,7 @@ def wipe_subscription(profile_id: int) -> Result:
|
|||
|
||||
|
||||
@type_checker
|
||||
def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime) -> Result:
|
||||
def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime, which_ticket: Optional[int] = None) -> Result:
|
||||
"""Checks if the billing code is already saved, and if not, saves it."""
|
||||
|
||||
logger.info("Saving billing code..")
|
||||
|
|
@ -597,6 +597,8 @@ def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime)
|
|||
expires_at=expires_at,
|
||||
billing_pending=True
|
||||
)
|
||||
if which_ticket:
|
||||
profile.ticket = which_ticket
|
||||
ProfileController.update(profile)
|
||||
return Result(valid=True)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -3,135 +3,339 @@ from typing import TYPE_CHECKING
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||
##############
|
||||
from core.services.using_tickets.send_unblinded import send_unblinded_ticket_to_server
|
||||
from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
|
||||
from core.services.prepare_tickets.ticket_tracker import update_ticket_tracker
|
||||
from core.models.Subscription import Subscription
|
||||
|
||||
from core.services.subscriptions.subscriptions_by_protocol import save_billing_code
|
||||
from core.utils.get_raw_string import get_raw_string
|
||||
from core.observers.TicketObserver import TicketObserver
|
||||
from core.services.helpers.does_ticket_file_exist import does_ticket_file_exist
|
||||
from core.services.prepare_tickets.ticket_tracker import does_ticket_tracker_exist
|
||||
from core.models.pydantic_models.ServerReply import ServerReply, ServerError
|
||||
from core.services.payment_phase.payment_state import TicketPaymentData
|
||||
from core.services.networking.make_url import make_url
|
||||
from core.models.manage.server_reply_tools import api_call_and_extraction, ApiCall, translate_server_failure
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.errors.logger import logger
|
||||
from core.Constants import Constants
|
||||
from core.models.enum_models import ProtocolChoice
|
||||
from core.utils.type_checker import type_checker
|
||||
from core.services.prepare_tickets.ticket_tracker import update_ticket_tracker
|
||||
from core.controllers.ProfileController import ProfileController
|
||||
|
||||
# from core.models.BaseProfile import BaseProfile as Profile
|
||||
# from core.services.using_tickets.send_unblinded import send_unblinded_ticket_to_server
|
||||
# from core.models.Subscription import Subscription
|
||||
# from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
CUSTOMER_SERVICE_MSG = "Please speak with customer service."
|
||||
|
||||
@type_checker
|
||||
def save_ticket_use(
|
||||
profile_id: int,
|
||||
billing_code: str,
|
||||
expires_at: datetime,
|
||||
location_id: int,
|
||||
which_ticket: int
|
||||
) -> Result:
|
||||
|
||||
saved_for_profile = save_billing_code(
|
||||
profile_id=profile_id,
|
||||
billing_code=billing_code,
|
||||
expires_at=expires_at,
|
||||
which_ticket=which_ticket
|
||||
)
|
||||
|
||||
# update the ticket tracker:
|
||||
saved_ticket_tracker = update_ticket_tracker(
|
||||
which_ticket=which_ticket,
|
||||
status="used",
|
||||
location=location_id,
|
||||
subscription=billing_code
|
||||
)
|
||||
|
||||
if not saved_for_profile.valid:
|
||||
return saved_for_profile # result object
|
||||
|
||||
if saved_ticket_tracker:
|
||||
return Result(valid=True)
|
||||
else:
|
||||
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message="Unable to update the ticket tracker that the ticket is used.")
|
||||
|
||||
|
||||
def make_payload(which_ticket: int, which_location: int, protocol_choice: ProtocolChoice) -> Result:
|
||||
"""Prepare the JSON to send to the billing server"""
|
||||
# ticket
|
||||
ticket_data = get_raw_string(str(which_ticket), "unblinded_final_ticket")
|
||||
|
||||
# key
|
||||
which_key = TicketPaymentData().which_key()
|
||||
if not which_key:
|
||||
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="Which ticket billing key is missing from your config file. Did you wipe it?")
|
||||
|
||||
# prep the JSON payload:
|
||||
payload = {
|
||||
"which_key": which_key,
|
||||
"which_location": which_location,
|
||||
"protocol_choice": protocol_choice.value,
|
||||
"ticket_data": ticket_data,
|
||||
}
|
||||
return Result(valid=True, data=payload)
|
||||
|
||||
|
||||
@type_checker
|
||||
def use_ticket_orchestrator(
|
||||
profile_id: int,
|
||||
which_ticket: int,
|
||||
which_location: int, # Location.id
|
||||
protocol_choice: ProtocolChoice,
|
||||
ticket_observer: TicketObserver,
|
||||
connection_observer: ConnectionObserver,
|
||||
) -> Result:
|
||||
"""
|
||||
Purpose:
|
||||
Coordinates using a ticket.
|
||||
Rank:
|
||||
Orchestrator
|
||||
Method:
|
||||
1) Send to the server
|
||||
2) Evaluate the response
|
||||
3) updates the 'ticker_tracker' JSON with new data
|
||||
4) Formats error messages for UI
|
||||
Called by:
|
||||
UseTicketController
|
||||
Returns:
|
||||
Return Objects
|
||||
do NOT raise errors
|
||||
"""
|
||||
|
||||
###################################
|
||||
# SEND TO THE SERVER
|
||||
# PROFILE DATA
|
||||
###################################
|
||||
api_reply_object = send_unblinded_ticket_to_server(
|
||||
which_ticket, which_location, protocol_choice, connection_observer
|
||||
profile = ProfileController.get(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)
|
||||
|
||||
if profile.ticket:
|
||||
error_msg = f"This profile already has a ticket of {profile.ticket}."
|
||||
logger.error(error_msg)
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||
|
||||
location_id = profile.location.id
|
||||
protocol_choice = ProtocolChoice(profile.connection.code)
|
||||
PRE_REQS = [profile_id, location_id, protocol_choice, which_ticket]
|
||||
if None in PRE_REQS:
|
||||
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="You are missing the profile data to use a ticket with.")
|
||||
|
||||
###################################
|
||||
# FILTER TICKET DATA
|
||||
###################################
|
||||
# does the ticket's file exist:
|
||||
ticket_exists = does_ticket_file_exist(which_ticket)
|
||||
if ticket_exists == False:
|
||||
error_msg = "The ticket file does not exist in the correct folder."
|
||||
ticket_observer.notify("failed_input", subject=error_msg)
|
||||
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
|
||||
|
||||
# does the file keeping track of ALL tickets exist:
|
||||
does_the_file_exist, path_of_file = does_ticket_tracker_exist()
|
||||
if does_the_file_exist == False:
|
||||
error_msg = f"The ticket tracker organizer file does not exist. Check the folder {path_of_file}"
|
||||
ticket_observer.notify("failed_input", subject=error_msg)
|
||||
return Result(valid=False, error_type=ResultError.MISSING_FILE, message=error_msg)
|
||||
|
||||
###################################
|
||||
# PREP PAYLOAD
|
||||
###################################
|
||||
payload_result = make_payload(which_ticket=which_ticket, protocol_choice=protocol_choice)
|
||||
if not payload_result.valid:
|
||||
return payload_result
|
||||
payload = payload_result.data
|
||||
logger.info("Payload ready.")
|
||||
|
||||
###################################
|
||||
# SEND TO API
|
||||
###################################
|
||||
which_endpoint = "validate"
|
||||
url = make_url(which_endpoint)
|
||||
|
||||
ticket_observer.notify("connecting", "Connecting..")
|
||||
server_reply = api_call_and_extraction(
|
||||
desired_type=None,
|
||||
method=ApiCall.POST,
|
||||
url=url,
|
||||
connection_observer=connection_observer,
|
||||
payload=payload,
|
||||
return_server_reply=True
|
||||
)
|
||||
|
||||
# NETWORK ERROR.
|
||||
if not api_reply_object.valid:
|
||||
return Result(valid=False, error_type=ResultError.CONNECTION, message=f"Could not establish a connection to Ticketing Server. {api_reply_object.message}.")
|
||||
# this is a connection error or invalid format itself:
|
||||
if isinstance(server_reply, Result) or not isinstance(server_reply, ServerReply):
|
||||
ticket_observer.notify("error", subject=server_reply.message)
|
||||
return server_reply
|
||||
|
||||
###################################
|
||||
# VERIFY DATA STRUCTURE
|
||||
# SUCCESS
|
||||
###################################
|
||||
raw_data_dict = api_reply_object.data
|
||||
if server_reply.valid or server_reply.error_type == ServerError.ALREADY_USED:
|
||||
billing_code = server_reply.billing_code
|
||||
expires_at = server_reply.expires_at
|
||||
|
||||
# INVALID STRUCTURE:
|
||||
if not isinstance(raw_data_dict, dict):
|
||||
error_msg = f"The Ticketing Server gave an invalid data format {type(raw_data_dict)} in it's reply."
|
||||
logger.error(f"{error_msg} with raw value of {raw_data_dict}.")
|
||||
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=f"{error_msg} {CUSTOMER_SERVICE_MSG}")
|
||||
if not billing_code or not expires_at:
|
||||
return Result(valid=False, error_type=ResultError.SERVER_SIDE, message="Server Replied it worked, but failed to provide a valid code.")
|
||||
|
||||
if "valid" not in raw_data_dict:
|
||||
error_msg = "The Ticketing Server gave an invalid data format that won't even tell us if it's valid or not."
|
||||
logger.error(f"{error_msg} with a raw value of {raw_data_dict}")
|
||||
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=f"{error_msg} {CUSTOMER_SERVICE_MSG}")
|
||||
|
||||
logger.debug("If we made it to this point, the API server connected and gave a correctly structured reply.")
|
||||
|
||||
###################################
|
||||
# EXTRACT & ACT
|
||||
###################################
|
||||
valid = raw_data_dict.get("valid", None)
|
||||
billing_code = raw_data_dict.get("billing_code", None)
|
||||
expires_at = raw_data_dict.get("expires_at", None)
|
||||
message = raw_data_dict.get("message", None)
|
||||
|
||||
logger.info(f"Was the ticket use valid? {valid}")
|
||||
|
||||
# GOOD OR ALREADY USED
|
||||
if valid or message == "already_used":
|
||||
logger.info("This is a valid use, or a previously valid use that's still good.")
|
||||
|
||||
# update the ticket tracker:
|
||||
saved = update_ticket_tracker(
|
||||
which_ticket=which_ticket,
|
||||
status="used",
|
||||
location=which_location,
|
||||
subscription=billing_code
|
||||
)
|
||||
|
||||
# Emergency:
|
||||
if not saved:
|
||||
emergency_save_billing(
|
||||
which_ticket=which_ticket,
|
||||
billing_code=billing_code
|
||||
)
|
||||
|
||||
subscription = Subscription(
|
||||
return save_ticket_use(
|
||||
profile_id=profile_id,
|
||||
billing_code=billing_code,
|
||||
expires_at=expires_at,
|
||||
location_id=location_id,
|
||||
which_ticket=which_ticket
|
||||
)
|
||||
|
||||
# even if not saved return it back, so it can be saved with the profile by other functions,
|
||||
return Result(valid=True, data=subscription)
|
||||
|
||||
###################################
|
||||
# EXPIRED
|
||||
if not valid and message == "expired":
|
||||
logger.error(f"Expired Ticket! Wiping slot {which_ticket}")
|
||||
saved = update_ticket_tracker(
|
||||
which_ticket=which_ticket,
|
||||
status="unused",
|
||||
location=None,
|
||||
subscription=None
|
||||
)
|
||||
if not saved:
|
||||
logger.error(f"Failed to save {which_ticket} is expired! That's okay the data is worthless to save.")
|
||||
return Result(valid=False, error_type=ResultError.SUBSCRIPTION, message=f"This ticket {which_ticket}'s subscription is expired. Do you want to wipe it?")
|
||||
###################################
|
||||
if server_reply.error_type == ServerError.EXPIRED:
|
||||
error_msg = "This ticket is expired. Do you want to wipe ALL your tickets? To start over."
|
||||
return Result(valid=False, error_type=ResultError.TICKET_EXPIRED, message=error_msg)
|
||||
|
||||
# UNKNOWN
|
||||
return Result(valid=False, error_type=ResultError.UNKNOWN, message=f"Unknown error with this data: {raw_data_dict}. {CUSTOMER_SERVICE_MSG}")
|
||||
###################################
|
||||
# WRONG LOCATION
|
||||
###################################
|
||||
if server_reply.error_type == ServerError.WRONG_LOCATION:
|
||||
error_msg = "The location entered is invalid. Do you want to sync location data now?"
|
||||
return Result(valid=False, error_type=ResultError.NEED_SYNC, message=error_msg)
|
||||
|
||||
###################################
|
||||
# NODE DOWN
|
||||
###################################
|
||||
if server_reply.error_type == ServerError.NODE_ERROR:
|
||||
error_msg = f"{server_reply.message}. Do you want to switch locations for this profile?"
|
||||
return Result(valid=False, error_type=ResultError.NODE_ERROR, message=error_msg) # Each GUI will navigate to edit.
|
||||
|
||||
###################################
|
||||
# NO SOLUTION:
|
||||
###################################
|
||||
return translate_server_failure(server_reply=server_reply)
|
||||
|
||||
|
||||
def emergency_save_billing(
|
||||
which_ticket: int,
|
||||
billing_code: str
|
||||
) -> None:
|
||||
docstring = """
|
||||
If ticket tracker fails to save it to the JSON. Emergency save to a random text file.
|
||||
"""
|
||||
logger.error(docstring)
|
||||
which_ticket_as_str = str(which_ticket)
|
||||
write_string_to_text_file(
|
||||
billing_code,
|
||||
f"{Constants.HV_TICKETING_CONFIG_HOME}/emergency_code_for_profile_{which_ticket_as_str}.txt",
|
||||
)
|
||||
|
||||
# # # the actual work here, everything else is just handling:
|
||||
# # ticket_observer.notify("connecting", "Connecting..")
|
||||
# # reply_object = use_ticket_orchestrator(
|
||||
# # which_ticket=which_ticket,
|
||||
# # which_location=which_location,
|
||||
# # protocol_choice=protocol_choice,
|
||||
# # connection_observer=connection_observer
|
||||
# # )
|
||||
# # return reply_object
|
||||
|
||||
|
||||
# @type_checker
|
||||
# def use_ticket_orchestrator(
|
||||
# which_ticket: int,
|
||||
# which_location: int, # Location.id
|
||||
# protocol_choice: ProtocolChoice,
|
||||
# connection_observer: ConnectionObserver,
|
||||
# ) -> Result:
|
||||
# pass
|
||||
|
||||
# """
|
||||
# Purpose:
|
||||
# Coordinates using a ticket.
|
||||
# Method:
|
||||
# 1) Send to the server
|
||||
# 2) Evaluate the response
|
||||
# 3) updates the 'ticker_tracker' JSON with new data
|
||||
# 4) Formats error messages for UI
|
||||
# """
|
||||
|
||||
|
||||
|
||||
# save_billing_code(profile_id: int, billing_code: str, expires_at: datetime)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ###################################
|
||||
# # SEND TO THE SERVER
|
||||
# ###################################
|
||||
# api_reply_object = send_unblinded_ticket_to_server(
|
||||
# which_ticket, which_location, protocol_choice, connection_observer
|
||||
# )
|
||||
|
||||
# # NETWORK ERROR.
|
||||
# if not api_reply_object.valid:
|
||||
# return Result(valid=False, error_type=ResultError.CONNECTION, message=f"Could not establish a connection to Ticketing Server. {api_reply_object.message}.")
|
||||
|
||||
# ###################################
|
||||
# # VERIFY DATA STRUCTURE
|
||||
# ###################################
|
||||
# raw_data_dict = api_reply_object.data
|
||||
|
||||
# # INVALID STRUCTURE:
|
||||
# if not isinstance(raw_data_dict, dict):
|
||||
# error_msg = f"The Ticketing Server gave an invalid data format {type(raw_data_dict)} in it's reply."
|
||||
# logger.error(f"{error_msg} with raw value of {raw_data_dict}.")
|
||||
# return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=f"{error_msg} {CUSTOMER_SERVICE_MSG}")
|
||||
|
||||
# if "valid" not in raw_data_dict:
|
||||
# error_msg = "The Ticketing Server gave an invalid data format that won't even tell us if it's valid or not."
|
||||
# logger.error(f"{error_msg} with a raw value of {raw_data_dict}")
|
||||
# return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=f"{error_msg} {CUSTOMER_SERVICE_MSG}")
|
||||
|
||||
# logger.debug("If we made it to this point, the API server connected and gave a correctly structured reply.")
|
||||
|
||||
# ###################################
|
||||
# # EXTRACT & ACT
|
||||
# ###################################
|
||||
# valid = raw_data_dict.get("valid", None)
|
||||
# billing_code = raw_data_dict.get("billing_code", None)
|
||||
# expires_at = raw_data_dict.get("expires_at", None)
|
||||
# message = raw_data_dict.get("message", None)
|
||||
|
||||
# logger.info(f"Was the ticket use valid? {valid}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# # GOOD OR ALREADY USED
|
||||
# if valid or message == "already_used":
|
||||
# logger.info("This is a valid use, or a previously valid use that's still good.")
|
||||
|
||||
|
||||
# def save_billing(billing_code: str):
|
||||
|
||||
# # Emergency:
|
||||
# if not saved:
|
||||
# emergency_save_billing(
|
||||
# which_ticket=which_ticket,
|
||||
# billing_code=billing_code
|
||||
# )
|
||||
|
||||
# subscription = Subscription(
|
||||
# billing_code=billing_code,
|
||||
# expires_at=expires_at,
|
||||
# )
|
||||
|
||||
# # even if not saved return it back, so it can be saved with the profile by other functions,
|
||||
# return Result(valid=True, data=subscription)
|
||||
|
||||
# # EXPIRED
|
||||
# if not valid and message == "expired":
|
||||
# logger.error(f"Expired Ticket! Wiping slot {which_ticket}")
|
||||
# saved = update_ticket_tracker(
|
||||
# which_ticket=which_ticket,
|
||||
# status="unused",
|
||||
# location=None,
|
||||
# subscription=None
|
||||
# )
|
||||
# if not saved:
|
||||
# logger.error(f"Failed to save {which_ticket} is expired! That's okay the data is worthless to save.")
|
||||
# return Result(valid=False, error_type=ResultError.SUBSCRIPTION, message=f"This ticket {which_ticket}'s subscription is expired. Do you want to wipe it?")
|
||||
|
||||
# # UNKNOWN
|
||||
# return Result(valid=False, error_type=ResultError.UNKNOWN, message=f"Unknown error with this data: {raw_data_dict}. {CUSTOMER_SERVICE_MSG}")
|
||||
|
||||
|
||||
# def emergency_save_billing(
|
||||
# which_ticket: int,
|
||||
# billing_code: str
|
||||
# ) -> None:
|
||||
# docstring = """
|
||||
# If ticket tracker fails to save it to the JSON. Emergency save to a random text file.
|
||||
# """
|
||||
# logger.error(docstring)
|
||||
# which_ticket_as_str = str(which_ticket)
|
||||
# write_string_to_text_file(
|
||||
# billing_code,
|
||||
# f"{Constants.HV_TICKETING_CONFIG_HOME}/emergency_code_for_profile_{which_ticket_as_str}.txt",
|
||||
# )
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue