ergon-framework-python 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ergon_framework_python-0.2.2/src/ergon_framework_python.egg-info → ergon_framework_python-0.2.4}/PKG-INFO +47 -3
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/README.md +42 -2
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/pyproject.toml +6 -1
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/__init__.py +1 -1
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/__init__.py +54 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/__init__.py +67 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/async_connector.py +198 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/async_service.py +168 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/connector.py +198 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/models.py +315 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/service.py +410 -0
- ergon_framework_python-0.2.4/src/ergon/connector/outlook/utils.py +118 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/async_service.py +61 -18
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/mixins/consumer.py +34 -22
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4/src/ergon_framework_python.egg-info}/PKG-INFO +47 -3
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon_framework_python.egg-info/SOURCES.txt +7 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon_framework_python.egg-info/requires.txt +5 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/tests/test_optional_connectors.py +40 -1
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/tests/test_smoke.py +1 -1
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/LICENSE +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/setup.cfg +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/docker-compose.telemetry.yml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/grafana.yaml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/loki.yaml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/otel-collector-config.yaml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/prometheus.yaml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/_observability/tempo.yaml +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/connectors/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/main.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/constants.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/config.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/exceptions.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/helpers.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/schemas.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/example_task/task.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/exceptions.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/helpers.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/schemas.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/bootstrap/src/__project__/tasks/settings.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/cli.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/_lazy.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/_client.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/adapters.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/activity.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/addresses.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/attachments.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/messages.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/records.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/channels/services/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/utils.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/_operations.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/ergon_platform/workflows/utils.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/excel/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/excel/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/excel/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/excel/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/async_auth_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/async_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/auth_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/nylas/utils.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/async_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/pipefy/version.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/postgres/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/postgres/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/postgres/async_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/postgres/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/helper.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/rabbitmq/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/async_connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/async_service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/connector.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/models.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/sqs/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/transaction.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/py.typed +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/service/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/service/service.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/base.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/exceptions.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/helpers.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/liveness.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/manager.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/mixins/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/mixins/metrics.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/mixins/producer.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/policies.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/runner.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/task/utils.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/telemetry/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/telemetry/_resource.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/telemetry/logging.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/telemetry/metrics.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/telemetry/tracing.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/utils/__init__.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/utils/env.py +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon_framework_python.egg-info/dependency_links.txt +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon_framework_python.egg-info/entry_points.txt +0 -0
- {ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon_framework_python.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ergon-framework-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Ergon internal task-oriented project bootstrapper
|
|
5
5
|
Author-email: Ergondata Technologies <anza.vossos@protonmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -31,6 +31,7 @@ Requires-Dist: asyncpg>=0.29.0; extra == "all"
|
|
|
31
31
|
Requires-Dist: boto3>=1.35.0; extra == "all"
|
|
32
32
|
Requires-Dist: ergon-platform-sdk>=0.2.0; extra == "all"
|
|
33
33
|
Requires-Dist: httpx>=0.28.0; extra == "all"
|
|
34
|
+
Requires-Dist: msal>=1.37.0; extra == "all"
|
|
34
35
|
Requires-Dist: nylas>=6.0.0; extra == "all"
|
|
35
36
|
Requires-Dist: openpyxl>=3.1.5; extra == "all"
|
|
36
37
|
Requires-Dist: pika>=1.3.0; extra == "all"
|
|
@@ -42,6 +43,9 @@ Provides-Extra: excel
|
|
|
42
43
|
Requires-Dist: openpyxl>=3.1.5; extra == "excel"
|
|
43
44
|
Provides-Extra: nylas
|
|
44
45
|
Requires-Dist: nylas>=6.0.0; extra == "nylas"
|
|
46
|
+
Provides-Extra: outlook
|
|
47
|
+
Requires-Dist: msal>=1.37.0; extra == "outlook"
|
|
48
|
+
Requires-Dist: requests>=2.32.5; extra == "outlook"
|
|
45
49
|
Provides-Extra: pipefy
|
|
46
50
|
Requires-Dist: httpx>=0.28.0; extra == "pipefy"
|
|
47
51
|
Requires-Dist: requests>=2.32.5; extra == "pipefy"
|
|
@@ -77,13 +81,53 @@ pull in client libraries that your application does not use:
|
|
|
77
81
|
```bash
|
|
78
82
|
pip install 'ergon-framework-python[rabbitmq]'
|
|
79
83
|
pip install 'ergon-framework-python[sqs,postgres]'
|
|
84
|
+
pip install 'ergon-framework-python[outlook]'
|
|
80
85
|
pip install 'ergon-framework-python[all]'
|
|
81
86
|
```
|
|
82
87
|
|
|
83
88
|
Available extras are `rabbitmq`, `sqs`, `postgres`, `pipefy`, `excel`,
|
|
84
|
-
`ergon-platform`, and `
|
|
89
|
+
`ergon-platform`, `nylas`, and `outlook`.
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
### Outlook / Microsoft Graph
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install 'ergon-framework-python[outlook]'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The Outlook connector talks to Microsoft Graph with Entra **application**
|
|
98
|
+
credentials. Consuming messages and attachments needs `Mail.Read`; marking,
|
|
99
|
+
categorizing, moving, and deleting need `Mail.ReadWrite`; send, reply, and
|
|
100
|
+
forward need `Mail.Send`. Graph `$filter` and `$search` cannot be combined.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from ergon.connector.outlook import (
|
|
104
|
+
AsyncOutlookGraphConnector,
|
|
105
|
+
OutlookAckActionConfig,
|
|
106
|
+
OutlookConsumerConfig,
|
|
107
|
+
OutlookGraphClient,
|
|
108
|
+
OutlookMessageFilter,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
client = OutlookGraphClient(
|
|
112
|
+
tenant_id="...",
|
|
113
|
+
client_id="...",
|
|
114
|
+
client_secret="...",
|
|
115
|
+
user_email="mailbox@example.com",
|
|
116
|
+
)
|
|
117
|
+
consumer = OutlookConsumerConfig(
|
|
118
|
+
folder_id="Inbox",
|
|
119
|
+
filter=OutlookMessageFilter(unread_only=True, has_attachments=True),
|
|
120
|
+
download_attachments=True,
|
|
121
|
+
ack_config=OutlookAckActionConfig(mark_as_read=True),
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Runnable example: [examples/outlook](https://github.com/danzanellovossos/ergon-framework/tree/main/sdks/python/examples/outlook).
|
|
126
|
+
Module reference: [Outlook connector README](https://github.com/danzanellovossos/ergon-framework/blob/main/sdks/python/src/ergon/connector/outlook/README.md).
|
|
127
|
+
|
|
128
|
+
### Local development
|
|
129
|
+
|
|
130
|
+
Install in editable mode:
|
|
87
131
|
|
|
88
132
|
```bash
|
|
89
133
|
pip install -e /path/to/ergon-framework/sdks/python
|
|
@@ -20,13 +20,53 @@ pull in client libraries that your application does not use:
|
|
|
20
20
|
```bash
|
|
21
21
|
pip install 'ergon-framework-python[rabbitmq]'
|
|
22
22
|
pip install 'ergon-framework-python[sqs,postgres]'
|
|
23
|
+
pip install 'ergon-framework-python[outlook]'
|
|
23
24
|
pip install 'ergon-framework-python[all]'
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
Available extras are `rabbitmq`, `sqs`, `postgres`, `pipefy`, `excel`,
|
|
27
|
-
`ergon-platform`, and `
|
|
28
|
+
`ergon-platform`, `nylas`, and `outlook`.
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
### Outlook / Microsoft Graph
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install 'ergon-framework-python[outlook]'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The Outlook connector talks to Microsoft Graph with Entra **application**
|
|
37
|
+
credentials. Consuming messages and attachments needs `Mail.Read`; marking,
|
|
38
|
+
categorizing, moving, and deleting need `Mail.ReadWrite`; send, reply, and
|
|
39
|
+
forward need `Mail.Send`. Graph `$filter` and `$search` cannot be combined.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ergon.connector.outlook import (
|
|
43
|
+
AsyncOutlookGraphConnector,
|
|
44
|
+
OutlookAckActionConfig,
|
|
45
|
+
OutlookConsumerConfig,
|
|
46
|
+
OutlookGraphClient,
|
|
47
|
+
OutlookMessageFilter,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
client = OutlookGraphClient(
|
|
51
|
+
tenant_id="...",
|
|
52
|
+
client_id="...",
|
|
53
|
+
client_secret="...",
|
|
54
|
+
user_email="mailbox@example.com",
|
|
55
|
+
)
|
|
56
|
+
consumer = OutlookConsumerConfig(
|
|
57
|
+
folder_id="Inbox",
|
|
58
|
+
filter=OutlookMessageFilter(unread_only=True, has_attachments=True),
|
|
59
|
+
download_attachments=True,
|
|
60
|
+
ack_config=OutlookAckActionConfig(mark_as_read=True),
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Runnable example: [examples/outlook](https://github.com/danzanellovossos/ergon-framework/tree/main/sdks/python/examples/outlook).
|
|
65
|
+
Module reference: [Outlook connector README](https://github.com/danzanellovossos/ergon-framework/blob/main/sdks/python/src/ergon/connector/outlook/README.md).
|
|
66
|
+
|
|
67
|
+
### Local development
|
|
68
|
+
|
|
69
|
+
Install in editable mode:
|
|
30
70
|
|
|
31
71
|
```bash
|
|
32
72
|
pip install -e /path/to/ergon-framework/sdks/python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ergon-framework-python"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "Ergon internal task-oriented project bootstrapper"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -36,6 +36,7 @@ all = [
|
|
|
36
36
|
"boto3>=1.35.0",
|
|
37
37
|
"ergon-platform-sdk>=0.2.0",
|
|
38
38
|
"httpx>=0.28.0",
|
|
39
|
+
"msal>=1.37.0",
|
|
39
40
|
"nylas>=6.0.0",
|
|
40
41
|
"openpyxl>=3.1.5",
|
|
41
42
|
"pika>=1.3.0",
|
|
@@ -47,6 +48,10 @@ all = [
|
|
|
47
48
|
]
|
|
48
49
|
excel = ["openpyxl>=3.1.5"]
|
|
49
50
|
nylas = ["nylas>=6.0.0"]
|
|
51
|
+
outlook = [
|
|
52
|
+
"msal>=1.37.0",
|
|
53
|
+
"requests>=2.32.5",
|
|
54
|
+
]
|
|
50
55
|
pipefy = [
|
|
51
56
|
"httpx>=0.28.0",
|
|
52
57
|
"requests>=2.32.5",
|
{ergon_framework_python-0.2.2 → ergon_framework_python-0.2.4}/src/ergon/connector/__init__.py
RENAMED
|
@@ -43,6 +43,25 @@ if TYPE_CHECKING:
|
|
|
43
43
|
NylasService,
|
|
44
44
|
SendMessageInput,
|
|
45
45
|
)
|
|
46
|
+
from .outlook import (
|
|
47
|
+
AsyncOutlookGraphConnector,
|
|
48
|
+
AsyncOutlookGraphService,
|
|
49
|
+
OutlookAckActionConfig,
|
|
50
|
+
OutlookAttachmentInput,
|
|
51
|
+
OutlookConsumerConfig,
|
|
52
|
+
OutlookEmailAddress,
|
|
53
|
+
OutlookFlagStatus,
|
|
54
|
+
OutlookGraphClient,
|
|
55
|
+
OutlookGraphConnector,
|
|
56
|
+
OutlookGraphService,
|
|
57
|
+
OutlookMessageFilter,
|
|
58
|
+
OutlookMessageQuery,
|
|
59
|
+
OutlookMessageSearch,
|
|
60
|
+
OutlookNackActionConfig,
|
|
61
|
+
OutlookProducerConfig,
|
|
62
|
+
OutlookSendMessageInput,
|
|
63
|
+
OutlookWellKnownFolder,
|
|
64
|
+
)
|
|
46
65
|
from .postgres import (
|
|
47
66
|
AsyncPostgresConnector,
|
|
48
67
|
AsyncPostgresService,
|
|
@@ -78,6 +97,7 @@ _CONNECTOR_SUBPACKAGES = (
|
|
|
78
97
|
"ergon_platform",
|
|
79
98
|
"excel",
|
|
80
99
|
"nylas",
|
|
100
|
+
"outlook",
|
|
81
101
|
"pipefy",
|
|
82
102
|
"postgres",
|
|
83
103
|
"rabbitmq",
|
|
@@ -91,6 +111,8 @@ _LAZY_EXPORTS = {
|
|
|
91
111
|
"AsyncNylasAuthService": "nylas",
|
|
92
112
|
"AsyncNylasConnector": "nylas",
|
|
93
113
|
"AsyncNylasService": "nylas",
|
|
114
|
+
"AsyncOutlookGraphConnector": "outlook",
|
|
115
|
+
"AsyncOutlookGraphService": "outlook",
|
|
94
116
|
"AsyncPostgresConnector": "postgres",
|
|
95
117
|
"AsyncPostgresService": "postgres",
|
|
96
118
|
"AsyncRabbitMQConnector": "rabbitmq",
|
|
@@ -126,6 +148,21 @@ _LAZY_EXPORTS = {
|
|
|
126
148
|
"NylasConsumerConfig": "nylas",
|
|
127
149
|
"NylasProducerConfig": "nylas",
|
|
128
150
|
"NylasService": "nylas",
|
|
151
|
+
"OutlookAckActionConfig": "outlook",
|
|
152
|
+
"OutlookAttachmentInput": "outlook",
|
|
153
|
+
"OutlookConsumerConfig": "outlook",
|
|
154
|
+
"OutlookEmailAddress": "outlook",
|
|
155
|
+
"OutlookFlagStatus": "outlook",
|
|
156
|
+
"OutlookGraphClient": "outlook",
|
|
157
|
+
"OutlookGraphConnector": "outlook",
|
|
158
|
+
"OutlookGraphService": "outlook",
|
|
159
|
+
"OutlookMessageFilter": "outlook",
|
|
160
|
+
"OutlookMessageQuery": "outlook",
|
|
161
|
+
"OutlookMessageSearch": "outlook",
|
|
162
|
+
"OutlookNackActionConfig": "outlook",
|
|
163
|
+
"OutlookProducerConfig": "outlook",
|
|
164
|
+
"OutlookSendMessageInput": "outlook",
|
|
165
|
+
"OutlookWellKnownFolder": "outlook",
|
|
129
166
|
"PostgresClient": "postgres",
|
|
130
167
|
"PostgresConsumerConfig": "postgres",
|
|
131
168
|
"PostgresProducerConfig": "postgres",
|
|
@@ -168,6 +205,8 @@ __all__ = [
|
|
|
168
205
|
"AsyncNylasAuthService",
|
|
169
206
|
"AsyncNylasConnector",
|
|
170
207
|
"AsyncNylasService",
|
|
208
|
+
"AsyncOutlookGraphConnector",
|
|
209
|
+
"AsyncOutlookGraphService",
|
|
171
210
|
"AuthUrlConfig",
|
|
172
211
|
"CodeExchangeInput",
|
|
173
212
|
"GrantAuthResult",
|
|
@@ -205,6 +244,21 @@ __all__ = [
|
|
|
205
244
|
"NylasConsumerConfig",
|
|
206
245
|
"NylasProducerConfig",
|
|
207
246
|
"NylasService",
|
|
247
|
+
"OutlookAckActionConfig",
|
|
248
|
+
"OutlookAttachmentInput",
|
|
249
|
+
"OutlookConsumerConfig",
|
|
250
|
+
"OutlookEmailAddress",
|
|
251
|
+
"OutlookFlagStatus",
|
|
252
|
+
"OutlookGraphClient",
|
|
253
|
+
"OutlookGraphConnector",
|
|
254
|
+
"OutlookGraphService",
|
|
255
|
+
"OutlookMessageFilter",
|
|
256
|
+
"OutlookMessageQuery",
|
|
257
|
+
"OutlookMessageSearch",
|
|
258
|
+
"OutlookNackActionConfig",
|
|
259
|
+
"OutlookProducerConfig",
|
|
260
|
+
"OutlookSendMessageInput",
|
|
261
|
+
"OutlookWellKnownFolder",
|
|
208
262
|
"PostgresClient",
|
|
209
263
|
"SendMessageInput",
|
|
210
264
|
"PostgresConsumerConfig",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any
|
|
2
|
+
|
|
3
|
+
from .._lazy import exported_names, load_optional_export
|
|
4
|
+
from .models import (
|
|
5
|
+
OutlookAckActionConfig,
|
|
6
|
+
OutlookAttachmentInput,
|
|
7
|
+
OutlookConsumerConfig,
|
|
8
|
+
OutlookEmailAddress,
|
|
9
|
+
OutlookFlagStatus,
|
|
10
|
+
OutlookGraphClient,
|
|
11
|
+
OutlookMessageFilter,
|
|
12
|
+
OutlookMessageQuery,
|
|
13
|
+
OutlookMessageSearch,
|
|
14
|
+
OutlookNackActionConfig,
|
|
15
|
+
OutlookProducerConfig,
|
|
16
|
+
OutlookSendMessageInput,
|
|
17
|
+
OutlookWellKnownFolder,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from .async_connector import AsyncOutlookGraphConnector
|
|
22
|
+
from .async_service import AsyncOutlookGraphService
|
|
23
|
+
from .connector import OutlookGraphConnector
|
|
24
|
+
from .service import OutlookGraphService
|
|
25
|
+
|
|
26
|
+
_LAZY_EXPORTS = {
|
|
27
|
+
"AsyncOutlookGraphConnector": "async_connector",
|
|
28
|
+
"AsyncOutlookGraphService": "async_service",
|
|
29
|
+
"OutlookGraphConnector": "connector",
|
|
30
|
+
"OutlookGraphService": "service",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __getattr__(name: str) -> Any:
|
|
35
|
+
return load_optional_export(
|
|
36
|
+
name=name,
|
|
37
|
+
package=__name__,
|
|
38
|
+
exports=_LAZY_EXPORTS,
|
|
39
|
+
namespace=globals(),
|
|
40
|
+
extra="outlook",
|
|
41
|
+
dependencies=("msal", "requests"),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def __dir__() -> list[str]:
|
|
46
|
+
return exported_names(globals(), _LAZY_EXPORTS)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"AsyncOutlookGraphConnector",
|
|
51
|
+
"AsyncOutlookGraphService",
|
|
52
|
+
"OutlookAckActionConfig",
|
|
53
|
+
"OutlookAttachmentInput",
|
|
54
|
+
"OutlookConsumerConfig",
|
|
55
|
+
"OutlookEmailAddress",
|
|
56
|
+
"OutlookFlagStatus",
|
|
57
|
+
"OutlookGraphClient",
|
|
58
|
+
"OutlookGraphConnector",
|
|
59
|
+
"OutlookGraphService",
|
|
60
|
+
"OutlookMessageFilter",
|
|
61
|
+
"OutlookMessageQuery",
|
|
62
|
+
"OutlookMessageSearch",
|
|
63
|
+
"OutlookNackActionConfig",
|
|
64
|
+
"OutlookProducerConfig",
|
|
65
|
+
"OutlookSendMessageInput",
|
|
66
|
+
"OutlookWellKnownFolder",
|
|
67
|
+
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..connector import AsyncConnector
|
|
6
|
+
from ..transaction import Transaction
|
|
7
|
+
from .async_service import AsyncOutlookGraphService
|
|
8
|
+
from .models import (
|
|
9
|
+
OutlookAckActionConfig,
|
|
10
|
+
OutlookConsumerConfig,
|
|
11
|
+
OutlookEmailAddress,
|
|
12
|
+
OutlookFlagStatus,
|
|
13
|
+
OutlookGraphClient,
|
|
14
|
+
OutlookMessageQuery,
|
|
15
|
+
OutlookNackActionConfig,
|
|
16
|
+
OutlookProducerConfig,
|
|
17
|
+
OutlookSendMessagePayload,
|
|
18
|
+
OutlookWellKnownFolder,
|
|
19
|
+
)
|
|
20
|
+
from .utils import build_ack_patch, build_nack_patch, merge_query
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AsyncOutlookGraphConnector(AsyncConnector):
|
|
26
|
+
service: AsyncOutlookGraphService
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
client: OutlookGraphClient,
|
|
31
|
+
consumer_config: Optional[OutlookConsumerConfig] = None,
|
|
32
|
+
producer_config: Optional[OutlookProducerConfig] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
self.service = AsyncOutlookGraphService(client)
|
|
35
|
+
self._consumer_config = consumer_config
|
|
36
|
+
self._producer_config = producer_config or OutlookProducerConfig()
|
|
37
|
+
|
|
38
|
+
async def fetch_transactions_async(
|
|
39
|
+
self,
|
|
40
|
+
batch_size: Optional[int] = None,
|
|
41
|
+
query_overrides: Optional[OutlookMessageQuery] = None,
|
|
42
|
+
*args: Any,
|
|
43
|
+
**kwargs: Any,
|
|
44
|
+
) -> List[Transaction]:
|
|
45
|
+
if self._consumer_config is None:
|
|
46
|
+
raise ValueError("AsyncOutlookGraphConnector requires a consumer_config to fetch transactions")
|
|
47
|
+
config = self._consumer_config
|
|
48
|
+
return await self.service.fetch_items(
|
|
49
|
+
merge_query(config, query_overrides, **kwargs),
|
|
50
|
+
batch_size or config.batch_size,
|
|
51
|
+
download_attachments=config.download_attachments,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
async def dispatch_transactions_async(
|
|
55
|
+
self,
|
|
56
|
+
transactions: List[Transaction],
|
|
57
|
+
*args: Any,
|
|
58
|
+
**kwargs: Any,
|
|
59
|
+
) -> List[str]:
|
|
60
|
+
dispatched_ids: List[str] = []
|
|
61
|
+
for transaction in transactions:
|
|
62
|
+
await self.service.send_message(
|
|
63
|
+
transaction.payload,
|
|
64
|
+
producer_config=self._producer_config,
|
|
65
|
+
)
|
|
66
|
+
dispatched_ids.append(transaction.id)
|
|
67
|
+
return dispatched_ids
|
|
68
|
+
|
|
69
|
+
async def send_message(self, payload: OutlookSendMessagePayload) -> None:
|
|
70
|
+
await self.service.send_message(payload, producer_config=self._producer_config)
|
|
71
|
+
|
|
72
|
+
async def mark_as_read(self, message_id: str) -> Dict[str, Any]:
|
|
73
|
+
return await self.service.mark_as_read(message_id)
|
|
74
|
+
|
|
75
|
+
async def mark_as_unread(self, message_id: str) -> Dict[str, Any]:
|
|
76
|
+
return await self.service.mark_as_unread(message_id)
|
|
77
|
+
|
|
78
|
+
async def set_flag(
|
|
79
|
+
self,
|
|
80
|
+
message_id: str,
|
|
81
|
+
status: OutlookFlagStatus | str = OutlookFlagStatus.FLAGGED,
|
|
82
|
+
) -> Dict[str, Any]:
|
|
83
|
+
return await self.service.set_flag(message_id, status)
|
|
84
|
+
|
|
85
|
+
async def set_categories(self, message_id: str, categories: List[str]) -> Dict[str, Any]:
|
|
86
|
+
return await self.service.set_categories(message_id, categories)
|
|
87
|
+
|
|
88
|
+
async def move_message(
|
|
89
|
+
self,
|
|
90
|
+
message_id: str,
|
|
91
|
+
folder_id: OutlookWellKnownFolder | str,
|
|
92
|
+
) -> Dict[str, Any]:
|
|
93
|
+
return await self.service.move_message(message_id, folder_id)
|
|
94
|
+
|
|
95
|
+
async def delete_message(self, message_id: str) -> None:
|
|
96
|
+
await self.service.delete_message(message_id)
|
|
97
|
+
|
|
98
|
+
async def reply(self, message_id: str, *, comment: str = "") -> None:
|
|
99
|
+
await self.service.reply(message_id, comment=comment)
|
|
100
|
+
|
|
101
|
+
async def reply_all(self, message_id: str, *, comment: str = "") -> None:
|
|
102
|
+
await self.service.reply_all(message_id, comment=comment)
|
|
103
|
+
|
|
104
|
+
async def forward(
|
|
105
|
+
self,
|
|
106
|
+
message_id: str,
|
|
107
|
+
to: List[OutlookEmailAddress],
|
|
108
|
+
comment: str = "",
|
|
109
|
+
) -> None:
|
|
110
|
+
await self.service.forward(message_id, to, comment=comment)
|
|
111
|
+
|
|
112
|
+
async def list_attachments(
|
|
113
|
+
self,
|
|
114
|
+
message_id: str,
|
|
115
|
+
download_content: bool = False,
|
|
116
|
+
) -> List[Dict[str, Any]]:
|
|
117
|
+
return await self.service.list_attachments(message_id, download_content=download_content)
|
|
118
|
+
|
|
119
|
+
async def save_attachment(
|
|
120
|
+
self,
|
|
121
|
+
attachment: Dict[str, Any],
|
|
122
|
+
destination: str | Path,
|
|
123
|
+
overwrite: bool = False,
|
|
124
|
+
) -> Path:
|
|
125
|
+
return await self.service.save_attachment(attachment, destination, overwrite=overwrite)
|
|
126
|
+
|
|
127
|
+
async def list_mail_folders(self, include_hidden: bool = False) -> List[Dict[str, Any]]:
|
|
128
|
+
return await self.service.list_mail_folders(include_hidden=include_hidden)
|
|
129
|
+
|
|
130
|
+
async def get_mail_folder(self, folder_id: OutlookWellKnownFolder | str) -> Dict[str, Any]:
|
|
131
|
+
return await self.service.get_mail_folder(folder_id)
|
|
132
|
+
|
|
133
|
+
async def reset_pagination(self) -> None:
|
|
134
|
+
await self.service.reset_pagination()
|
|
135
|
+
|
|
136
|
+
async def fetch_transaction_by_id_async(
|
|
137
|
+
self,
|
|
138
|
+
transaction_id: str,
|
|
139
|
+
*args: Any,
|
|
140
|
+
**kwargs: Any,
|
|
141
|
+
) -> Transaction:
|
|
142
|
+
config = self._consumer_config
|
|
143
|
+
return await self.service.find_message_transaction(
|
|
144
|
+
transaction_id,
|
|
145
|
+
select=kwargs.get("select"),
|
|
146
|
+
download_attachments=config.download_attachments if config else False,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
async def get_transactions_count_async(self, *args: Any, **kwargs: Any) -> int:
|
|
150
|
+
if self._consumer_config is None:
|
|
151
|
+
raise ValueError("AsyncOutlookGraphConnector requires a consumer_config to count transactions")
|
|
152
|
+
query_overrides = kwargs.pop("query_overrides", None)
|
|
153
|
+
max_pages = kwargs.pop("max_pages", None)
|
|
154
|
+
query = merge_query(self._consumer_config, query_overrides, **kwargs)
|
|
155
|
+
return await self.service.get_messages_count(query, max_pages=max_pages)
|
|
156
|
+
|
|
157
|
+
async def ack_transaction(
|
|
158
|
+
self,
|
|
159
|
+
transaction: Transaction,
|
|
160
|
+
ack_config: Optional[OutlookAckActionConfig] = None,
|
|
161
|
+
) -> None:
|
|
162
|
+
config = ack_config or (self._consumer_config.ack_config if self._consumer_config else None)
|
|
163
|
+
if config is None:
|
|
164
|
+
return
|
|
165
|
+
if config.delete:
|
|
166
|
+
await self.service.delete_message(transaction.id)
|
|
167
|
+
return
|
|
168
|
+
patch = build_ack_patch(config)
|
|
169
|
+
if patch:
|
|
170
|
+
await self.service.update_message(transaction.id, patch)
|
|
171
|
+
if config.move_to_folder_id:
|
|
172
|
+
await self.service.move_message(transaction.id, config.move_to_folder_id)
|
|
173
|
+
|
|
174
|
+
async def nack_transaction(
|
|
175
|
+
self,
|
|
176
|
+
transaction: Transaction,
|
|
177
|
+
requeue: bool = True,
|
|
178
|
+
nack_config: Optional[OutlookNackActionConfig] = None,
|
|
179
|
+
) -> None:
|
|
180
|
+
config = nack_config or (self._consumer_config.nack_config if self._consumer_config else None)
|
|
181
|
+
if config is None:
|
|
182
|
+
if not requeue:
|
|
183
|
+
raise ValueError("nack_config with a failure category or folder is required when requeue=False")
|
|
184
|
+
config = OutlookNackActionConfig()
|
|
185
|
+
if not requeue and not config.move_to_folder_id and not config.categories:
|
|
186
|
+
raise ValueError("nack_config with a failure category or folder is required when requeue=False")
|
|
187
|
+
|
|
188
|
+
patch = build_nack_patch(config)
|
|
189
|
+
if patch:
|
|
190
|
+
await self.service.update_message(transaction.id, patch)
|
|
191
|
+
if config.move_to_folder_id:
|
|
192
|
+
await self.service.move_message(transaction.id, config.move_to_folder_id)
|
|
193
|
+
if requeue:
|
|
194
|
+
await self.service.reset_pagination()
|
|
195
|
+
logger.debug("Outlook message %s was made available for refetch", transaction.id)
|
|
196
|
+
|
|
197
|
+
async def close(self) -> None:
|
|
198
|
+
await self.service.close()
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..transaction import Transaction
|
|
6
|
+
from .models import (
|
|
7
|
+
OutlookEmailAddress,
|
|
8
|
+
OutlookFlagStatus,
|
|
9
|
+
OutlookGraphClient,
|
|
10
|
+
OutlookMessageQuery,
|
|
11
|
+
OutlookProducerConfig,
|
|
12
|
+
OutlookSendMessagePayload,
|
|
13
|
+
OutlookWellKnownFolder,
|
|
14
|
+
)
|
|
15
|
+
from .service import OutlookGraphService
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AsyncOutlookGraphService:
|
|
19
|
+
"""Async facade over the blocking MSAL and requests implementations."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: OutlookGraphClient) -> None:
|
|
22
|
+
self._sync = OutlookGraphService(client)
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def user_email(self) -> str:
|
|
26
|
+
return self._sync.user_email
|
|
27
|
+
|
|
28
|
+
async def close(self) -> None:
|
|
29
|
+
await asyncio.to_thread(self._sync.close)
|
|
30
|
+
|
|
31
|
+
async def reset_pagination(self) -> None:
|
|
32
|
+
self._sync.reset_pagination()
|
|
33
|
+
|
|
34
|
+
async def list_messages(
|
|
35
|
+
self,
|
|
36
|
+
query: OutlookMessageQuery,
|
|
37
|
+
limit: int,
|
|
38
|
+
next_link: Optional[str] = None,
|
|
39
|
+
use_internal_pagination: bool = True,
|
|
40
|
+
) -> Dict[str, Any]:
|
|
41
|
+
return await asyncio.to_thread(
|
|
42
|
+
self._sync.list_messages,
|
|
43
|
+
query,
|
|
44
|
+
limit,
|
|
45
|
+
next_link=next_link,
|
|
46
|
+
use_internal_pagination=use_internal_pagination,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
async def find_message(self, message_id: str, *, select: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
50
|
+
return await asyncio.to_thread(self._sync.find_message, message_id, select=select)
|
|
51
|
+
|
|
52
|
+
async def list_attachments(
|
|
53
|
+
self,
|
|
54
|
+
message_id: str,
|
|
55
|
+
download_content: bool = False,
|
|
56
|
+
) -> List[Dict[str, Any]]:
|
|
57
|
+
return await asyncio.to_thread(
|
|
58
|
+
self._sync.list_attachments,
|
|
59
|
+
message_id,
|
|
60
|
+
download_content=download_content,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
async def save_attachment(
|
|
64
|
+
self,
|
|
65
|
+
attachment: Dict[str, Any],
|
|
66
|
+
destination: str | Path,
|
|
67
|
+
overwrite: bool = False,
|
|
68
|
+
) -> Path:
|
|
69
|
+
return await asyncio.to_thread(
|
|
70
|
+
self._sync.save_attachment,
|
|
71
|
+
attachment,
|
|
72
|
+
destination,
|
|
73
|
+
overwrite=overwrite,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def fetch_items(
|
|
77
|
+
self,
|
|
78
|
+
query: OutlookMessageQuery,
|
|
79
|
+
limit: int,
|
|
80
|
+
download_attachments: bool = False,
|
|
81
|
+
) -> List[Transaction]:
|
|
82
|
+
return await asyncio.to_thread(
|
|
83
|
+
self._sync.fetch_items,
|
|
84
|
+
query,
|
|
85
|
+
limit,
|
|
86
|
+
download_attachments=download_attachments,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
async def find_message_transaction(
|
|
90
|
+
self,
|
|
91
|
+
message_id: str,
|
|
92
|
+
select: Optional[List[str]] = None,
|
|
93
|
+
download_attachments: bool = False,
|
|
94
|
+
) -> Transaction:
|
|
95
|
+
return await asyncio.to_thread(
|
|
96
|
+
self._sync.find_message_transaction,
|
|
97
|
+
message_id,
|
|
98
|
+
select=select,
|
|
99
|
+
download_attachments=download_attachments,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
async def get_messages_count(
|
|
103
|
+
self,
|
|
104
|
+
query: OutlookMessageQuery,
|
|
105
|
+
max_pages: Optional[int] = None,
|
|
106
|
+
) -> int:
|
|
107
|
+
return await asyncio.to_thread(self._sync.get_messages_count, query, max_pages=max_pages)
|
|
108
|
+
|
|
109
|
+
async def send_message(
|
|
110
|
+
self,
|
|
111
|
+
payload: OutlookSendMessagePayload,
|
|
112
|
+
producer_config: Optional[OutlookProducerConfig] = None,
|
|
113
|
+
) -> None:
|
|
114
|
+
await asyncio.to_thread(
|
|
115
|
+
self._sync.send_message,
|
|
116
|
+
payload,
|
|
117
|
+
producer_config=producer_config,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def update_message(self, message_id: str, request_body: Dict[str, Any]) -> Dict[str, Any]:
|
|
121
|
+
return await asyncio.to_thread(self._sync.update_message, message_id, request_body)
|
|
122
|
+
|
|
123
|
+
async def mark_as_read(self, message_id: str) -> Dict[str, Any]:
|
|
124
|
+
return await asyncio.to_thread(self._sync.mark_as_read, message_id)
|
|
125
|
+
|
|
126
|
+
async def mark_as_unread(self, message_id: str) -> Dict[str, Any]:
|
|
127
|
+
return await asyncio.to_thread(self._sync.mark_as_unread, message_id)
|
|
128
|
+
|
|
129
|
+
async def set_flag(
|
|
130
|
+
self,
|
|
131
|
+
message_id: str,
|
|
132
|
+
status: OutlookFlagStatus | str = OutlookFlagStatus.FLAGGED,
|
|
133
|
+
) -> Dict[str, Any]:
|
|
134
|
+
return await asyncio.to_thread(self._sync.set_flag, message_id, status)
|
|
135
|
+
|
|
136
|
+
async def set_categories(self, message_id: str, categories: List[str]) -> Dict[str, Any]:
|
|
137
|
+
return await asyncio.to_thread(self._sync.set_categories, message_id, categories)
|
|
138
|
+
|
|
139
|
+
async def move_message(
|
|
140
|
+
self,
|
|
141
|
+
message_id: str,
|
|
142
|
+
folder_id: OutlookWellKnownFolder | str,
|
|
143
|
+
) -> Dict[str, Any]:
|
|
144
|
+
return await asyncio.to_thread(self._sync.move_message, message_id, folder_id)
|
|
145
|
+
|
|
146
|
+
async def delete_message(self, message_id: str) -> None:
|
|
147
|
+
await asyncio.to_thread(self._sync.delete_message, message_id)
|
|
148
|
+
|
|
149
|
+
async def reply(self, message_id: str, *, comment: str = "") -> None:
|
|
150
|
+
await asyncio.to_thread(self._sync.reply, message_id, comment=comment)
|
|
151
|
+
|
|
152
|
+
async def reply_all(self, message_id: str, *, comment: str = "") -> None:
|
|
153
|
+
await asyncio.to_thread(self._sync.reply_all, message_id, comment=comment)
|
|
154
|
+
|
|
155
|
+
async def forward(
|
|
156
|
+
self,
|
|
157
|
+
message_id: str,
|
|
158
|
+
to: List[OutlookEmailAddress],
|
|
159
|
+
*,
|
|
160
|
+
comment: str = "",
|
|
161
|
+
) -> None:
|
|
162
|
+
await asyncio.to_thread(self._sync.forward, message_id, to, comment=comment)
|
|
163
|
+
|
|
164
|
+
async def list_mail_folders(self, *, include_hidden: bool = False) -> List[Dict[str, Any]]:
|
|
165
|
+
return await asyncio.to_thread(self._sync.list_mail_folders, include_hidden=include_hidden)
|
|
166
|
+
|
|
167
|
+
async def get_mail_folder(self, folder_id: OutlookWellKnownFolder | str) -> Dict[str, Any]:
|
|
168
|
+
return await asyncio.to_thread(self._sync.get_mail_folder, folder_id)
|