ergon-framework-python 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl
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/__init__.py +1 -1
- ergon/connector/__init__.py +20 -0
- ergon/connector/ergon_platform/__init__.py +78 -11
- ergon/connector/ergon_platform/async_connector.py +3 -221
- ergon/connector/ergon_platform/channels/__init__.py +54 -0
- ergon/connector/ergon_platform/channels/adapters.py +230 -0
- ergon/connector/ergon_platform/channels/async_connector.py +327 -0
- ergon/connector/ergon_platform/channels/connector.py +294 -0
- ergon/connector/ergon_platform/channels/models.py +460 -0
- ergon/connector/ergon_platform/channels/services/__init__.py +39 -0
- ergon/connector/ergon_platform/channels/services/activity.py +300 -0
- ergon/connector/ergon_platform/channels/services/addresses.py +325 -0
- ergon/connector/ergon_platform/channels/services/attachments.py +283 -0
- ergon/connector/ergon_platform/channels/services/messages.py +127 -0
- ergon/connector/ergon_platform/channels/services/records.py +75 -0
- ergon/connector/ergon_platform/channels/services/service.py +34 -0
- ergon/connector/ergon_platform/connector.py +3 -198
- ergon/connector/ergon_platform/models.py +1 -73
- ergon/connector/ergon_platform/utils.py +39 -248
- ergon/connector/ergon_platform/workflows/__init__.py +52 -0
- ergon/connector/ergon_platform/{_operations.py → workflows/_operations.py} +8 -3
- ergon/connector/ergon_platform/workflows/async_connector.py +224 -0
- ergon/connector/ergon_platform/workflows/connector.py +201 -0
- ergon/connector/ergon_platform/workflows/models.py +90 -0
- ergon/connector/ergon_platform/workflows/utils.py +250 -0
- ergon/task/mixins/consumer.py +0 -2
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/METADATA +3 -3
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/RECORD +32 -15
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/WHEEL +0 -0
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/entry_points.txt +0 -0
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {ergon_framework_python-0.2.1.dist-info → ergon_framework_python-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ...connector import AsyncConnector
|
|
6
|
+
from ...transaction import Transaction
|
|
7
|
+
from .._client import create_ergon_client
|
|
8
|
+
from ..models import ErgonPlatformClient
|
|
9
|
+
from ._operations import ErgonPlatformWorkflowsService
|
|
10
|
+
from .models import (
|
|
11
|
+
CreateItemPayload,
|
|
12
|
+
ErgonPlatformConsumerConfig,
|
|
13
|
+
ErgonPlatformProducerConfig,
|
|
14
|
+
)
|
|
15
|
+
from .utils import get_value, normalize_create_payload
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AsyncErgonPlatformConnector(AsyncConnector):
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
client: ErgonPlatformClient,
|
|
24
|
+
consumer_config: Optional[ErgonPlatformConsumerConfig] = None,
|
|
25
|
+
producer_config: Optional[ErgonPlatformProducerConfig] = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.client = create_ergon_client(client)
|
|
28
|
+
self.service = ErgonPlatformWorkflowsService(client, self.client)
|
|
29
|
+
self._operations = self.service
|
|
30
|
+
self._consumer_config = consumer_config
|
|
31
|
+
self._producer_config = producer_config or ErgonPlatformProducerConfig()
|
|
32
|
+
|
|
33
|
+
async def fetch_transactions_async(
|
|
34
|
+
self,
|
|
35
|
+
batch_size: Optional[int] = None,
|
|
36
|
+
*args,
|
|
37
|
+
**kwargs,
|
|
38
|
+
) -> List[Transaction]:
|
|
39
|
+
if self._consumer_config is None:
|
|
40
|
+
raise ValueError("AsyncErgonPlatformConnector requires a consumer_config to fetch transactions")
|
|
41
|
+
|
|
42
|
+
config = self._consumer_config
|
|
43
|
+
limit = batch_size or config.batch_size
|
|
44
|
+
params: Dict[str, Any] = {**config.list_params, **kwargs}
|
|
45
|
+
if config.unassigned:
|
|
46
|
+
params.pop("assigned_to", None)
|
|
47
|
+
params["assigned"] = "no"
|
|
48
|
+
|
|
49
|
+
transactions = await asyncio.to_thread(
|
|
50
|
+
lambda: self._operations.fetch_items(
|
|
51
|
+
config.workflow_id,
|
|
52
|
+
config.phase_id,
|
|
53
|
+
limit=limit,
|
|
54
|
+
offset=config.offset,
|
|
55
|
+
**params,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
if not config.unassigned:
|
|
59
|
+
return transactions
|
|
60
|
+
|
|
61
|
+
claimed_transactions: List[Transaction] = []
|
|
62
|
+
for transaction in transactions:
|
|
63
|
+
try:
|
|
64
|
+
await asyncio.to_thread(lambda: self.client.workflows.items.claim(transaction.id, {}))
|
|
65
|
+
claimed_transactions.append(await self.fetch_transaction_by_id_async(transaction.id))
|
|
66
|
+
except Exception:
|
|
67
|
+
logger.warning(
|
|
68
|
+
"Failed to claim item %s during unassigned fetch; skipping it",
|
|
69
|
+
transaction.id,
|
|
70
|
+
exc_info=True,
|
|
71
|
+
)
|
|
72
|
+
return claimed_transactions
|
|
73
|
+
|
|
74
|
+
async def dispatch_transactions_async(
|
|
75
|
+
self,
|
|
76
|
+
transactions: List[Transaction],
|
|
77
|
+
*args,
|
|
78
|
+
**kwargs,
|
|
79
|
+
) -> List[str]:
|
|
80
|
+
created_ids: List[str] = []
|
|
81
|
+
for transaction in transactions:
|
|
82
|
+
result = await self._create_from_payload(transaction.payload)
|
|
83
|
+
created_ids.append(self._created_item_id(result))
|
|
84
|
+
return created_ids
|
|
85
|
+
|
|
86
|
+
async def fetch_child_transactions_async(
|
|
87
|
+
self,
|
|
88
|
+
parent_item_id: str,
|
|
89
|
+
*args,
|
|
90
|
+
**kwargs,
|
|
91
|
+
) -> List[Transaction]:
|
|
92
|
+
return await asyncio.to_thread(lambda: self._operations.fetch_child_items(parent_item_id, **kwargs))
|
|
93
|
+
|
|
94
|
+
async def fetch_transaction_by_id_async(self, transaction_id: str, *args, **kwargs) -> Transaction:
|
|
95
|
+
workflow_id = self._consumer_config.workflow_id if self._consumer_config else ""
|
|
96
|
+
return await asyncio.to_thread(
|
|
97
|
+
lambda: self._operations.get_item_transaction(transaction_id, workflow_id, **kwargs)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def fetch_items_by_query(
|
|
101
|
+
self, workflow_id: str, query: Optional[Dict[str, Any]] = None, **fields: Any
|
|
102
|
+
) -> List[Transaction]:
|
|
103
|
+
return await asyncio.to_thread(lambda: self._operations.fetch_items_by_query(workflow_id, query, **fields))
|
|
104
|
+
|
|
105
|
+
async def get_transactions_count_async(self, *args, **kwargs) -> int:
|
|
106
|
+
if self._consumer_config is None:
|
|
107
|
+
raise ValueError("AsyncErgonPlatformConnector requires a consumer_config to count transactions")
|
|
108
|
+
|
|
109
|
+
config = self._consumer_config
|
|
110
|
+
params: Dict[str, Any] = {**config.list_params, **kwargs}
|
|
111
|
+
return await asyncio.to_thread(
|
|
112
|
+
lambda: self._operations.get_phase_items_count(
|
|
113
|
+
config.workflow_id,
|
|
114
|
+
config.phase_id,
|
|
115
|
+
**params,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
async def ack_transaction(self, transaction: Transaction, phase_id: Optional[str] = None) -> None:
|
|
120
|
+
target_phase = phase_id
|
|
121
|
+
if target_phase is None and self._consumer_config is not None:
|
|
122
|
+
target_phase = self._consumer_config.ack_phase_id
|
|
123
|
+
if not target_phase:
|
|
124
|
+
return
|
|
125
|
+
await asyncio.to_thread(lambda: self.client.workflows.items.route(transaction.id, to_phase_id=target_phase))
|
|
126
|
+
|
|
127
|
+
async def release_item(
|
|
128
|
+
self,
|
|
129
|
+
item_id: str,
|
|
130
|
+
data: Optional[Dict[str, Any]] = None,
|
|
131
|
+
*,
|
|
132
|
+
delay_seconds: Optional[int] = None,
|
|
133
|
+
**fields: Any,
|
|
134
|
+
) -> Any:
|
|
135
|
+
return await asyncio.to_thread(
|
|
136
|
+
lambda: self._operations.release_item(
|
|
137
|
+
item_id,
|
|
138
|
+
data,
|
|
139
|
+
delay_seconds=delay_seconds,
|
|
140
|
+
**fields,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def nack_transaction(self, transaction: Transaction, requeue: bool = True, delay_seconds: int = 10) -> None:
|
|
145
|
+
if requeue:
|
|
146
|
+
await asyncio.to_thread(lambda: self._operations.release_item(transaction.id, delay_seconds=delay_seconds))
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
target_phase = self._consumer_config.nack_phase_id if self._consumer_config is not None else None
|
|
150
|
+
if not target_phase:
|
|
151
|
+
raise ValueError("nack_phase_id is required when nack_transaction is called with requeue=False")
|
|
152
|
+
|
|
153
|
+
await asyncio.to_thread(lambda: self.client.workflows.items.route(transaction.id, to_phase_id=target_phase))
|
|
154
|
+
|
|
155
|
+
async def close(self) -> None:
|
|
156
|
+
await asyncio.to_thread(self.client.close)
|
|
157
|
+
|
|
158
|
+
async def list_phase_fields(
|
|
159
|
+
self,
|
|
160
|
+
phase_id: str,
|
|
161
|
+
*,
|
|
162
|
+
workflow_id: Optional[str] = None,
|
|
163
|
+
include_workflow_fields: bool = True,
|
|
164
|
+
**params: Any,
|
|
165
|
+
) -> Any:
|
|
166
|
+
return await asyncio.to_thread(
|
|
167
|
+
lambda: self._operations.list_phase_fields(
|
|
168
|
+
phase_id,
|
|
169
|
+
workflow_id=workflow_id,
|
|
170
|
+
include_workflow_fields=include_workflow_fields,
|
|
171
|
+
**params,
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
async def get_pipeline_result(
|
|
176
|
+
self,
|
|
177
|
+
workflow_id: str,
|
|
178
|
+
item_id: str,
|
|
179
|
+
field_id: str,
|
|
180
|
+
buckets_file_id: Optional[str] = None,
|
|
181
|
+
) -> Dict[str, Any]:
|
|
182
|
+
return await asyncio.to_thread(
|
|
183
|
+
self._operations.get_pipeline_result,
|
|
184
|
+
workflow_id,
|
|
185
|
+
item_id,
|
|
186
|
+
field_id,
|
|
187
|
+
buckets_file_id,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
async def _create_from_payload(self, payload: CreateItemPayload) -> Any:
|
|
191
|
+
data = normalize_create_payload(payload)
|
|
192
|
+
producer = self._producer_config
|
|
193
|
+
|
|
194
|
+
workflow_id = data.get("workflow_id") or producer.workflow_id
|
|
195
|
+
phase_id = data.get("phase_id") or producer.phase_id
|
|
196
|
+
if not workflow_id:
|
|
197
|
+
raise ValueError("workflow_id is required to create an item (set it in payload or producer_config)")
|
|
198
|
+
if not phase_id:
|
|
199
|
+
raise ValueError("phase_id is required to create an item (set it in payload or producer_config)")
|
|
200
|
+
|
|
201
|
+
attachment = data.get("attachment")
|
|
202
|
+
attachment_field_id = data.get("attachment_field_id") or producer.attachment_field_id
|
|
203
|
+
content_type = data.get("content_type") or producer.default_content_type
|
|
204
|
+
parent_item_id = data.get("parent_item_id") or producer.parent_item_id
|
|
205
|
+
|
|
206
|
+
return await asyncio.to_thread(
|
|
207
|
+
lambda: self._operations.create_item(
|
|
208
|
+
workflow_id,
|
|
209
|
+
phase_id,
|
|
210
|
+
data["title"],
|
|
211
|
+
parent_item_id=parent_item_id,
|
|
212
|
+
field_values=data.get("field_values"),
|
|
213
|
+
attachment=attachment,
|
|
214
|
+
attachment_field_id=attachment_field_id,
|
|
215
|
+
content_type=content_type,
|
|
216
|
+
**data.get("fields", {}),
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _created_item_id(result: Any) -> str:
|
|
222
|
+
if isinstance(result, dict) and "item" in result:
|
|
223
|
+
return str(get_value(result["item"], "id", ""))
|
|
224
|
+
return str(get_value(result, "id", ""))
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from ...connector import Connector
|
|
5
|
+
from ...transaction import Transaction
|
|
6
|
+
from .._client import create_ergon_client
|
|
7
|
+
from ..models import ErgonPlatformClient
|
|
8
|
+
from ._operations import ErgonPlatformWorkflowsService
|
|
9
|
+
from .models import (
|
|
10
|
+
CreateItemPayload,
|
|
11
|
+
ErgonPlatformConsumerConfig,
|
|
12
|
+
ErgonPlatformProducerConfig,
|
|
13
|
+
)
|
|
14
|
+
from .utils import get_value, normalize_create_payload
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ErgonPlatformConnector(Connector):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
client: ErgonPlatformClient,
|
|
23
|
+
consumer_config: Optional[ErgonPlatformConsumerConfig] = None,
|
|
24
|
+
producer_config: Optional[ErgonPlatformProducerConfig] = None,
|
|
25
|
+
) -> None:
|
|
26
|
+
self.client = create_ergon_client(client)
|
|
27
|
+
self.service = ErgonPlatformWorkflowsService(client, self.client)
|
|
28
|
+
self._operations = self.service
|
|
29
|
+
self._consumer_config = consumer_config
|
|
30
|
+
self._producer_config = producer_config or ErgonPlatformProducerConfig()
|
|
31
|
+
|
|
32
|
+
def fetch_transactions(
|
|
33
|
+
self,
|
|
34
|
+
batch_size: Optional[int] = None,
|
|
35
|
+
*args,
|
|
36
|
+
**kwargs,
|
|
37
|
+
) -> List[Transaction]:
|
|
38
|
+
if self._consumer_config is None:
|
|
39
|
+
raise ValueError("ErgonPlatformConnector requires a consumer_config to fetch transactions")
|
|
40
|
+
|
|
41
|
+
config = self._consumer_config
|
|
42
|
+
limit = batch_size or config.batch_size
|
|
43
|
+
params: Dict[str, Any] = {**config.list_params, **kwargs}
|
|
44
|
+
if config.unassigned:
|
|
45
|
+
params.pop("assigned_to", None)
|
|
46
|
+
params["assigned"] = "no"
|
|
47
|
+
|
|
48
|
+
transactions = self._operations.fetch_items(
|
|
49
|
+
config.workflow_id,
|
|
50
|
+
config.phase_id,
|
|
51
|
+
limit=limit,
|
|
52
|
+
offset=config.offset,
|
|
53
|
+
**params,
|
|
54
|
+
)
|
|
55
|
+
if not config.unassigned:
|
|
56
|
+
return transactions
|
|
57
|
+
|
|
58
|
+
claimed_transactions: List[Transaction] = []
|
|
59
|
+
for transaction in transactions:
|
|
60
|
+
try:
|
|
61
|
+
self.client.workflows.items.claim(transaction.id, {})
|
|
62
|
+
claimed_transactions.append(self.fetch_transaction_by_id(transaction.id))
|
|
63
|
+
except Exception:
|
|
64
|
+
logger.warning(
|
|
65
|
+
"Failed to claim item %s during unassigned fetch; skipping it",
|
|
66
|
+
transaction.id,
|
|
67
|
+
exc_info=True,
|
|
68
|
+
)
|
|
69
|
+
return claimed_transactions
|
|
70
|
+
|
|
71
|
+
def dispatch_transactions(self, transactions: List[Transaction], *args, **kwargs) -> List[str]:
|
|
72
|
+
created_ids: List[str] = []
|
|
73
|
+
for transaction in transactions:
|
|
74
|
+
result = self._create_from_payload(transaction.payload)
|
|
75
|
+
created_ids.append(self._created_item_id(result))
|
|
76
|
+
return created_ids
|
|
77
|
+
|
|
78
|
+
def fetch_child_transactions(
|
|
79
|
+
self,
|
|
80
|
+
parent_item_id: str,
|
|
81
|
+
*args,
|
|
82
|
+
**kwargs,
|
|
83
|
+
) -> List[Transaction]:
|
|
84
|
+
return self._operations.fetch_child_items(parent_item_id, **kwargs)
|
|
85
|
+
|
|
86
|
+
def fetch_transaction_by_id(self, transaction_id: str, *args, **kwargs) -> Transaction:
|
|
87
|
+
workflow_id = self._consumer_config.workflow_id if self._consumer_config else ""
|
|
88
|
+
return self._operations.get_item_transaction(transaction_id, workflow_id, **kwargs)
|
|
89
|
+
|
|
90
|
+
def fetch_items_by_query(
|
|
91
|
+
self, workflow_id: str, query: Optional[Dict[str, Any]] = None, **fields: Any
|
|
92
|
+
) -> List[Transaction]:
|
|
93
|
+
return self._operations.fetch_items_by_query(workflow_id, query, **fields)
|
|
94
|
+
|
|
95
|
+
def get_transactions_count(self, *args, **kwargs) -> int:
|
|
96
|
+
if self._consumer_config is None:
|
|
97
|
+
raise ValueError("ErgonPlatformConnector requires a consumer_config to count transactions")
|
|
98
|
+
|
|
99
|
+
config = self._consumer_config
|
|
100
|
+
params: Dict[str, Any] = {**config.list_params, **kwargs}
|
|
101
|
+
return self._operations.get_phase_items_count(
|
|
102
|
+
config.workflow_id,
|
|
103
|
+
config.phase_id,
|
|
104
|
+
**params,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def ack_transaction(self, transaction: Transaction, phase_id: Optional[str] = None) -> None:
|
|
108
|
+
target_phase = phase_id
|
|
109
|
+
if target_phase is None and self._consumer_config is not None:
|
|
110
|
+
target_phase = self._consumer_config.ack_phase_id
|
|
111
|
+
if not target_phase:
|
|
112
|
+
return
|
|
113
|
+
self.client.workflows.items.route(transaction.id, to_phase_id=target_phase)
|
|
114
|
+
|
|
115
|
+
def release_item(
|
|
116
|
+
self,
|
|
117
|
+
item_id: str,
|
|
118
|
+
data: Optional[Dict[str, Any]] = None,
|
|
119
|
+
*,
|
|
120
|
+
delay_seconds: Optional[int] = None,
|
|
121
|
+
**fields: Any,
|
|
122
|
+
) -> Any:
|
|
123
|
+
return self._operations.release_item(
|
|
124
|
+
item_id,
|
|
125
|
+
data,
|
|
126
|
+
delay_seconds=delay_seconds,
|
|
127
|
+
**fields,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def nack_transaction(self, transaction: Transaction, requeue: bool = True, delay_seconds: int = 10) -> None:
|
|
131
|
+
if requeue:
|
|
132
|
+
self._operations.release_item(transaction.id, delay_seconds=delay_seconds)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
target_phase = self._consumer_config.nack_phase_id if self._consumer_config is not None else None
|
|
136
|
+
if not target_phase:
|
|
137
|
+
raise ValueError("nack_phase_id is required when nack_transaction is called with requeue=False")
|
|
138
|
+
|
|
139
|
+
self.client.workflows.items.route(transaction.id, to_phase_id=target_phase)
|
|
140
|
+
logger.debug("Item %s moved to nack phase %s", transaction.id, target_phase)
|
|
141
|
+
|
|
142
|
+
def close(self) -> None:
|
|
143
|
+
self.client.close()
|
|
144
|
+
|
|
145
|
+
def list_phase_fields(
|
|
146
|
+
self,
|
|
147
|
+
phase_id: str,
|
|
148
|
+
*,
|
|
149
|
+
workflow_id: Optional[str] = None,
|
|
150
|
+
include_workflow_fields: bool = True,
|
|
151
|
+
**params: Any,
|
|
152
|
+
) -> Any:
|
|
153
|
+
return self._operations.list_phase_fields(
|
|
154
|
+
phase_id,
|
|
155
|
+
workflow_id=workflow_id,
|
|
156
|
+
include_workflow_fields=include_workflow_fields,
|
|
157
|
+
**params,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def get_pipeline_result(
|
|
161
|
+
self,
|
|
162
|
+
workflow_id: str,
|
|
163
|
+
item_id: str,
|
|
164
|
+
field_id: str,
|
|
165
|
+
buckets_file_id: Optional[str] = None,
|
|
166
|
+
) -> Dict[str, Any]:
|
|
167
|
+
return self._operations.get_pipeline_result(workflow_id, item_id, field_id, buckets_file_id)
|
|
168
|
+
|
|
169
|
+
def _create_from_payload(self, payload: CreateItemPayload) -> Any:
|
|
170
|
+
data = normalize_create_payload(payload)
|
|
171
|
+
producer = self._producer_config
|
|
172
|
+
|
|
173
|
+
workflow_id = data.get("workflow_id") or producer.workflow_id
|
|
174
|
+
phase_id = data.get("phase_id") or producer.phase_id
|
|
175
|
+
if not workflow_id:
|
|
176
|
+
raise ValueError("workflow_id is required to create an item (set it in payload or producer_config)")
|
|
177
|
+
if not phase_id:
|
|
178
|
+
raise ValueError("phase_id is required to create an item (set it in payload or producer_config)")
|
|
179
|
+
|
|
180
|
+
attachment = data.get("attachment")
|
|
181
|
+
attachment_field_id = data.get("attachment_field_id") or producer.attachment_field_id
|
|
182
|
+
content_type = data.get("content_type") or producer.default_content_type
|
|
183
|
+
parent_item_id = data.get("parent_item_id") or producer.parent_item_id
|
|
184
|
+
|
|
185
|
+
return self._operations.create_item(
|
|
186
|
+
workflow_id,
|
|
187
|
+
phase_id,
|
|
188
|
+
data["title"],
|
|
189
|
+
parent_item_id=parent_item_id,
|
|
190
|
+
field_values=data.get("field_values"),
|
|
191
|
+
attachment=attachment,
|
|
192
|
+
attachment_field_id=attachment_field_id,
|
|
193
|
+
content_type=content_type,
|
|
194
|
+
**data.get("fields", {}),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def _created_item_id(result: Any) -> str:
|
|
199
|
+
if isinstance(result, dict) and "item" in result:
|
|
200
|
+
return str(get_value(result["item"], "id", ""))
|
|
201
|
+
return str(get_value(result, "id", ""))
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
# Status buckets returned by the Ergon Platform attachment pipeline.
|
|
6
|
+
SUCCESS_STATUSES = {"completed", "complete", "succeeded", "success", "processed", "ready"}
|
|
7
|
+
FAILURE_STATUSES = {"failed", "error", "errored", "skipped", "oversized"}
|
|
8
|
+
PROCESSING_STATUSES = {
|
|
9
|
+
"created",
|
|
10
|
+
"in-progress",
|
|
11
|
+
"in_progress",
|
|
12
|
+
"pending",
|
|
13
|
+
"processing",
|
|
14
|
+
"queued",
|
|
15
|
+
"running",
|
|
16
|
+
"stored",
|
|
17
|
+
"uploaded",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ErgonPlatformConsumerConfig(BaseModel):
|
|
22
|
+
"""Parameters used to list workflow items as transactions."""
|
|
23
|
+
|
|
24
|
+
workflow_id: str = Field(description="Workflow ID to consume items from")
|
|
25
|
+
phase_id: str = Field(description="Phase ID to list items from")
|
|
26
|
+
batch_size: int = Field(default=50, ge=1, description="Max items per fetch")
|
|
27
|
+
offset: int = Field(default=0, ge=0, description="Pagination offset")
|
|
28
|
+
ack_phase_id: Optional[str] = Field(
|
|
29
|
+
default=None,
|
|
30
|
+
description="Phase to route an item to on ack; ack is a no-op when omitted",
|
|
31
|
+
)
|
|
32
|
+
nack_phase_id: Optional[str] = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
description="Phase to route an item to on nack when requeue=False",
|
|
35
|
+
)
|
|
36
|
+
unassigned: bool = Field(
|
|
37
|
+
default=False,
|
|
38
|
+
description="When true, fetch only unassigned cards and claim them during fetch",
|
|
39
|
+
)
|
|
40
|
+
list_params: Dict[str, Any] = Field(
|
|
41
|
+
default_factory=dict,
|
|
42
|
+
description="Extra query params forwarded to the items listing endpoint",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ErgonPlatformProducerConfig(BaseModel):
|
|
47
|
+
"""Defaults applied when creating items via dispatch."""
|
|
48
|
+
|
|
49
|
+
workflow_id: Optional[str] = Field(default=None, description="Default workflow for created items")
|
|
50
|
+
phase_id: Optional[str] = Field(default=None, description="Default phase for created items")
|
|
51
|
+
attachment_field_id: Optional[str] = Field(
|
|
52
|
+
default=None,
|
|
53
|
+
description="Default field ID used when an attachment is provided",
|
|
54
|
+
)
|
|
55
|
+
default_content_type: Optional[str] = Field(
|
|
56
|
+
default=None,
|
|
57
|
+
description="Fallback MIME type when it cannot be guessed from the filename",
|
|
58
|
+
)
|
|
59
|
+
parent_item_id: Optional[str] = Field(
|
|
60
|
+
default=None,
|
|
61
|
+
description="Default parent item ID used to create child items via dispatch",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class CreateItemInput(BaseModel):
|
|
66
|
+
"""Payload describing an item to create, with optional attachment upload."""
|
|
67
|
+
|
|
68
|
+
title: str = Field(description="Item title")
|
|
69
|
+
workflow_id: Optional[str] = Field(default=None, description="Workflow ID; falls back to producer config")
|
|
70
|
+
phase_id: Optional[str] = Field(default=None, description="Target phase ID; falls back to producer config")
|
|
71
|
+
parent_item_id: Optional[str] = Field(
|
|
72
|
+
default=None,
|
|
73
|
+
description="Optional parent item ID to create this item as a child",
|
|
74
|
+
)
|
|
75
|
+
field_values: Optional[Dict[str, Any]] = Field(default=None, description="Field values keyed by field ID")
|
|
76
|
+
attachment: Optional[str] = Field(default=None, description="Local file path to upload after creation")
|
|
77
|
+
attachment_field_id: Optional[str] = Field(
|
|
78
|
+
default=None,
|
|
79
|
+
description="Field ID that receives the attachment; falls back to producer config",
|
|
80
|
+
)
|
|
81
|
+
content_type: Optional[str] = Field(default=None, description="Override MIME type for the attachment")
|
|
82
|
+
extra_fields: Dict[str, Any] = Field(
|
|
83
|
+
default_factory=dict,
|
|
84
|
+
description="Additional keyword fields forwarded to create_item",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
model_config = {"populate_by_name": True}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
CreateItemPayload = Union[CreateItemInput, Dict[str, Any]]
|