universal-mcp-applications 0.1.32__py3-none-any.whl → 0.1.36rc2__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.
- universal_mcp/applications/ahrefs/app.py +52 -198
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +111 -464
- universal_mcp/applications/asana/app.py +417 -1567
- universal_mcp/applications/aws_s3/app.py +36 -103
- universal_mcp/applications/bill/app.py +546 -1957
- universal_mcp/applications/box/app.py +1068 -3981
- universal_mcp/applications/braze/app.py +364 -1430
- universal_mcp/applications/browser_use/app.py +2 -8
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +61 -200
- universal_mcp/applications/canva/app.py +45 -110
- universal_mcp/applications/clickup/app.py +207 -674
- universal_mcp/applications/coda/app.py +146 -426
- universal_mcp/applications/confluence/app.py +310 -1098
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +28 -107
- universal_mcp/applications/dialpad/app.py +283 -756
- universal_mcp/applications/digitalocean/app.py +1766 -5777
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +14 -64
- universal_mcp/applications/elevenlabs/app.py +9 -47
- universal_mcp/applications/exa/app.py +6 -17
- universal_mcp/applications/falai/app.py +24 -101
- universal_mcp/applications/figma/app.py +53 -137
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +51 -152
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +91 -528
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +52 -161
- universal_mcp/applications/github/app.py +19 -56
- universal_mcp/applications/gong/app.py +88 -248
- universal_mcp/applications/google_calendar/app.py +16 -68
- universal_mcp/applications/google_docs/app.py +85 -189
- universal_mcp/applications/google_drive/app.py +141 -463
- universal_mcp/applications/google_gemini/app.py +12 -64
- universal_mcp/applications/google_mail/app.py +28 -157
- universal_mcp/applications/google_searchconsole/app.py +15 -48
- universal_mcp/applications/google_sheet/app.py +100 -581
- universal_mcp/applications/google_sheet/helper.py +10 -37
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +44 -122
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
- universal_mcp/applications/hubspot/app.py +23 -87
- universal_mcp/applications/jira/app.py +2071 -7986
- universal_mcp/applications/klaviyo/app.py +494 -1376
- universal_mcp/applications/linkedin/README.md +9 -2
- universal_mcp/applications/linkedin/app.py +240 -181
- universal_mcp/applications/mailchimp/app.py +450 -1605
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +217 -699
- universal_mcp/applications/ms_teams/app.py +64 -186
- universal_mcp/applications/neon/app.py +86 -192
- universal_mcp/applications/notion/app.py +21 -36
- universal_mcp/applications/onedrive/app.py +16 -38
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/app.py +24 -84
- universal_mcp/applications/perplexity/app.py +4 -19
- universal_mcp/applications/pipedrive/app.py +832 -3142
- universal_mcp/applications/posthog/app.py +163 -432
- universal_mcp/applications/reddit/app.py +40 -139
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +14 -41
- universal_mcp/applications/rocketlane/app.py +221 -934
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +50 -109
- universal_mcp/applications/semanticscholar/app.py +22 -64
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +512 -1262
- universal_mcp/applications/sentry/app.py +271 -906
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/app.py +17 -39
- universal_mcp/applications/shopify/app.py +1551 -4287
- universal_mcp/applications/shortcut/app.py +155 -417
- universal_mcp/applications/slack/app.py +33 -115
- universal_mcp/applications/spotify/app.py +126 -325
- universal_mcp/applications/supabase/app.py +104 -213
- universal_mcp/applications/tavily/app.py +1 -1
- universal_mcp/applications/trello/app.py +693 -2656
- universal_mcp/applications/twilio/app.py +14 -50
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +70 -283
- universal_mcp/applications/wrike/app.py +45 -118
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +75 -261
- universal_mcp/applications/zenquotes/app.py +2 -2
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/RECORD +105 -105
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
from collections.abc import Iterable
|
|
2
2
|
from typing import Any
|
|
3
|
-
|
|
4
|
-
# Import necessary components from pyairtable
|
|
5
3
|
from pyairtable import Api
|
|
6
4
|
from pyairtable.api.base import Base
|
|
7
5
|
from pyairtable.api.table import Table
|
|
8
|
-
from pyairtable.api.types import
|
|
9
|
-
RecordDeletedDict,
|
|
10
|
-
RecordDict,
|
|
11
|
-
RecordId,
|
|
12
|
-
UpdateRecordDict,
|
|
13
|
-
UpsertResultDict,
|
|
14
|
-
WritableFields,
|
|
15
|
-
)
|
|
6
|
+
from pyairtable.api.types import RecordDeletedDict, RecordDict, RecordId, UpdateRecordDict, UpsertResultDict, WritableFields
|
|
16
7
|
from pyairtable.formulas import Formula, to_formula_str
|
|
17
8
|
from universal_mcp.applications.application import APIApplication
|
|
18
9
|
from universal_mcp.integrations import Integration
|
|
@@ -32,18 +23,12 @@ class AirtableApp(APIApplication):
|
|
|
32
23
|
if not self.integration:
|
|
33
24
|
raise ValueError("Integration is not set for AirtableApp.")
|
|
34
25
|
credentials = self.integration.get_credentials()
|
|
35
|
-
api_key = (
|
|
36
|
-
credentials.get("api_key")
|
|
37
|
-
or credentials.get("apiKey")
|
|
38
|
-
or credentials.get("API_KEY")
|
|
39
|
-
)
|
|
26
|
+
api_key = credentials.get("api_key") or credentials.get("apiKey") or credentials.get("API_KEY")
|
|
40
27
|
if not api_key:
|
|
41
28
|
raise ValueError("Airtable API key is not configured in the integration.")
|
|
42
29
|
return Api(api_key)
|
|
43
30
|
|
|
44
|
-
def _prepare_pyairtable_params(
|
|
45
|
-
self, collected_options: dict[str, Any]
|
|
46
|
-
) -> dict[str, Any]:
|
|
31
|
+
def _prepare_pyairtable_params(self, collected_options: dict[str, Any]) -> dict[str, Any]:
|
|
47
32
|
"""
|
|
48
33
|
Extracts the actual parameters for pyairtable from the collected options.
|
|
49
34
|
If `collected_options` contains a key "options" whose value is a dictionary
|
|
@@ -53,10 +38,9 @@ class AirtableApp(APIApplication):
|
|
|
53
38
|
nested_options = collected_options.get("options")
|
|
54
39
|
if isinstance(nested_options, dict):
|
|
55
40
|
return nested_options
|
|
56
|
-
|
|
57
41
|
return collected_options
|
|
58
42
|
|
|
59
|
-
def list_bases(self) -> list[Base] | str:
|
|
43
|
+
async def list_bases(self) -> list[Base] | str:
|
|
60
44
|
"""
|
|
61
45
|
Lists all bases accessible with the current API key.
|
|
62
46
|
|
|
@@ -73,7 +57,7 @@ class AirtableApp(APIApplication):
|
|
|
73
57
|
except Exception as e:
|
|
74
58
|
return f"Error listing bases: {type(e).__name__} - {e}"
|
|
75
59
|
|
|
76
|
-
def list_tables(self, base_id: str) -> list[Table] | str:
|
|
60
|
+
async def list_tables(self, base_id: str) -> list[Table] | str:
|
|
77
61
|
"""
|
|
78
62
|
Lists all tables within a specified base.
|
|
79
63
|
|
|
@@ -92,13 +76,9 @@ class AirtableApp(APIApplication):
|
|
|
92
76
|
base = client.base(base_id)
|
|
93
77
|
return base.tables()
|
|
94
78
|
except Exception as e:
|
|
95
|
-
return (
|
|
96
|
-
f"Error listing tables for base '{base_id}': {type(e).__name__} - {e}"
|
|
97
|
-
)
|
|
79
|
+
return f"Error listing tables for base '{base_id}': {type(e).__name__} - {e}"
|
|
98
80
|
|
|
99
|
-
def get_record(
|
|
100
|
-
self, base_id: str, table_id_or_name: str, record_id: RecordId, **options: Any
|
|
101
|
-
) -> RecordDict | str:
|
|
81
|
+
async def get_record(self, base_id: str, table_id_or_name: str, record_id: RecordId, **options: Any) -> RecordDict | str:
|
|
102
82
|
"""
|
|
103
83
|
Retrieves a single record by its ID from a specified table within a base.
|
|
104
84
|
|
|
@@ -125,9 +105,7 @@ class AirtableApp(APIApplication):
|
|
|
125
105
|
except Exception as e:
|
|
126
106
|
return f"Error getting record '{record_id}' from '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
127
107
|
|
|
128
|
-
def list_records(
|
|
129
|
-
self, base_id: str, table_id_or_name: str, **options: Any
|
|
130
|
-
) -> list[RecordDict] | str:
|
|
108
|
+
async def list_records(self, base_id: str, table_id_or_name: str, **options: Any) -> list[RecordDict] | str:
|
|
131
109
|
"""
|
|
132
110
|
Lists records from a specified table within a base.
|
|
133
111
|
|
|
@@ -150,26 +128,13 @@ class AirtableApp(APIApplication):
|
|
|
150
128
|
client = self._get_client()
|
|
151
129
|
table = client.table(base_id, table_id_or_name)
|
|
152
130
|
pyairtable_params = self._prepare_pyairtable_params(options)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if "formula" in pyairtable_params and isinstance(
|
|
156
|
-
pyairtable_params["formula"], Formula
|
|
157
|
-
):
|
|
158
|
-
pyairtable_params["formula"] = to_formula_str(
|
|
159
|
-
pyairtable_params["formula"]
|
|
160
|
-
)
|
|
161
|
-
|
|
131
|
+
if "formula" in pyairtable_params and isinstance(pyairtable_params["formula"], Formula):
|
|
132
|
+
pyairtable_params["formula"] = to_formula_str(pyairtable_params["formula"])
|
|
162
133
|
return table.all(**pyairtable_params)
|
|
163
134
|
except Exception as e:
|
|
164
135
|
return f"Error listing records from '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
165
136
|
|
|
166
|
-
def create_record(
|
|
167
|
-
self,
|
|
168
|
-
base_id: str,
|
|
169
|
-
table_id_or_name: str,
|
|
170
|
-
fields: WritableFields,
|
|
171
|
-
**options: Any, # Captures typecast, use_field_ids etc.
|
|
172
|
-
) -> RecordDict | str:
|
|
137
|
+
async def create_record(self, base_id: str, table_id_or_name: str, fields: WritableFields, **options: Any) -> RecordDict | str:
|
|
173
138
|
"""
|
|
174
139
|
Creates a new record in a specified table within a base.
|
|
175
140
|
|
|
@@ -191,44 +156,20 @@ class AirtableApp(APIApplication):
|
|
|
191
156
|
try:
|
|
192
157
|
client = self._get_client()
|
|
193
158
|
table = client.table(base_id, table_id_or_name)
|
|
194
|
-
# pyairtable's Table.create() takes typecast and use_field_ids as named args,
|
|
195
|
-
# not as **kwargs. We need to extract them or use defaults.
|
|
196
|
-
|
|
197
159
|
prepared_options = self._prepare_pyairtable_params(options)
|
|
198
160
|
prepared_options.get("typecast", False)
|
|
199
|
-
prepared_options.get(
|
|
200
|
-
"use_field_ids"
|
|
201
|
-
) # Let pyairtable handle default if None
|
|
202
|
-
|
|
203
|
-
# Ensure only valid kwargs for pyairtable's `create` are passed if it doesn't use **kwargs for these
|
|
204
|
-
# For pyairtable.Table.create, it only takes `typecast` and `use_field_ids` as specific keyword args.
|
|
205
|
-
# The `**options` in `AirtableApp` could collect other things if the tool call sends them.
|
|
206
|
-
# The `pyairtable.Table.create` signature is:
|
|
207
|
-
# create(self, fields: WritableFields, typecast: bool = False, use_field_ids: Optional[bool] = None)
|
|
208
|
-
# It does NOT take a general **kwargs.
|
|
209
|
-
|
|
210
|
-
# So, we must call it with the specific parameters.
|
|
211
|
-
# `_prepare_pyairtable_params` helps if "options" is nested, but we still need to map.
|
|
212
|
-
|
|
161
|
+
prepared_options.get("use_field_ids")
|
|
213
162
|
call_kwargs = {}
|
|
214
163
|
if "typecast" in prepared_options:
|
|
215
164
|
call_kwargs["typecast"] = prepared_options["typecast"]
|
|
216
|
-
if
|
|
217
|
-
"use_field_ids" in prepared_options
|
|
218
|
-
): # Pass it only if explicitly provided
|
|
165
|
+
if "use_field_ids" in prepared_options:
|
|
219
166
|
call_kwargs["use_field_ids"] = prepared_options["use_field_ids"]
|
|
220
|
-
|
|
221
167
|
return table.create(fields=fields, **call_kwargs)
|
|
222
168
|
except Exception as e:
|
|
223
169
|
return f"Error creating record in '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
224
170
|
|
|
225
|
-
def update_record(
|
|
226
|
-
self,
|
|
227
|
-
base_id: str,
|
|
228
|
-
table_id_or_name: str,
|
|
229
|
-
record_id: RecordId,
|
|
230
|
-
fields: WritableFields,
|
|
231
|
-
**options: Any, # Captures replace, typecast, use_field_ids etc.
|
|
171
|
+
async def update_record(
|
|
172
|
+
self, base_id: str, table_id_or_name: str, record_id: RecordId, fields: WritableFields, **options: Any
|
|
232
173
|
) -> RecordDict | str:
|
|
233
174
|
"""
|
|
234
175
|
Updates an existing record in a specified table within a base.
|
|
@@ -252,11 +193,6 @@ class AirtableApp(APIApplication):
|
|
|
252
193
|
try:
|
|
253
194
|
client = self._get_client()
|
|
254
195
|
table = client.table(base_id, table_id_or_name)
|
|
255
|
-
# pyairtable.Table.update() signature:
|
|
256
|
-
# update(self, record_id: RecordId, fields: WritableFields, replace: bool = False,
|
|
257
|
-
# typecast: bool = False, use_field_ids: Optional[bool] = None)
|
|
258
|
-
# It does NOT take a general **kwargs.
|
|
259
|
-
|
|
260
196
|
prepared_options = self._prepare_pyairtable_params(options)
|
|
261
197
|
call_kwargs = {}
|
|
262
198
|
if "replace" in prepared_options:
|
|
@@ -265,14 +201,11 @@ class AirtableApp(APIApplication):
|
|
|
265
201
|
call_kwargs["typecast"] = prepared_options["typecast"]
|
|
266
202
|
if "use_field_ids" in prepared_options:
|
|
267
203
|
call_kwargs["use_field_ids"] = prepared_options["use_field_ids"]
|
|
268
|
-
|
|
269
204
|
return table.update(record_id, fields=fields, **call_kwargs)
|
|
270
205
|
except Exception as e:
|
|
271
206
|
return f"Error updating record '{record_id}' in '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
272
207
|
|
|
273
|
-
def delete_record(
|
|
274
|
-
self, base_id: str, table_id_or_name: str, record_id: RecordId
|
|
275
|
-
) -> RecordDeletedDict | str:
|
|
208
|
+
async def delete_record(self, base_id: str, table_id_or_name: str, record_id: RecordId) -> RecordDeletedDict | str:
|
|
276
209
|
"""
|
|
277
210
|
Deletes a record from a specified table within a base.
|
|
278
211
|
|
|
@@ -295,12 +228,8 @@ class AirtableApp(APIApplication):
|
|
|
295
228
|
except Exception as e:
|
|
296
229
|
return f"Error deleting record '{record_id}' from '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
297
230
|
|
|
298
|
-
def batch_create_records(
|
|
299
|
-
self,
|
|
300
|
-
base_id: str,
|
|
301
|
-
table_id_or_name: str,
|
|
302
|
-
records: Iterable[WritableFields],
|
|
303
|
-
**options: Any, # Captures typecast, use_field_ids etc.
|
|
231
|
+
async def batch_create_records(
|
|
232
|
+
self, base_id: str, table_id_or_name: str, records: Iterable[WritableFields], **options: Any
|
|
304
233
|
) -> list[RecordDict] | str:
|
|
305
234
|
"""
|
|
306
235
|
Creates multiple records in batches in a specified table.
|
|
@@ -323,27 +252,18 @@ class AirtableApp(APIApplication):
|
|
|
323
252
|
try:
|
|
324
253
|
client = self._get_client()
|
|
325
254
|
table = client.table(base_id, table_id_or_name)
|
|
326
|
-
# pyairtable.Table.batch_create() signature:
|
|
327
|
-
# batch_create(self, records: Iterable[WritableFields], typecast: bool = False, use_field_ids: Optional[bool] = None)
|
|
328
|
-
# It does NOT take a general **kwargs.
|
|
329
|
-
|
|
330
255
|
prepared_options = self._prepare_pyairtable_params(options)
|
|
331
256
|
call_kwargs = {}
|
|
332
257
|
if "typecast" in prepared_options:
|
|
333
258
|
call_kwargs["typecast"] = prepared_options["typecast"]
|
|
334
259
|
if "use_field_ids" in prepared_options:
|
|
335
260
|
call_kwargs["use_field_ids"] = prepared_options["use_field_ids"]
|
|
336
|
-
|
|
337
261
|
return table.batch_create(records, **call_kwargs)
|
|
338
262
|
except Exception as e:
|
|
339
263
|
return f"Error batch creating records in '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
340
264
|
|
|
341
|
-
def batch_update_records(
|
|
342
|
-
self,
|
|
343
|
-
base_id: str,
|
|
344
|
-
table_id_or_name: str,
|
|
345
|
-
records: Iterable[UpdateRecordDict],
|
|
346
|
-
**options: Any, # Captures replace, typecast, use_field_ids etc.
|
|
265
|
+
async def batch_update_records(
|
|
266
|
+
self, base_id: str, table_id_or_name: str, records: Iterable[UpdateRecordDict], **options: Any
|
|
347
267
|
) -> list[RecordDict] | str:
|
|
348
268
|
"""
|
|
349
269
|
Updates multiple records in batches in a specified table.
|
|
@@ -366,11 +286,6 @@ class AirtableApp(APIApplication):
|
|
|
366
286
|
try:
|
|
367
287
|
client = self._get_client()
|
|
368
288
|
table = client.table(base_id, table_id_or_name)
|
|
369
|
-
# pyairtable.Table.batch_update() signature:
|
|
370
|
-
# batch_update(self, records: Iterable[UpdateRecordDict], replace: bool = False,
|
|
371
|
-
# typecast: bool = False, use_field_ids: Optional[bool] = None)
|
|
372
|
-
# It does NOT take a general **kwargs.
|
|
373
|
-
|
|
374
289
|
prepared_options = self._prepare_pyairtable_params(options)
|
|
375
290
|
call_kwargs = {}
|
|
376
291
|
if "replace" in prepared_options:
|
|
@@ -379,12 +294,11 @@ class AirtableApp(APIApplication):
|
|
|
379
294
|
call_kwargs["typecast"] = prepared_options["typecast"]
|
|
380
295
|
if "use_field_ids" in prepared_options:
|
|
381
296
|
call_kwargs["use_field_ids"] = prepared_options["use_field_ids"]
|
|
382
|
-
|
|
383
297
|
return table.batch_update(records, **call_kwargs)
|
|
384
298
|
except Exception as e:
|
|
385
299
|
return f"Error batch updating records in '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
386
300
|
|
|
387
|
-
def batch_delete_records(
|
|
301
|
+
async def batch_delete_records(
|
|
388
302
|
self, base_id: str, table_id_or_name: str, record_ids: Iterable[RecordId]
|
|
389
303
|
) -> list[RecordDeletedDict] | str:
|
|
390
304
|
"""
|
|
@@ -409,15 +323,8 @@ class AirtableApp(APIApplication):
|
|
|
409
323
|
except Exception as e:
|
|
410
324
|
return f"Error batch deleting records from '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|
|
411
325
|
|
|
412
|
-
def batch_upsert_records(
|
|
413
|
-
self,
|
|
414
|
-
base_id: str,
|
|
415
|
-
table_id_or_name: str,
|
|
416
|
-
records: Iterable[
|
|
417
|
-
dict[str, Any]
|
|
418
|
-
], # pyairtable expects Dict, not UpdateRecordDict here
|
|
419
|
-
key_fields: list[str],
|
|
420
|
-
**options: Any, # Captures replace, typecast, use_field_ids etc.
|
|
326
|
+
async def batch_upsert_records(
|
|
327
|
+
self, base_id: str, table_id_or_name: str, records: Iterable[dict[str, Any]], key_fields: list[str], **options: Any
|
|
421
328
|
) -> UpsertResultDict | str:
|
|
422
329
|
"""
|
|
423
330
|
Updates or creates records in batches in a specified table.
|
|
@@ -444,11 +351,6 @@ class AirtableApp(APIApplication):
|
|
|
444
351
|
try:
|
|
445
352
|
client = self._get_client()
|
|
446
353
|
table = client.table(base_id, table_id_or_name)
|
|
447
|
-
# pyairtable.Table.batch_upsert() signature:
|
|
448
|
-
# batch_upsert(self, records: Iterable[Dict[str, Any]], key_fields: List[FieldName],
|
|
449
|
-
# replace: bool = False, typecast: bool = False, use_field_ids: Optional[bool] = None)
|
|
450
|
-
# It does NOT take a general **kwargs.
|
|
451
|
-
|
|
452
354
|
prepared_options = self._prepare_pyairtable_params(options)
|
|
453
355
|
call_kwargs = {}
|
|
454
356
|
if "replace" in prepared_options:
|
|
@@ -457,7 +359,6 @@ class AirtableApp(APIApplication):
|
|
|
457
359
|
call_kwargs["typecast"] = prepared_options["typecast"]
|
|
458
360
|
if "use_field_ids" in prepared_options:
|
|
459
361
|
call_kwargs["use_field_ids"] = prepared_options["use_field_ids"]
|
|
460
|
-
|
|
461
362
|
return table.batch_upsert(records, key_fields=key_fields, **call_kwargs)
|
|
462
363
|
except Exception as e:
|
|
463
364
|
return f"Error batch upserting records in '{table_id_or_name}' in '{base_id}': {type(e).__name__} - {e}"
|