diff --git a/.github/workflows/on-merge.yml b/.github/workflows/on-merge.yml index 161131e..22ca210 100644 --- a/.github/workflows/on-merge.yml +++ b/.github/workflows/on-merge.yml @@ -39,12 +39,11 @@ jobs: environment: ${{ steps.target.outputs.environment }} pypi_url: ${{ steps.target.outputs.url }} steps: - # - uses: googleapis/release-please-action@v4 - - uses: google-github-actions/release-please-action@v4 + - uses: googleapis/release-please-action@v4 id: release with: release-type: python - token: ${{ secrets.PAT_TOKEN }} + token: ${{ secrets.PPAT_TOKEN }} target-branch: ${{ github.ref_name }} - name: determine target environment id: target diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0967ef4..6bbfeb6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{} +{".":"0.5.0"} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fd191bb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +## [0.5.0](https://github.com/tagshelfsrl/alfred-python/compare/v0.4.0...v0.5.0) (2024-05-20) + + +### Features + +* implement realtime functionality. ([#18](https://github.com/tagshelfsrl/alfred-python/issues/18)) ([1b0d070](https://github.com/tagshelfsrl/alfred-python/commit/1b0d070932db0d46bef20546a78bdb70a4ccebf4)) + + +### Bug Fixes + +* move the alfred package up in the hierarchy out of the src/ folder. ([#36](https://github.com/tagshelfsrl/alfred-python/issues/36)) ([fa5a845](https://github.com/tagshelfsrl/alfred-python/commit/fa5a8452b573ea09c1ea66438b84082f257a727f)) +* release-please-action org rename and typo ([#37](https://github.com/tagshelfsrl/alfred-python/issues/37)) ([c0fd44c](https://github.com/tagshelfsrl/alfred-python/commit/c0fd44cc32581323dad5c635b9a74a304157908d)) + +## [0.4.0](https://github.com/tagshelfsrl/alfred-python/compare/v0.3.0...v0.4.0) (2024-05-14) + + +### Features + +* add job domain ([#9](https://github.com/tagshelfsrl/alfred-python/issues/9)) ([aa9528c](https://github.com/tagshelfsrl/alfred-python/commit/aa9528c18d35c2789fcda0590a7f270a691bc8f5)) +* Add LICENSE and pyproject.toml configuration ([#1](https://github.com/tagshelfsrl/alfred-python/issues/1)) ([e674ba6](https://github.com/tagshelfsrl/alfred-python/commit/e674ba62777ea05fb5b0ebf4ed91c578dd3a02fa)) +* **AL-860:** Implement base HTTP client class ([#3](https://github.com/tagshelfsrl/alfred-python/issues/3)) ([da0d92e](https://github.com/tagshelfsrl/alfred-python/commit/da0d92e19b7279f18eec918195f5ca76bc1f09c7)) +* **AL-866:** Implement authentication methods supported in Alfred ([#4](https://github.com/tagshelfsrl/alfred-python/issues/4)) ([25c1530](https://github.com/tagshelfsrl/alfred-python/commit/25c153060129b73ef8a0511c28b98c5471905dee)) +* **AL-867:** Implement OAuth refresh token handler ([#5](https://github.com/tagshelfsrl/alfred-python/issues/5)) ([adc29d5](https://github.com/tagshelfsrl/alfred-python/commit/adc29d5562bf74dec97b3428e4dcb8bebfd201f5)) +* **AL-869:** Implemented Data Points domain ([#6](https://github.com/tagshelfsrl/alfred-python/issues/6)) ([faf0d09](https://github.com/tagshelfsrl/alfred-python/commit/faf0d09995d7a2ad563f7145c4e7c4b7395d367a)) +* **AL-870:** implement session domain ([#7](https://github.com/tagshelfsrl/alfred-python/issues/7)) ([823afa0](https://github.com/tagshelfsrl/alfred-python/commit/823afa0b415411cd4a0a824291ca3565676f54cf)) +* **AL-871:** Implement Files domain class and methods ([#14](https://github.com/tagshelfsrl/alfred-python/issues/14)) ([f34469d](https://github.com/tagshelfsrl/alfred-python/commit/f34469d8a0647691fc139da35bcc0376e3785651)) +* AL-886 enables pypi publishes ([#20](https://github.com/tagshelfsrl/alfred-python/issues/20)) ([85abecb](https://github.com/tagshelfsrl/alfred-python/commit/85abecb051e6304ccbf92f47ebc6834572df5880)) +* introduce response parsing based on configurable option at instance and endpoint level. ([#11](https://github.com/tagshelfsrl/alfred-python/issues/11)) ([cc0995d](https://github.com/tagshelfsrl/alfred-python/commit/cc0995d9e66c5ed4dc84761a1212d1c6d0ae6119)) +* setup functionality to apply throttling when the remaining requests get to a certain threshold. ([#15](https://github.com/tagshelfsrl/alfred-python/issues/15)) ([bceb818](https://github.com/tagshelfsrl/alfred-python/commit/bceb818570094daf258eb15ca5592047ec9f2808)) +* Update README documentation ([#12](https://github.com/tagshelfsrl/alfred-python/issues/12)) ([4724ddb](https://github.com/tagshelfsrl/alfred-python/commit/4724ddb82111be4f7ffa8ee08767cd81306a57b0)) + + +### Bug Fixes + +* adds missing permissions to publish job ([#23](https://github.com/tagshelfsrl/alfred-python/issues/23)) ([5884a24](https://github.com/tagshelfsrl/alfred-python/commit/5884a24c2803fc3f81250602036f27fd4720768b)) +* adds missing trailing slash ([3d5fb17](https://github.com/tagshelfsrl/alfred-python/commit/3d5fb17e72f692983122389e79e415e4195de875)) +* adds pypi test url for staging deployments ([495db8a](https://github.com/tagshelfsrl/alfred-python/commit/495db8a1159b7a2d9b82fcaa094330bf3bda1365)) +* **AL-869:** Set overrides as empty dict if value is None ([#8](https://github.com/tagshelfsrl/alfred-python/issues/8)) ([c446c89](https://github.com/tagshelfsrl/alfred-python/commit/c446c89ef0ca5f2152e1041ec5a007b7f89eae9e)) +* branch check and prod pypi url ([a6c6e64](https://github.com/tagshelfsrl/alfred-python/commit/a6c6e64bc30c6b432ec9faf06e6bee154b06c461)) +* Fixed bad imports ([#17](https://github.com/tagshelfsrl/alfred-python/issues/17)) ([ac4afbd](https://github.com/tagshelfsrl/alfred-python/commit/ac4afbdce6f0ded162d5fe5196a5d5804de9cc91)) +* prerelease-type set value to beta ([89178db](https://github.com/tagshelfsrl/alfred-python/commit/89178db752611b2e3e88f770bab60942a16f4bdf)) +* pypi wrong upload urls ([2181ece](https://github.com/tagshelfsrl/alfred-python/commit/2181eced8aa262a0f9bb6398d0711c2516fbe8b3)) +* trigger build ([e41149a](https://github.com/tagshelfsrl/alfred-python/commit/e41149a396a509a5e981887f410820c7cb6f5616)) +* trying to enable prerelease tags ([7b33846](https://github.com/tagshelfsrl/alfred-python/commit/7b33846b69d848b5032d23d7216cc190b79c2203)) +* trying to enable prerelease tags 2 ([fa998cd](https://github.com/tagshelfsrl/alfred-python/commit/fa998cd78bde051b097b9ebcb220f1b810de59c2)) +* trying to enable prereleases adding directly to action ([b332af1](https://github.com/tagshelfsrl/alfred-python/commit/b332af17bda35f80e88315892550ce56bf426017)) +* typo on github actions permissions ([4f8a49e](https://github.com/tagshelfsrl/alfred-python/commit/4f8a49e6f3270d59fe3d93ce6c335cf362d4108a)) +* ues old release-please-action until they finish changes ([#24](https://github.com/tagshelfsrl/alfred-python/issues/24)) ([8b039ae](https://github.com/tagshelfsrl/alfred-python/commit/8b039ae176d70cafc2d6037bd3c573973ad51900)) +* Updated domain methods to expect a tuple instead of a Response object ([#16](https://github.com/tagshelfsrl/alfred-python/issues/16)) ([6f28402](https://github.com/tagshelfsrl/alfred-python/commit/6f284020611c99fc4b2482b25fc51fe783cf37ac)) +* use old release-please ([577c888](https://github.com/tagshelfsrl/alfred-python/commit/577c888e97f4593f5477d80b7f95393acd49e9a4)) +* Use response headers to get the content-type instead of request ([#13](https://github.com/tagshelfsrl/alfred-python/issues/13)) ([e54eeab](https://github.com/tagshelfsrl/alfred-python/commit/e54eeab7bc32736210106b65b4c766cf9fe5133f)) +* versioning strategy adjustment ([55fa551](https://github.com/tagshelfsrl/alfred-python/commit/55fa551f812bdb3525bfa6f500c0b6f5a0585119)) +* versioning strategy config ([823d7c0](https://github.com/tagshelfsrl/alfred-python/commit/823d7c0cc86bcb07b896cfae29a806b5dab4aad0)) diff --git a/Makefile b/Makefile index 8d16433..8eb8279 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ clean: rm -rf build/ rm -rf dist/ - rm -rf src/*.egg-info/ - rm -rf src/alfred/__pycache__/ + rm -rf *.egg-info/ + rm -rf alfred/__pycache__/ build: python -m build diff --git a/README.md b/README.md index d6404a0..a9cb879 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ Check out this simple example to get up and running: ```python from alfred.rest import AlfredClient -from alfred.base.config import Configuration +from alfred.base import Configuration -config = Configuration.v1() +config = Configuration.default() auth_config = {"api_key": "AXXXXXXXXXXXXXXXXXXXXXX"} client = AlfredClient(config, auth_config) @@ -171,6 +171,89 @@ In this SDK, we implement automatic retries to enhance the reliability of networ For non-idempotent methods like POST and PATCH, the SDK does not perform retries by default because doing so could potentially result in unwanted side effects or duplicate operations. If you need to enable retries for these methods under specific circumstances, please handle them cautiously in your application logic. +## Real-time Events + +The `alfred-python` library provides a way to listen to events emitted by Alfred IPA in real-time through a websockets implementation. This feature is particularly useful when you need to monitor the progress of a Job, File, or any other event that occurs within the Alfred platform. To see more information visit our [official documentation](https://docs.tagshelf.dev). + +### Getting started + +To get started, you need to create an instance of the `AlfredRealTimeClient` class. + +```python +from alfred import AlfredRealTimeClient +from alfred.base import Configuration +from alfred import AuthConfiguration + +config = Configuration.default() + +auth_config = AuthConfiguration({ + "api_key": "AXXXXXXXXXXXXXXXXXXXXXX" +}) + +client = AlfredRealTimeClient(config, auth_config, verbose=True) +``` + +### File Events +These events are specifically designed to respond to a variety of actions or status changes related to Files. To see more details about File events, visit our [official documentation](https://docs.tagshelf.dev/event-api/fileevents). +```python +# Listen to all File events +client.on_file_event(lambda data: print(data)) +``` + +### Job Events +Alfred performs asynchronous document classification, extraction, and indexing on a variety of file types. The events detailed here offer insights into how a Job progresses, fails, retries, or completes its tasks. To see more details about Job events, visit our [official documentation](https://docs.tagshelf.dev/event-api/jobevents). + +```python +# Listen to all Job events +client.on_job_event(lambda data: print(data)) +``` + +### Specific Events + +This enables you to select a specific event you wish to monitor from the list of supported events. +It's particularly useful when you want to listen to a specific event instead of all events of a particular type. + +Here's an example of how to listen to a specific event: + +```python +from alfred.base import FileEvent, JobEvent + +# Listen to the specific File Done event +client.on(FileEvent.FILE_DONE_EVENT.value, lambda data: print(data)) + +# Listen to the specific Job Finished event +client.on(JobEvent.JOB_FINISHED_EVENT.value, lambda data: print(data)) +``` + +Here is a list of all supported events: + +| Event Type | Event Name | Description | +| --- | --- | --- | +| FileEvent | `FILE_ADD_TO_JOB_EVENT` | Triggered when a file is added to a job for processing. | +| FileEvent | `FILE_CATEGORY_CREATE_EVENT` | Occurs when a new category is created for a file. | +| FileEvent | `FILE_CATEGORY_DELETE_EVENT` | Signals the deletion of a file's category. | +| FileEvent | `FILE_CHANGE_TAG_EVENT` | Indicates a change in the tag associated with a file. | +| FileEvent | `FILE_DONE_EVENT` | Marks the completion of file processing. | +| FileEvent | `FILE_EXTRACTED_DATA_CREATE_EVENT` | Triggered when new data is extracted from a file. | +| FileEvent | `FILE_EXTRACTED_DATA_DELETE_EVENT` | Occurs when extracted data from a file is deleted. | +| FileEvent | `FILE_FAILED_EVENT` | Indicates a failure in file processing. | +| FileEvent | `FILE_MOVE_EVENT` | Signals the movement of a file within the system. | +| FileEvent | `FILE_MOVE_TO_PENDING_EVENT` | Triggered when a file is moved to a pending state. | +| FileEvent | `FILE_MOVE_TO_RECYCLE_BIN_EVENT` | Indicates movement of a file to the recycle bin. | +| FileEvent | `FILE_PROPERTY_CREATE_EVENT` | Reflects the creation of a file property. | +| FileEvent | `FILE_PROPERTY_DELETE_EVENT` | Signals the deletion of a file property. | +| FileEvent | `FILE_REMOVE_TAG_EVENT` | Signals the removal of a tag from a file. | +| FileEvent | `FILE_STATUS_UPDATE_EVENT` | Indicates an update in the file's status. | +| FileEvent | `FILE_UPDATE_EVENT` | Triggered when a file is updated in any manner. | +| JobEvent | `JOB_CREATE_EVENT` | Triggered when a new job is instantiated for file operations. | +| JobEvent | `JOB_EXCEEDED_RETRIES_EVENT` | Fires when job exceeds maximum retry attempts for a stage. | +| JobEvent | `JOB_FAILED_EVENT` | Occurs when a job halts due to an unrecoverable error. | +| JobEvent | `JOB_FINISHED_EVENT` | Triggered when job successfully completes all workflow stages. | +| JobEvent | `JOB_INVALID_EVENT` | Fires when job fails initial validation of input files or parameters. | +| JobEvent | `JOB_RETRY_EVENT` | Triggered when job retries a stage after a recoverable failure. | +| JobEvent | `JOB_STAGE_UPDATE_EVENT` | Occurs when job transitions from one workflow stage to another. | +| JobEvent | `JOB_START_EVENT` | Triggered when job begins its workflow and state machine. | + ## Development Setup ### Setting up the development environment diff --git a/alfred/__init__.py b/alfred/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/alfred/__init__.py @@ -0,0 +1 @@ + diff --git a/src/alfred/base/__init__.py b/alfred/base/__init__.py similarity index 100% rename from src/alfred/base/__init__.py rename to alfred/base/__init__.py diff --git a/src/alfred/base/config.py b/alfred/base/config.py similarity index 78% rename from src/alfred/base/config.py rename to alfred/base/config.py index d834023..5bafaa0 100644 --- a/src/alfred/base/config.py +++ b/alfred/base/config.py @@ -4,6 +4,7 @@ class ConfigurationDict(TypedDict): base_url: Text + realtime_url: Text version: int @@ -15,7 +16,7 @@ class Configuration: @staticmethod def default() -> ConfigurationDict: """ - Returns default client configuration. Currently targets Alfred V1. + Returns default client configuration. Currently, targets Alfred V1. """ return Configuration.v1() @@ -28,4 +29,5 @@ def v1(overrides: Optional[OverridesDict] = None) -> ConfigurationDict: return { "version": 1, "base_url": overrides.get("base_url", "https://app.tagshelf.com"), + "realtime_url": overrides.get("realtime_url", "https://sockets.tagshelf.io"), } diff --git a/alfred/base/constants.py b/alfred/base/constants.py new file mode 100644 index 0000000..2c8324f --- /dev/null +++ b/alfred/base/constants.py @@ -0,0 +1,94 @@ +from enum import Enum + +from alfred.http.typed import ResponseType + +# Response type/header mapping +RESPONSE_TYPE_HEADER_MAPPING = { + ResponseType.JSON: "application/json", + ResponseType.TEXT: "text/plain", + ResponseType.XML: "application/xml", +} + + +class EventType(Enum): + """ + Enumeration of event types. + """ + JOB_EVENT = "job_event" + FILE_EVENT = "file_event" + + +class FileEvent(Enum): + # Triggered when a file is added to a job for processing. + FILE_ADD_TO_JOB_EVENT = "file_add_to_job_event" + + # Occurs when a new category is created for a file. + FILE_CATEGORY_CREATE_EVENT = "file_category_create_event" + + # Signals the deletion of a file's category. + FILE_CATEGORY_DELETE_EVENT = "file_category_delete_event" + + # Indicates a change in the tag associated with a file. + FILE_CHANGE_TAG_EVENT = "file_change_tag_event" + + # Marks the completion of file processing. + FILE_DONE_EVENT = "file_done_event" + + # Triggered when new data is extracted from a file. + FILE_EXTRACTED_DATA_CREATE_EVENT = "file_extracted_data_create_event" + + # Occurs when extracted data from a file is deleted. + FILE_EXTRACTED_DATA_DELETE_EVENT = "file_extracted_data_delete_event" + + # Indicates a failure in file processing. + FILE_FAILED_EVENT = "file_failed_event" + + # Signals the movement of a file within the system. + FILE_MOVE_EVENT = "file_move_event" + + # Triggered when a file is moved to a pending state. + FILE_MOVE_TO_PENDING_EVENT = "file_move_to_pending_event" + + # Indicates movement of a file to the recycle bin. + FILE_MOVE_TO_RECYCLE_BIN_EVENT = "file_move_to_recycle_bin_event" + + # Reflects the creation of a file property. + FILE_PROPERTY_CREATE_EVENT = "file_property_create_event" + + # Signals the deletion of a file property. + FILE_PROPERTY_DELETE_EVENT = "file_property_delete_event" + + # Signals the removal of a tag from a file. + FILE_REMOVE_TAG_EVENT = "file_remove_tag_event" + + # Indicates an update in the file's status. + FILE_STATUS_UPDATE_EVENT = "file_status_update_event" + + # Triggered when a file is updated in any manner. + FILE_UPDATE_EVENT = "file_update_event" + + +class JobEvent(Enum): + # Triggered when a new job is instantiated for file operations. + JOB_CREATE_EVENT = "job_create_event" + + # Fires when job exceeds maximum retry attempts for a stage. + JOB_EXCEEDED_RETRIES_EVENT = "job_exceeded_retries_event" + + # Occurs when a job halts due to an unrecoverable error. + JOB_FAILED_EVENT = "job_failed_event" + + # Triggered when job successfully completes all workflow stages. + JOB_FINISHED_EVENT = "job_finished_event" + + # Fires when job fails initial validation of input files or parameters. + JOB_INVALID_EVENT = "job_invalid_event" + + # Triggered when job retries a stage after a recoverable failure. + JOB_RETRY_EVENT = "job_retry_event" + + # Occurs when job transitions from one workflow stage to another. + JOB_STAGE_UPDATE_EVENT = "job_stage_update_event" + + # Triggered when job begins its workflow and state machine. + JOB_START_EVENT = "job_start_event" diff --git a/src/alfred/base/exceptions.py b/alfred/base/exceptions.py similarity index 100% rename from src/alfred/base/exceptions.py rename to alfred/base/exceptions.py diff --git a/alfred/exceptions/__init__.py b/alfred/exceptions/__init__.py new file mode 100644 index 0000000..446f135 --- /dev/null +++ b/alfred/exceptions/__init__.py @@ -0,0 +1,7 @@ +class ConnectionError(Exception): + """ + Raised when a connection error occurs. + """ + def __init__(self, message="A connection error occurred"): + self.message = message + super().__init__(self.message) diff --git a/src/alfred/http/__init__.py b/alfred/http/__init__.py similarity index 100% rename from src/alfred/http/__init__.py rename to alfred/http/__init__.py diff --git a/src/alfred/http/http_client.py b/alfred/http/http_client.py similarity index 100% rename from src/alfred/http/http_client.py rename to alfred/http/http_client.py diff --git a/src/alfred/http/typed.py b/alfred/http/typed.py similarity index 100% rename from src/alfred/http/typed.py rename to alfred/http/typed.py diff --git a/alfred/realtime/__init__.py b/alfred/realtime/__init__.py new file mode 100644 index 0000000..4075ffd --- /dev/null +++ b/alfred/realtime/__init__.py @@ -0,0 +1,124 @@ +# Native Imports +from typing import Union + +# 3rd Party Imports +import socketio + +# Project Imports +import alfred.exceptions +from alfred.base.config import ConfigurationDict +from alfred.base.constants import EventType, FileEvent, JobEvent +from alfred.http.typed import AuthConfiguration +from alfred.utils import logging, setup_logger + + +class AlfredRealTimeClient: + def __init__(self, config: ConfigurationDict, auth_config: AuthConfiguration, verbose=False): + """ + Initializes the AlfredRealTimeClient class. + + Args: + config (ConfigurationDict): The configuration dictionary. + auth_config (AuthConfiguration): The authentication configuration. + verbose (bool, optional): Whether to print verbose output. Defaults to False. + """ + self.socket = socketio.Client() + self.verbose = verbose + self.config = config + self.auth_config = auth_config + self.base_url = config.get("realtime_url") + + # Initialize logger + self.logger = logging.getLogger("alfred-python") + if self.logger.level == logging.NOTSET: + setup_logger({ + "level": "DEBUG" if verbose else "INFO", + "name": "alfred-python" + }) + print(self.logger.level) + + # Subscribe to connection life-cycle events. + self.socket.on('connect', self.__on_connect) + self.socket.on('disconnect', self.__on_disconnect) + self.socket.on('connect_error', self.__on_connect_error) + + # Establish connection with verbose output if enabled + if self.verbose: + self.logger.debug("Attempting to establish a connection...") + try: + self.socket.connect(f"{self.base_url}?apiKey={auth_config.get('api_key')}") + except Exception as err: + raise alfred.exceptions.ConnectionError(f"Could not establish connection with server: {err}") + + def __on_connect(self): + """ + Handles the 'connect' event. + """ + self.logger.info(f"Successfully connected to: {self.base_url}") + + def __on_disconnect(self): + """ + Handles the 'disconnect' event. + """ + self.logger.info("Disconnected from the server.") + + def __on_connect_error(self, err): + """ + Handles the 'connect_error' event. + + Args: + err (str): The error message. + """ + self.logger.info("Connection error: %s", err) + self.disconnect() + raise Exception(f"Failed to connect to {self.base_url}: {err}") + + def __callback(self, event: Union[FileEvent, JobEvent, EventType], callback): + """ + Wrapper function to subscribe a specific event. + + Args: + event (str): The event name. + callback (function): The callback function to handle the event. + """ + def handle_event(data): + if self.verbose: + self.logger.debug(f"Event {event} received: %s", data) + callback(data) + + self.socket.on(event, handle_event) + + def on_file_event(self, callback): + """ + Listens to all file-related events. + + Args: + callback (function): The callback function to handle the event. + """ + self.__callback(EventType.FILE_EVENT.value, callback) + + def on_job_event(self, callback): + """ + Listens to all job-related events. + + Args: + callback (function): The callback function to handle the event. + """ + self.__callback(EventType.JOB_EVENT.value, callback) + + def on(self, event: Union[FileEvent, JobEvent], callback): + """ + Listens to a specific event. + + Args: + event (str): The event name. + callback (function): The callback function to handle the event. + """ + self.__callback(event, callback) + + def disconnect(self): + """ + Disconnects client from the server. + """ + self.logger.info("Closing connection...") + self.socket.disconnect() diff --git a/src/alfred/rest/__init__.py b/alfred/rest/__init__.py similarity index 83% rename from src/alfred/rest/__init__.py rename to alfred/rest/__init__.py index da03f12..aeefa1e 100644 --- a/src/alfred/rest/__init__.py +++ b/alfred/rest/__init__.py @@ -2,13 +2,13 @@ from typing import Optional # Project imports -from src.alfred.base.config import ConfigurationDict -from src.alfred.http.http_client import HttpClient -from src.alfred.http.typed import AuthConfiguration, HttpConfiguration -from src.alfred.rest.data_points import DataPointsBase, DataPointsFactory -from src.alfred.rest.sessions import SessionsBase, SessionsFactory -from src.alfred.rest.jobs import JobsBase, JobsFactory -from src.alfred.rest.files import FilesBase, FilesFactory +from alfred.base.config import ConfigurationDict +from alfred.http.http_client import HttpClient +from alfred.http.typed import AuthConfiguration, HttpConfiguration +from alfred.rest.data_points import DataPointsBase, DataPointsFactory +from alfred.rest.sessions import SessionsBase, SessionsFactory +from alfred.rest.jobs import JobsBase, JobsFactory +from alfred.rest.files import FilesBase, FilesFactory class AlfredClient: diff --git a/src/alfred/rest/data_points/__init__.py b/alfred/rest/data_points/__init__.py similarity index 100% rename from src/alfred/rest/data_points/__init__.py rename to alfred/rest/data_points/__init__.py diff --git a/src/alfred/rest/data_points/base.py b/alfred/rest/data_points/base.py similarity index 100% rename from src/alfred/rest/data_points/base.py rename to alfred/rest/data_points/base.py diff --git a/src/alfred/rest/data_points/v1.py b/alfred/rest/data_points/v1.py similarity index 81% rename from src/alfred/rest/data_points/v1.py rename to alfred/rest/data_points/v1.py index 90f0037..94b0cb0 100644 --- a/src/alfred/rest/data_points/v1.py +++ b/alfred/rest/data_points/v1.py @@ -2,8 +2,8 @@ from typing import Text # Project imports -from src.alfred.http.http_client import HttpClient -from src.alfred.rest.data_points.base import DataPointsBase +from alfred.http.http_client import HttpClient +from alfred.rest.data_points.base import DataPointsBase class DataPoints(DataPointsBase): diff --git a/src/alfred/rest/files/__init__.py b/alfred/rest/files/__init__.py similarity index 100% rename from src/alfred/rest/files/__init__.py rename to alfred/rest/files/__init__.py diff --git a/src/alfred/rest/files/base.py b/alfred/rest/files/base.py similarity index 100% rename from src/alfred/rest/files/base.py rename to alfred/rest/files/base.py diff --git a/src/alfred/rest/files/typed.py b/alfred/rest/files/typed.py similarity index 100% rename from src/alfred/rest/files/typed.py rename to alfred/rest/files/typed.py diff --git a/src/alfred/rest/files/v1.py b/alfred/rest/files/v1.py similarity index 92% rename from src/alfred/rest/files/v1.py rename to alfred/rest/files/v1.py index ae14097..7a085dd 100644 --- a/src/alfred/rest/files/v1.py +++ b/alfred/rest/files/v1.py @@ -2,13 +2,12 @@ import os import json from typing import Text -from io import BufferedReader from urllib.parse import unquote # Project imports -from src.alfred.rest.files.typed import * # pylint: disable=W0401, W0614 -from src.alfred.http.http_client import HttpClient -from src.alfred.base.exceptions import AlfredMissingArgument +from alfred.rest.files.typed import * # pylint: disable=W0401, W0614 +from alfred.http.http_client import HttpClient +from alfred.base.exceptions import AlfredMissingArgument from .base import FilesBase from .typed import FileDetailsResponse diff --git a/src/alfred/rest/jobs/__init__.py b/alfred/rest/jobs/__init__.py similarity index 100% rename from src/alfred/rest/jobs/__init__.py rename to alfred/rest/jobs/__init__.py diff --git a/src/alfred/rest/jobs/base.py b/alfred/rest/jobs/base.py similarity index 89% rename from src/alfred/rest/jobs/base.py rename to alfred/rest/jobs/base.py index f889701..95f6b3e 100644 --- a/src/alfred/rest/jobs/base.py +++ b/alfred/rest/jobs/base.py @@ -1,6 +1,6 @@ # Native imports from typing import Text, Any -from src.alfred.rest.jobs.typed import CreateJobDict +from alfred.rest.jobs.typed import CreateJobDict from abc import ABC, abstractmethod diff --git a/src/alfred/rest/jobs/typed.py b/alfred/rest/jobs/typed.py similarity index 100% rename from src/alfred/rest/jobs/typed.py rename to alfred/rest/jobs/typed.py diff --git a/src/alfred/rest/jobs/v1.py b/alfred/rest/jobs/v1.py similarity index 81% rename from src/alfred/rest/jobs/v1.py rename to alfred/rest/jobs/v1.py index 8963b46..bd3f627 100644 --- a/src/alfred/rest/jobs/v1.py +++ b/alfred/rest/jobs/v1.py @@ -2,9 +2,9 @@ from typing import Text # Project imports -from src.alfred.rest.jobs.typed import CreateJobDict -from src.alfred.http.http_client import HttpClient -from src.alfred.rest.jobs.base import JobsBase +from alfred.rest.jobs.typed import CreateJobDict +from alfred.http.http_client import HttpClient +from alfred.rest.jobs.base import JobsBase class Jobs(JobsBase): diff --git a/src/alfred/rest/sessions/__init__.py b/alfred/rest/sessions/__init__.py similarity index 100% rename from src/alfred/rest/sessions/__init__.py rename to alfred/rest/sessions/__init__.py diff --git a/src/alfred/rest/sessions/base.py b/alfred/rest/sessions/base.py similarity index 100% rename from src/alfred/rest/sessions/base.py rename to alfred/rest/sessions/base.py diff --git a/src/alfred/rest/sessions/v1.py b/alfred/rest/sessions/v1.py similarity index 85% rename from src/alfred/rest/sessions/v1.py rename to alfred/rest/sessions/v1.py index 916929a..c8f1dfd 100644 --- a/src/alfred/rest/sessions/v1.py +++ b/alfred/rest/sessions/v1.py @@ -2,8 +2,8 @@ from typing import Text # Project imports -from src.alfred.http.http_client import HttpClient -from src.alfred.rest.sessions.base import SessionsBase +from alfred.http.http_client import HttpClient +from alfred.rest.sessions.base import SessionsBase class Sessions(SessionsBase): diff --git a/alfred/typings/__init__.py b/alfred/typings/__init__.py new file mode 100644 index 0000000..0b79e7f --- /dev/null +++ b/alfred/typings/__init__.py @@ -0,0 +1 @@ +from alfred.typings.misc import * diff --git a/src/alfred/typings/misc.py b/alfred/typings/misc.py similarity index 70% rename from src/alfred/typings/misc.py rename to alfred/typings/misc.py index 90a4920..b855fab 100644 --- a/src/alfred/typings/misc.py +++ b/alfred/typings/misc.py @@ -1,10 +1,10 @@ # Native imports -from typing import TypedDict +from typing import TypedDict, Union # Typed dictionaries class LoggingOptions(TypedDict): - level: int + level: Union[str, int] name: str format: str papertrail_host: str diff --git a/alfred/utils/__init__.py b/alfred/utils/__init__.py new file mode 100644 index 0000000..fe8c6a6 --- /dev/null +++ b/alfred/utils/__init__.py @@ -0,0 +1 @@ +from alfred.utils.logging import * diff --git a/src/alfred/utils/logging.py b/alfred/utils/logging.py similarity index 98% rename from src/alfred/utils/logging.py rename to alfred/utils/logging.py index 0518a5f..d91de9d 100644 --- a/src/alfred/utils/logging.py +++ b/alfred/utils/logging.py @@ -6,7 +6,7 @@ from typing import Union # Project imports -from src.alfred.typings import LoggingOptions +from alfred.typings import LoggingOptions def setup_logger(options: LoggingOptions): diff --git a/pyproject.toml b/pyproject.toml index fe626b0..8c139e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "alfred-python" -version = "0.0.1" +version = "0.5.0" authors = [{ name = "Tagshelf LLC", email = "support@tagshelf.com" }] description = "Python library designed to simplify and streamline interactions with the Alfred API" readme = "README.md" @@ -19,11 +19,12 @@ dependencies = [ "pydantic >= 2.0", "pydantic-settings >= 2.0", "requests >= 2.30", + "python-socketio >= 5.11", + "websocket-client >= 1.8" ] [project.urls] homepage = "https://github.com/tagshelfsrl/alfred-python" [tool.setuptools.packages.find] -where = ["src"] include = ["alfred", "alfred.*"] diff --git a/requirements.txt b/requirements.txt index c0657b0..4c78517 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ annotated-types==0.6.0 +bidict==0.23.1 build==1.2.1 certifi==2024.2.2 charset-normalizer==3.3.2 +h11==0.14.0 idna==3.7 importlib_metadata==7.1.0 packaging==24.0 @@ -10,8 +12,13 @@ pydantic-settings==2.2.1 pydantic_core==2.18.2 pyproject_hooks==1.1.0 python-dotenv==1.0.1 +python-engineio==4.9.0 +python-socketio==5.11.2 requests==2.31.0 +simple-websocket==1.0.0 tomli==2.0.1 typing_extensions==4.11.0 urllib3==2.2.1 +websocket-client==1.8.0 +wsproto==1.2.0 zipp==3.18.1 diff --git a/src/alfred/__init__.py b/src/alfred/__init__.py deleted file mode 100644 index f102a9c..0000000 --- a/src/alfred/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.0.1" diff --git a/src/alfred/base/constants.py b/src/alfred/base/constants.py deleted file mode 100644 index 41b6108..0000000 --- a/src/alfred/base/constants.py +++ /dev/null @@ -1,8 +0,0 @@ -from src.alfred.http.typed import ResponseType - -# Response type/header mapping -RESPONSE_TYPE_HEADER_MAPPING = { - ResponseType.JSON: "application/json", - ResponseType.TEXT: "text/plain", - ResponseType.XML: "application/xml", -} diff --git a/src/alfred/typings/__init__.py b/src/alfred/typings/__init__.py deleted file mode 100644 index 50a738d..0000000 --- a/src/alfred/typings/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from src.alfred.typings.misc import * diff --git a/src/alfred/utils/__init__.py b/src/alfred/utils/__init__.py deleted file mode 100644 index 11f6ea8..0000000 --- a/src/alfred/utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from src.alfred.utils.logging import *