示例#1
0
 def __init__(self, p_client_id: str, p_log: Log,
              p_passenger_factory: AbstractPassengerFactory,
              p_args: JsonDatabaseArguments):
     self.client_id = p_client_id
     self._log = p_log
     self._passenger_factory = p_passenger_factory
     self._args = p_args
     self._path = JsonPathBuilder(self.client_id, self._args)
示例#2
0
 def create_client_if_missing(self, p_client_id: str, p_base_dict: dict):
     """ Creates the client folder structure if not present """
     path_builder = JsonPathBuilder(p_client_id, self._args)
     pathlib.Path(path_builder.client_dir_path).mkdir(parents=True,
                                                      exist_ok=True)
     pathlib.Path(path_builder.log_root_path).mkdir(parents=True,
                                                    exist_ok=True)
     pathlib.Path(path_builder.queue_root_path).mkdir(parents=True,
                                                      exist_ok=True)
     if not path.exists(path_builder.config_file_path):
         self._save_config_dict(p_client_id, p_base_dict)
示例#3
0
 def client_directories(self) -> List[str]:
     """ Returns all client directories """
     client_root_path = JsonPathBuilder.get_client_root_path(self._args)
     return [f.name for f in scandir(client_root_path) if f.is_dir()]
示例#4
0
 def _get_config_file_path(self, p_client_id: str) -> str:
     path_builder = JsonPathBuilder(p_client_id, self._args)
     return path_builder.config_file_path
示例#5
0
 def _delete_client(self, p_client_id: str):
     path_builder = JsonPathBuilder(p_client_id, self._args)
     rmtree(path_builder.client_dir_path)
示例#6
0
class JsonQueue:
    """ Queue implementation for JSON database """
    class DataOperation(Enum):
        """ Data operation enum """
        insert = 1
        update = 2

    def __init__(self, p_client_id: str, p_log: Log,
                 p_passenger_factory: AbstractPassengerFactory,
                 p_args: JsonDatabaseArguments):
        self.client_id = p_client_id
        self._log = p_log
        self._passenger_factory = p_passenger_factory
        self._args = p_args
        self._path = JsonPathBuilder(self.client_id, self._args)

    def delete_passengers(self, p_passengers: List[AbstractPassenger]):
        """ Deletes the given passengers from the disk """
        all_passenger_directories = self._path.passenger_directories
        for passenger in p_passengers:
            if passenger.internal_id in all_passenger_directories:
                self._log.append_text(
                    f"Deleting passenger directory {passenger.internal_id}")
                passenger_dir_path = self._path.get_passenger_directory_path(
                    passenger.internal_id)
                shutil.rmtree(passenger_dir_path)

    def erase_passenger_queue(self):
        """ Deletes all passengers from the disk """
        all_passenger_directories = self._path.passenger_directories
        for passenger_directory in all_passenger_directories:
            self._log.append_text(
                f"Deleting passenger directory {passenger_directory}")
            passenger_dir_path = self._path.get_passenger_directory_path(
                passenger_directory)
            shutil.rmtree(passenger_dir_path)

    def get_passengers(
            self,  # pylint: disable=R0912, R0913, R0914
            p_passenger_module: str = None,
            p_processor_status: QueueStatus = None,
            p_pusher_status: QueueStatus = None,
            p_puller_notified: bool = None,
            p_pulled_before: datetime = None,
            p_internal_id: str = None) -> List[PassengerQueueStatus]:
        """ Returns passengers """
        output = []

        for passenger_directory in self._path.passenger_directories:
            if p_internal_id is not None and passenger_directory != p_internal_id:
                continue

            passenger_json = self._get_passenger_file_as_json(
                passenger_directory)
            pull_datetime = JsonToolkit.convert_json_date_to_datetime(
                passenger_json["pull_datetime"])  # pylint: disable=C0301

            if p_pusher_status is not None and len(
                    passenger_json["pusher_statuses"]) > 0:
                a_pusher_found = False
                for pusher_status in passenger_json["pusher_statuses"]:
                    if pusher_status["status"] == p_pusher_status.name:
                        a_pusher_found = True
                        break
                if not a_pusher_found:
                    continue

            if p_processor_status is not None and len(
                    passenger_json["processor_statuses"]) > 0:
                a_processor_found = False
                for processor_status in passenger_json["processor_statuses"]:
                    if processor_status["status"] == p_processor_status.name:
                        a_processor_found = True
                        break
                if not a_processor_found:
                    continue

            if p_passenger_module is not None and passenger_json[
                    "passenger_module"] != p_passenger_module:  # pylint: disable=C0301
                continue

            if p_puller_notified is not None and passenger_json[
                    "puller_notified"] != p_puller_notified:  # pylint: disable=C0301
                continue

            if p_pulled_before is not None and pull_datetime > p_pulled_before:
                continue

            passenger_obj = self._passenger_factory.create_passenger(
                passenger_json["passenger_module"])  # pylint: disable=C0301
            passenger_obj.internal_id = passenger_json["internal_id"]
            passenger_obj.external_id = passenger_json["external_id"]
            passenger_obj.source_system = passenger_json["source_system"]
            passenger_obj.puller_module = passenger_json["puller_module"]
            passenger_obj.pull_datetime = pull_datetime
            self._log.append_text(f"Found passenger {passenger_obj.id_text}")

            for attachment_json in passenger_json["attachments"]:
                attachment_obj = self._get_attachment_obj(
                    passenger_obj.internal_id, attachment_json)
                passenger_obj.attachments.append(attachment_obj)

            if "log_guids" in passenger_json:
                for log_guid in passenger_json["log_guids"]:
                    passenger_obj.collect_log_guid(uuid.UUID(log_guid))

            paqs = PassengerQueueStatus(p_passenger=passenger_obj,
                                        p_pusher_statuses=[],
                                        p_processor_statuses=[])

            paqs.puller_notified = passenger_json["puller_notified"]

            for processor_status in passenger_json["processor_statuses"]:
                paqs.processor_statuses.append(
                    ProcessorQueueStatus(
                        p_processor_module=processor_status[
                            "processor_module"],
                        p_status=QueueStatus[processor_status["status"]]))

            for pusher_status in passenger_json["pusher_statuses"]:
                paqs.pusher_statuses.append(
                    PusherQueueStatus(
                        p_pusher_module=pusher_status["pusher_module"],
                        p_status=QueueStatus[pusher_status["status"]]))

            output.append(paqs)
        return output

    def insert_passenger(self, p_passenger_status: PassengerQueueStatus):
        """ Creates a new folder & puts files within """
        self._log.append_text(
            f"Adding passenger {p_passenger_status.passenger.id_text} to queue"
        )  # pylint: disable=C0301
        self._validate_passenger_status(p_passenger_status,
                                        JsonQueue.DataOperation.insert)

        passenger_dict = {
            "external_id": p_passenger_status.passenger.external_id,
            "internal_id": str(p_passenger_status.passenger.internal_id),
            "source_system": p_passenger_status.passenger.source_system,
            "passenger_module": p_passenger_status.passenger.passenger_module,
            "puller_module": p_passenger_status.passenger.puller_module,
            "puller_notified": p_passenger_status.puller_notified,
            "pull_datetime":
            p_passenger_status.passenger.pull_datetime.isoformat(),
            "attachments": [],
            "log_guids": [],
            "processor_statuses": [],
            "pusher_statuses": []
        }

        mkdir(
            self._path.get_passenger_directory_path(
                passenger_dict["internal_id"]))
        mkdir(
            self._path.get_attachment_directory_path(
                passenger_dict["internal_id"]))

        for attachment in p_passenger_status.passenger.attachments:
            attachment_dict = {
                "format": attachment.format.name,
                "name": attachment.name
            }
            passenger_dict["attachments"].append(attachment_dict)

            if attachment.format == AttachmentFormat.binary:
                self._write_attachment_file_bin(attachment_dict["name"],
                                                attachment.binary_content,
                                                passenger_dict["internal_id"])
            elif attachment.format == AttachmentFormat.text:
                self._write_attachment_file_text(attachment_dict["name"],
                                                 attachment.text_content,
                                                 passenger_dict["internal_id"])
            else:
                raise AttachmentError(AttachmentError.ErrorCode.invalid_format,
                                      attachment.format)

        for guid in p_passenger_status.passenger.log_guids:
            passenger_dict["log_guids"].append(str(guid))

        for processor_status in p_passenger_status.processor_statuses:
            processor_status_dict = {
                "processor_module": processor_status.processor_module,
                "status": processor_status.status.name
            }
            passenger_dict["processor_statuses"].append(processor_status_dict)

        for pusher_status in p_passenger_status.pusher_statuses:
            pusher_status_dict = {
                "pusher_module": pusher_status.pusher_module,
                "status": pusher_status.status.name
            }
            passenger_dict["pusher_statuses"].append(pusher_status_dict)

        self._write_passenger_json_into_file(passenger_dict)

    def set_processor_status(self, p_passenger: AbstractPassenger,
                             p_processor_module: str, p_status: QueueStatus):
        """ Updates processor status within the JSON file """

        self._log.append_text("Setting status " + p_status.name +
                              " for passenger " + p_passenger.id_text +
                              " processor " + p_processor_module)

        passenger_json = self._get_passenger_file_as_json(
            p_passenger.internal_id)

        for processor_status in passenger_json["processor_statuses"]:
            if processor_status["processor_module"] == p_processor_module:
                processor_status["status"] = p_status.name

        self._write_passenger_json_into_file(passenger_json)

    def set_pusher_status(self, p_passenger: AbstractPassenger,
                          p_pusher_module: str, p_status: QueueStatus):
        """ Updates pusher status within the JSON file """
        self._log.append_text("Setting status " + p_status.name +
                              " for passenger " + p_passenger.id_text +
                              " pusher " + p_pusher_module)

        passenger_json = self._get_passenger_file_as_json(
            p_passenger.internal_id)

        for pusher_status in passenger_json["pusher_statuses"]:
            if pusher_status["pusher_module"] == p_pusher_module:
                pusher_status["status"] = p_status.name

        self._write_passenger_json_into_file(passenger_json)

    def update_passenger(self, p_passenger_status: PassengerQueueStatus):
        """ Updates passenger """
        self._log.append_text(
            f"Updating passenger {p_passenger_status.passenger.id_text}")
        self._validate_passenger_status(p_passenger_status,
                                        JsonQueue.DataOperation.update)
        self.delete_passengers([p_passenger_status.passenger])
        self.insert_passenger(p_passenger_status)

    def _delete_attachment_file(self, p_file_name: str, p_internal_id: str):
        self._log.append_text(f"Deleting attachment {p_file_name}")

        full_path = self._path.get_attachment_file_path(
            p_internal_id, p_file_name)
        if not path.exists(full_path):
            self._log.append_entry(
                LogEntry(p_message="File not found",
                         p_type=MessageType.warning))
            return
        remove(full_path)

    def _get_attachment_obj(self, p_internal_id: str,
                            p_attachment_json: dict) -> Attachment:
        output = Attachment(
            p_name=p_attachment_json["name"],
            p_format=AttachmentFormat[p_attachment_json["format"]])

        full_path = self._path.get_attachment_file_path(
            p_internal_id, output.name)
        self._log.append_text(f"Reading attachment from disk: {full_path}")

        if output.format == AttachmentFormat.text:
            with open(full_path, "r", encoding="utf-8") as text_file:
                output.text_content = text_file.read()
        elif output.format == AttachmentFormat.binary:
            with open(full_path, "rb") as bin_file:
                output.binary_content = bin_file.read()
        else:
            raise AttachmentError(AttachmentError.ErrorCode.invalid_format,
                                  output.format)

        return output

    def _get_passenger_file_as_json(self, p_internal_id: str) -> dict:
        try:
            passenger_file_path = self._path.get_passenger_file_path(
                p_internal_id)
            self._log.append_text("Reading passenger file " +
                                  passenger_file_path)
            with open(passenger_file_path, encoding="utf-8") as json_file:
                passengers_json = json.load(json_file)
            return passengers_json
        except Exception as error:  # pylint: disable=W0703
            print(str(error))
            return {}

    def _validate_passenger_status(self,
                                   p_passenger_status: PassengerQueueStatus,
                                   p_operation: DataOperation):
        self._log.append_text(
            f"Validating passenger status for {p_passenger_status.passenger.id_text}"
        )  # pylint: disable=C0301

        if str(p_passenger_status.passenger.internal_id) == "":
            raise PassengerError(
                PassengerError.ErrorCode.internal_id_missing,
                p_passenger_id=p_passenger_status.passenger.id_text)

        QueueStatusValidator.validate_queue_module(
            "Puller", p_passenger_status.passenger.id_text,
            p_passenger_status.passenger.puller_module)

        for processor_status in p_passenger_status.processor_statuses:
            QueueStatusValidator.validate_queue_module(
                "Processor", p_passenger_status.passenger.id_text,
                processor_status.processor_module)

        for pusher_status in p_passenger_status.pusher_statuses:
            QueueStatusValidator.validate_queue_module(
                "Pusher", p_passenger_status.passenger.id_text,
                pusher_status.pusher_module)

        AttachmentValidator.validate_attachments(
            p_passenger_status.passenger.attachments)

        if p_operation == JsonQueue.DataOperation.insert and \
                p_passenger_status.passenger.internal_id in self._path.passenger_directories:
            raise PassengerError(PassengerError.ErrorCode.already_exists,
                                 p_passenger_status.passenger.id_text)

    def _write_attachment_file_text(self, p_file_name: str,
                                    p_file_content: str, p_internal_id: str):
        full_path = self._path.get_attachment_file_path(
            p_internal_id, p_file_name)
        self._log.append_text("Writing text attachment to disk: " + full_path)
        with open(full_path, "w", encoding="utf-8") as text_file:
            text_file.write(p_file_content)

    def _write_attachment_file_bin(self, p_file_name: str,
                                   p_file_content: bytearray,
                                   p_internal_id: str):
        full_path = self._path.get_attachment_file_path(
            p_internal_id, p_file_name)
        self._log.append_text(
            f"Writing binary attachment to disk: {full_path}")
        with open(full_path, "wb") as bin_file:
            bin_file.write(p_file_content)

    def _write_passenger_json_into_file(self, p_json: dict):
        passenger_file_path = self._path.get_passenger_file_path(
            p_json["internal_id"])
        self._log.append_text(
            f"Writing passenger file to disk: {passenger_file_path}")
        with open(passenger_file_path, "w", encoding="utf-8") as json_file:
            json.dump(p_json, json_file)
示例#7
0
 def _get_path_builder(self, p_client_id: str) -> JsonPathBuilder:
     return JsonPathBuilder(p_client_id, self._args)