Spaces:
Running
Running
Commit ·
3d1f304
1
Parent(s): 8ec8b4a
feat: complete gmail and sheets api method coverage
Browse files- app/api/v1/gmail.py +93 -0
- app/api/v1/sheets.py +235 -0
- app/models/schemas.py +93 -0
- app/services/gmail_service.py +60 -0
- app/services/sheets_service.py +172 -0
app/api/v1/gmail.py
CHANGED
|
@@ -26,6 +26,7 @@ from app.models.schemas import (
|
|
| 26 |
GmailRefreshResponse,
|
| 27 |
GmailSendResponse,
|
| 28 |
GmailSettingsUpdateRequest,
|
|
|
|
| 29 |
GmailWatchRequest,
|
| 30 |
)
|
| 31 |
from app.services.gmail_service import (
|
|
@@ -1356,6 +1357,98 @@ async def verify_send_as(
|
|
| 1356 |
return _ok(start, creds, {"verification_sent": send_as_email})
|
| 1357 |
|
| 1358 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1359 |
@router.get("/settings/delegates", response_model=GmailGenericResponse,
|
| 1360 |
summary="List delegates")
|
| 1361 |
async def list_delegates(
|
|
|
|
| 26 |
GmailRefreshResponse,
|
| 27 |
GmailSendResponse,
|
| 28 |
GmailSettingsUpdateRequest,
|
| 29 |
+
GmailSmimeInfoRequest,
|
| 30 |
GmailWatchRequest,
|
| 31 |
)
|
| 32 |
from app.services.gmail_service import (
|
|
|
|
| 1357 |
return _ok(start, creds, {"verification_sent": send_as_email})
|
| 1358 |
|
| 1359 |
|
| 1360 |
+
@router.get("/settings/send-as/{send_as_email}/smime-info", response_model=GmailGenericResponse,
|
| 1361 |
+
summary="List S/MIME certificates for a send-as alias (smimeInfo.list)")
|
| 1362 |
+
async def list_smime_info(
|
| 1363 |
+
send_as_email: str,
|
| 1364 |
+
creds: GmailCredentials = Depends(_credentials),
|
| 1365 |
+
service: GmailService = Depends(get_gmail_service),
|
| 1366 |
+
user_id: Optional[str] = None,
|
| 1367 |
+
):
|
| 1368 |
+
start = time.perf_counter()
|
| 1369 |
+
try:
|
| 1370 |
+
data = await service.list_smime_info(creds, send_as_email, user_id=user_id or "me")
|
| 1371 |
+
except GmailAPIError as exc:
|
| 1372 |
+
raise _http_error(exc) from exc
|
| 1373 |
+
_logger.info("Gmail smimeInfo listed for %s (%.2fms)", send_as_email, _elapsed_ms(start))
|
| 1374 |
+
return _ok(start, creds, data)
|
| 1375 |
+
|
| 1376 |
+
|
| 1377 |
+
@router.post("/settings/send-as/{send_as_email}/smime-info", response_model=GmailGenericResponse,
|
| 1378 |
+
summary="Insert an S/MIME certificate for a send-as alias (smimeInfo.insert)")
|
| 1379 |
+
async def insert_smime_info(
|
| 1380 |
+
send_as_email: str,
|
| 1381 |
+
body: GmailSmimeInfoRequest,
|
| 1382 |
+
creds: GmailCredentials = Depends(_credentials),
|
| 1383 |
+
service: GmailService = Depends(get_gmail_service),
|
| 1384 |
+
user_id: Optional[str] = None,
|
| 1385 |
+
):
|
| 1386 |
+
start = time.perf_counter()
|
| 1387 |
+
try:
|
| 1388 |
+
data = await service.insert_smime_info(creds, send_as_email, body.payload, user_id=user_id or "me")
|
| 1389 |
+
except GmailAPIError as exc:
|
| 1390 |
+
raise _http_error(exc) from exc
|
| 1391 |
+
_logger.info("Gmail smimeInfo inserted for %s (%.2fms)", send_as_email, _elapsed_ms(start))
|
| 1392 |
+
return _ok(start, creds, data)
|
| 1393 |
+
|
| 1394 |
+
|
| 1395 |
+
@router.get("/settings/send-as/{send_as_email}/smime-info/{smime_info_id}",
|
| 1396 |
+
response_model=GmailGenericResponse,
|
| 1397 |
+
summary="Get a single S/MIME certificate (smimeInfo.get)")
|
| 1398 |
+
async def get_smime_info(
|
| 1399 |
+
send_as_email: str,
|
| 1400 |
+
smime_info_id: str,
|
| 1401 |
+
creds: GmailCredentials = Depends(_credentials),
|
| 1402 |
+
service: GmailService = Depends(get_gmail_service),
|
| 1403 |
+
user_id: Optional[str] = None,
|
| 1404 |
+
):
|
| 1405 |
+
start = time.perf_counter()
|
| 1406 |
+
try:
|
| 1407 |
+
data = await service.get_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me")
|
| 1408 |
+
except GmailAPIError as exc:
|
| 1409 |
+
raise _http_error(exc) from exc
|
| 1410 |
+
_logger.info("Gmail smimeInfo %s fetched (%.2fms)", smime_info_id, _elapsed_ms(start))
|
| 1411 |
+
return _ok(start, creds, data)
|
| 1412 |
+
|
| 1413 |
+
|
| 1414 |
+
@router.delete("/settings/send-as/{send_as_email}/smime-info/{smime_info_id}",
|
| 1415 |
+
response_model=GmailGenericResponse,
|
| 1416 |
+
summary="Delete an S/MIME certificate (smimeInfo.delete)")
|
| 1417 |
+
async def delete_smime_info(
|
| 1418 |
+
send_as_email: str,
|
| 1419 |
+
smime_info_id: str,
|
| 1420 |
+
creds: GmailCredentials = Depends(_credentials),
|
| 1421 |
+
service: GmailService = Depends(get_gmail_service),
|
| 1422 |
+
user_id: Optional[str] = None,
|
| 1423 |
+
):
|
| 1424 |
+
start = time.perf_counter()
|
| 1425 |
+
try:
|
| 1426 |
+
await service.delete_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me")
|
| 1427 |
+
except GmailAPIError as exc:
|
| 1428 |
+
raise _http_error(exc) from exc
|
| 1429 |
+
_logger.info("Gmail smimeInfo %s deleted (%.2fms)", smime_info_id, _elapsed_ms(start))
|
| 1430 |
+
return _ok(start, creds, {"deleted": smime_info_id})
|
| 1431 |
+
|
| 1432 |
+
|
| 1433 |
+
@router.post("/settings/send-as/{send_as_email}/smime-info/{smime_info_id}/setDefault",
|
| 1434 |
+
response_model=GmailGenericResponse,
|
| 1435 |
+
summary="Set an S/MIME certificate as the default for a send-as alias (smimeInfo.setDefault)")
|
| 1436 |
+
async def set_default_smime_info(
|
| 1437 |
+
send_as_email: str,
|
| 1438 |
+
smime_info_id: str,
|
| 1439 |
+
creds: GmailCredentials = Depends(_credentials),
|
| 1440 |
+
service: GmailService = Depends(get_gmail_service),
|
| 1441 |
+
user_id: Optional[str] = None,
|
| 1442 |
+
):
|
| 1443 |
+
start = time.perf_counter()
|
| 1444 |
+
try:
|
| 1445 |
+
await service.set_default_smime_info(creds, send_as_email, smime_info_id, user_id=user_id or "me")
|
| 1446 |
+
except GmailAPIError as exc:
|
| 1447 |
+
raise _http_error(exc) from exc
|
| 1448 |
+
_logger.info("Gmail smimeInfo %s set default (%.2fms)", smime_info_id, _elapsed_ms(start))
|
| 1449 |
+
return _ok(start, creds, {"default_set": smime_info_id})
|
| 1450 |
+
|
| 1451 |
+
|
| 1452 |
@router.get("/settings/delegates", response_model=GmailGenericResponse,
|
| 1453 |
summary="List delegates")
|
| 1454 |
async def list_delegates(
|
app/api/v1/sheets.py
CHANGED
|
@@ -9,10 +9,18 @@ from app.config import get_settings
|
|
| 9 |
from app.core.logger import get_logger
|
| 10 |
from app.models.schemas import (
|
| 11 |
SheetsAppendRequest,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
SheetsBatchUpdateRequest,
|
|
|
|
|
|
|
| 13 |
SheetsCreateSpreadsheetRequest,
|
| 14 |
SheetsGenericResponse,
|
|
|
|
| 15 |
SheetsRefreshResponse,
|
|
|
|
| 16 |
SheetsValueUpdateRequest,
|
| 17 |
)
|
| 18 |
from app.services.sheets_service import (
|
|
@@ -225,6 +233,57 @@ async def batch_update(
|
|
| 225 |
return _ok(start, creds, data)
|
| 226 |
|
| 227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
# ---------------------------------------------------------------------------
|
| 229 |
# Values
|
| 230 |
# ---------------------------------------------------------------------------
|
|
@@ -362,3 +421,179 @@ async def clear_values(
|
|
| 362 |
raise _http_error(exc) from exc
|
| 363 |
_logger.info("Sheets values %s!%s cleared (%.2fms)", spreadsheet_id, range, _elapsed_ms(start))
|
| 364 |
return _ok(start, creds, data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
from app.core.logger import get_logger
|
| 10 |
from app.models.schemas import (
|
| 11 |
SheetsAppendRequest,
|
| 12 |
+
SheetsBatchClearByDataFilterRequest,
|
| 13 |
+
SheetsBatchClearRequest,
|
| 14 |
+
SheetsBatchGetByDataFilterRequest,
|
| 15 |
+
SheetsBatchUpdateByDataFilterRequest,
|
| 16 |
SheetsBatchUpdateRequest,
|
| 17 |
+
SheetsBatchUpdateValuesRequest,
|
| 18 |
+
SheetsCopySheetRequest,
|
| 19 |
SheetsCreateSpreadsheetRequest,
|
| 20 |
SheetsGenericResponse,
|
| 21 |
+
SheetsGetByDataFilterRequest,
|
| 22 |
SheetsRefreshResponse,
|
| 23 |
+
SheetsSearchDeveloperMetadataRequest,
|
| 24 |
SheetsValueUpdateRequest,
|
| 25 |
)
|
| 26 |
from app.services.sheets_service import (
|
|
|
|
| 233 |
return _ok(start, creds, data)
|
| 234 |
|
| 235 |
|
| 236 |
+
@router.post("/spreadsheets/{spreadsheet_id}:getByDataFilter",
|
| 237 |
+
response_model=SheetsGenericResponse,
|
| 238 |
+
summary="Read a spreadsheet filtered by DataFilter objects (spreadsheets.getByDataFilter)")
|
| 239 |
+
async def get_spreadsheet_by_data_filter(
|
| 240 |
+
spreadsheet_id: str,
|
| 241 |
+
body: SheetsGetByDataFilterRequest,
|
| 242 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 243 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 244 |
+
):
|
| 245 |
+
start = time.perf_counter()
|
| 246 |
+
try:
|
| 247 |
+
data = await service.get_spreadsheet_by_data_filter(
|
| 248 |
+
creds,
|
| 249 |
+
spreadsheet_id,
|
| 250 |
+
data_filters=body.data_filters,
|
| 251 |
+
include_grid_data=body.include_grid_data,
|
| 252 |
+
)
|
| 253 |
+
except SheetsAPIError as exc:
|
| 254 |
+
raise _http_error(exc) from exc
|
| 255 |
+
_logger.info(
|
| 256 |
+
"Sheets spreadsheet %s read by data filter (%.2fms)", spreadsheet_id, _elapsed_ms(start)
|
| 257 |
+
)
|
| 258 |
+
return _ok(start, creds, data)
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
@router.post("/spreadsheets/{spreadsheet_id}/sheets/{sheet_id}:copyTo",
|
| 262 |
+
response_model=SheetsGenericResponse,
|
| 263 |
+
summary="Copy a sheet to another spreadsheet (sheets.copyTo)")
|
| 264 |
+
async def copy_sheet(
|
| 265 |
+
spreadsheet_id: str,
|
| 266 |
+
sheet_id: int,
|
| 267 |
+
body: SheetsCopySheetRequest,
|
| 268 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 269 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 270 |
+
):
|
| 271 |
+
start = time.perf_counter()
|
| 272 |
+
try:
|
| 273 |
+
data = await service.copy_sheet(
|
| 274 |
+
creds,
|
| 275 |
+
spreadsheet_id,
|
| 276 |
+
sheet_id,
|
| 277 |
+
destination_spreadsheet_id=body.destination_spreadsheet_id,
|
| 278 |
+
)
|
| 279 |
+
except SheetsAPIError as exc:
|
| 280 |
+
raise _http_error(exc) from exc
|
| 281 |
+
_logger.info(
|
| 282 |
+
"Sheets sheet %d copied from %s (%.2fms)", sheet_id, spreadsheet_id, _elapsed_ms(start)
|
| 283 |
+
)
|
| 284 |
+
return _ok(start, creds, data)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
# ---------------------------------------------------------------------------
|
| 288 |
# Values
|
| 289 |
# ---------------------------------------------------------------------------
|
|
|
|
| 421 |
raise _http_error(exc) from exc
|
| 422 |
_logger.info("Sheets values %s!%s cleared (%.2fms)", spreadsheet_id, range, _elapsed_ms(start))
|
| 423 |
return _ok(start, creds, data)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
@router.post("/spreadsheets/{spreadsheet_id}/values:batchUpdate",
|
| 427 |
+
response_model=SheetsGenericResponse,
|
| 428 |
+
summary="Write multiple ranges in one call (values.batchUpdate)")
|
| 429 |
+
async def batch_update_values(
|
| 430 |
+
spreadsheet_id: str,
|
| 431 |
+
body: SheetsBatchUpdateValuesRequest,
|
| 432 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 433 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 434 |
+
value_input_option: str = "RAW",
|
| 435 |
+
):
|
| 436 |
+
start = time.perf_counter()
|
| 437 |
+
try:
|
| 438 |
+
data = await service.batch_update_values(
|
| 439 |
+
creds,
|
| 440 |
+
spreadsheet_id,
|
| 441 |
+
data=body.data,
|
| 442 |
+
value_input_option=value_input_option,
|
| 443 |
+
include_values_in_response=body.include_values_in_response,
|
| 444 |
+
value_render_option=body.value_render_option,
|
| 445 |
+
response_value_render_option=body.response_value_render_option,
|
| 446 |
+
response_date_time_render_option=body.response_date_time_render_option,
|
| 447 |
+
)
|
| 448 |
+
except SheetsAPIError as exc:
|
| 449 |
+
raise _http_error(exc) from exc
|
| 450 |
+
_logger.info("Sheets values batch-updated on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start))
|
| 451 |
+
return _ok(start, creds, data)
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
@router.post("/spreadsheets/{spreadsheet_id}/values:batchClear",
|
| 455 |
+
response_model=SheetsGenericResponse,
|
| 456 |
+
summary="Clear multiple ranges in one call (values.batchClear)")
|
| 457 |
+
async def batch_clear_values(
|
| 458 |
+
spreadsheet_id: str,
|
| 459 |
+
body: SheetsBatchClearRequest,
|
| 460 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 461 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 462 |
+
):
|
| 463 |
+
start = time.perf_counter()
|
| 464 |
+
try:
|
| 465 |
+
data = await service.batch_clear_values(
|
| 466 |
+
creds, spreadsheet_id, ranges=body.ranges
|
| 467 |
+
)
|
| 468 |
+
except SheetsAPIError as exc:
|
| 469 |
+
raise _http_error(exc) from exc
|
| 470 |
+
_logger.info("Sheets values batch-cleared on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start))
|
| 471 |
+
return _ok(start, creds, data)
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
@router.post("/spreadsheets/{spreadsheet_id}/values:batchGetByDataFilter",
|
| 475 |
+
response_model=SheetsGenericResponse,
|
| 476 |
+
summary="Read multiple ranges by DataFilter (values.batchGetByDataFilter)")
|
| 477 |
+
async def batch_get_values_by_data_filter(
|
| 478 |
+
spreadsheet_id: str,
|
| 479 |
+
body: SheetsBatchGetByDataFilterRequest,
|
| 480 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 481 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 482 |
+
):
|
| 483 |
+
start = time.perf_counter()
|
| 484 |
+
try:
|
| 485 |
+
data = await service.batch_get_values_by_data_filter(
|
| 486 |
+
creds,
|
| 487 |
+
spreadsheet_id,
|
| 488 |
+
data_filters=body.data_filters,
|
| 489 |
+
major_dimension=body.major_dimension,
|
| 490 |
+
value_render_option=body.value_render_option,
|
| 491 |
+
date_time_render_option=body.date_time_render_option,
|
| 492 |
+
)
|
| 493 |
+
except SheetsAPIError as exc:
|
| 494 |
+
raise _http_error(exc) from exc
|
| 495 |
+
_logger.info(
|
| 496 |
+
"Sheets batchGetByDataFilter on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start)
|
| 497 |
+
)
|
| 498 |
+
return _ok(start, creds, data)
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
@router.post("/spreadsheets/{spreadsheet_id}/values:batchUpdateByDataFilter",
|
| 502 |
+
response_model=SheetsGenericResponse,
|
| 503 |
+
summary="Write multiple DataFilter ranges (values.batchUpdateByDataFilter)")
|
| 504 |
+
async def batch_update_values_by_data_filter(
|
| 505 |
+
spreadsheet_id: str,
|
| 506 |
+
body: SheetsBatchUpdateByDataFilterRequest,
|
| 507 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 508 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 509 |
+
value_input_option: str = "RAW",
|
| 510 |
+
):
|
| 511 |
+
start = time.perf_counter()
|
| 512 |
+
try:
|
| 513 |
+
data = await service.batch_update_values_by_data_filter(
|
| 514 |
+
creds,
|
| 515 |
+
spreadsheet_id,
|
| 516 |
+
data=body.data,
|
| 517 |
+
value_input_option=value_input_option,
|
| 518 |
+
include_values_in_response=body.include_values_in_response,
|
| 519 |
+
value_render_option=body.value_render_option,
|
| 520 |
+
response_value_render_option=body.response_value_render_option,
|
| 521 |
+
response_date_time_render_option=body.response_date_time_render_option,
|
| 522 |
+
)
|
| 523 |
+
except SheetsAPIError as exc:
|
| 524 |
+
raise _http_error(exc) from exc
|
| 525 |
+
_logger.info(
|
| 526 |
+
"Sheets batchUpdateByDataFilter on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start)
|
| 527 |
+
)
|
| 528 |
+
return _ok(start, creds, data)
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
@router.post("/spreadsheets/{spreadsheet_id}/values:batchClearByDataFilter",
|
| 532 |
+
response_model=SheetsGenericResponse,
|
| 533 |
+
summary="Clear multiple DataFilter ranges (values.batchClearByDataFilter)")
|
| 534 |
+
async def batch_clear_values_by_data_filter(
|
| 535 |
+
spreadsheet_id: str,
|
| 536 |
+
body: SheetsBatchClearByDataFilterRequest,
|
| 537 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 538 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 539 |
+
):
|
| 540 |
+
start = time.perf_counter()
|
| 541 |
+
try:
|
| 542 |
+
data = await service.batch_clear_values_by_data_filter(
|
| 543 |
+
creds, spreadsheet_id, data_filters=body.data_filters
|
| 544 |
+
)
|
| 545 |
+
except SheetsAPIError as exc:
|
| 546 |
+
raise _http_error(exc) from exc
|
| 547 |
+
_logger.info(
|
| 548 |
+
"Sheets batchClearByDataFilter on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start)
|
| 549 |
+
)
|
| 550 |
+
return _ok(start, creds, data)
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
# ---------------------------------------------------------------------------
|
| 554 |
+
# Developer metadata
|
| 555 |
+
# ---------------------------------------------------------------------------
|
| 556 |
+
|
| 557 |
+
@router.get("/spreadsheets/{spreadsheet_id}/developerMetadata/{metadata_id}",
|
| 558 |
+
response_model=SheetsGenericResponse,
|
| 559 |
+
summary="Get a single developer metadata entry (developerMetadata.get)")
|
| 560 |
+
async def get_developer_metadata(
|
| 561 |
+
spreadsheet_id: str,
|
| 562 |
+
metadata_id: int,
|
| 563 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 564 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 565 |
+
):
|
| 566 |
+
start = time.perf_counter()
|
| 567 |
+
try:
|
| 568 |
+
data = await service.get_developer_metadata(creds, spreadsheet_id, metadata_id)
|
| 569 |
+
except SheetsAPIError as exc:
|
| 570 |
+
raise _http_error(exc) from exc
|
| 571 |
+
_logger.info(
|
| 572 |
+
"Sheets developer metadata %d fetched from %s (%.2fms)",
|
| 573 |
+
metadata_id,
|
| 574 |
+
spreadsheet_id,
|
| 575 |
+
_elapsed_ms(start),
|
| 576 |
+
)
|
| 577 |
+
return _ok(start, creds, data)
|
| 578 |
+
|
| 579 |
+
|
| 580 |
+
@router.post("/spreadsheets/{spreadsheet_id}/developerMetadata:search",
|
| 581 |
+
response_model=SheetsGenericResponse,
|
| 582 |
+
summary="Search developer metadata (developerMetadata.search)")
|
| 583 |
+
async def search_developer_metadata(
|
| 584 |
+
spreadsheet_id: str,
|
| 585 |
+
body: SheetsSearchDeveloperMetadataRequest,
|
| 586 |
+
creds: SheetsCredentials = Depends(_credentials),
|
| 587 |
+
service: SheetsService = Depends(get_sheets_service),
|
| 588 |
+
):
|
| 589 |
+
start = time.perf_counter()
|
| 590 |
+
try:
|
| 591 |
+
data = await service.search_developer_metadata(
|
| 592 |
+
creds, spreadsheet_id, data_filters=body.data_filters or None
|
| 593 |
+
)
|
| 594 |
+
except SheetsAPIError as exc:
|
| 595 |
+
raise _http_error(exc) from exc
|
| 596 |
+
_logger.info(
|
| 597 |
+
"Sheets developer metadata searched on %s (%.2fms)", spreadsheet_id, _elapsed_ms(start)
|
| 598 |
+
)
|
| 599 |
+
return _ok(start, creds, data)
|
app/models/schemas.py
CHANGED
|
@@ -1964,6 +1964,22 @@ class GmailSendAsRequest(BaseModel):
|
|
| 1964 |
signature: Optional[str] = Field(None, description="Email signature (HTML)")
|
| 1965 |
|
| 1966 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1967 |
class GmailGenericResponse(BaseModel):
|
| 1968 |
"""Standard Gmail operation envelope."""
|
| 1969 |
|
|
@@ -2053,6 +2069,83 @@ class SheetsBatchUpdateRequest(BaseModel):
|
|
| 2053 |
requests: List[Dict[str, Any]] = Field(..., min_length=1, description="List of request objects (addSheet, updateCells, etc.)")
|
| 2054 |
|
| 2055 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2056 |
class SheetsGenericResponse(BaseModel):
|
| 2057 |
"""Standard Google Sheets operation envelope."""
|
| 2058 |
|
|
|
|
| 1964 |
signature: Optional[str] = Field(None, description="Email signature (HTML)")
|
| 1965 |
|
| 1966 |
|
| 1967 |
+
class GmailSmimeInfoRequest(BaseModel):
|
| 1968 |
+
"""Body for inserting an S/MIME certificate (users.settings.sendAs.smimeInfo.insert)."""
|
| 1969 |
+
|
| 1970 |
+
payload: Dict[str, Any] = Field(
|
| 1971 |
+
...,
|
| 1972 |
+
description="SmimeInfo resource: either 'pkcs12' (base64) + 'encryptedKeyPassword', or 'pem'. See Gmail API reference.",
|
| 1973 |
+
)
|
| 1974 |
+
|
| 1975 |
+
@field_validator("payload")
|
| 1976 |
+
@classmethod
|
| 1977 |
+
def _non_empty(cls, v: Dict[str, Any]) -> Dict[str, Any]:
|
| 1978 |
+
if not v:
|
| 1979 |
+
raise ValueError("payload must not be empty.")
|
| 1980 |
+
return v
|
| 1981 |
+
|
| 1982 |
+
|
| 1983 |
class GmailGenericResponse(BaseModel):
|
| 1984 |
"""Standard Gmail operation envelope."""
|
| 1985 |
|
|
|
|
| 2069 |
requests: List[Dict[str, Any]] = Field(..., min_length=1, description="List of request objects (addSheet, updateCells, etc.)")
|
| 2070 |
|
| 2071 |
|
| 2072 |
+
class SheetsGetByDataFilterRequest(BaseModel):
|
| 2073 |
+
"""Body for reading a spreadsheet filtered by DataFilter (spreadsheets.getByDataFilter)."""
|
| 2074 |
+
|
| 2075 |
+
data_filters: List[Dict[str, Any]] = Field(
|
| 2076 |
+
...,
|
| 2077 |
+
min_length=1,
|
| 2078 |
+
description="List of DataFilter objects; each must set exactly one of a1Range, gridRange, namedRangeId, developerMetadataLookup",
|
| 2079 |
+
)
|
| 2080 |
+
include_grid_data: bool = Field(False, description="Whether to include cell grid data in the response")
|
| 2081 |
+
|
| 2082 |
+
|
| 2083 |
+
class SheetsCopySheetRequest(BaseModel):
|
| 2084 |
+
"""Body for copying a sheet to another spreadsheet (sheets.copyTo)."""
|
| 2085 |
+
|
| 2086 |
+
destination_spreadsheet_id: str = Field(
|
| 2087 |
+
..., min_length=1, description="ID of the spreadsheet to copy the sheet into"
|
| 2088 |
+
)
|
| 2089 |
+
|
| 2090 |
+
|
| 2091 |
+
class SheetsBatchGetByDataFilterRequest(BaseModel):
|
| 2092 |
+
"""Body for reading multiple ranges by DataFilter (values.batchGetByDataFilter)."""
|
| 2093 |
+
|
| 2094 |
+
data_filters: List[Dict[str, Any]] = Field(..., min_length=1, description="List of DataFilter objects")
|
| 2095 |
+
major_dimension: Optional[str] = Field(None, pattern="^(ROWS|COLUMNS)$", description="Major dimension of the returned values")
|
| 2096 |
+
value_render_option: Optional[str] = Field(None, pattern="^(FORMATTED_VALUE|UNFORMATTED_VALUE|FORMULA)$", description="Render option for returned values")
|
| 2097 |
+
date_time_render_option: Optional[str] = Field(None, pattern="^(SERIAL_NUMBER|FORMATTED_STRING)$", description="Date/time render option for returned values")
|
| 2098 |
+
|
| 2099 |
+
|
| 2100 |
+
class SheetsBatchUpdateValuesRequest(BaseModel):
|
| 2101 |
+
"""Body for writing multiple ranges in one call (values.batchUpdate)."""
|
| 2102 |
+
|
| 2103 |
+
data: List[Dict[str, Any]] = Field(
|
| 2104 |
+
..., min_length=1, description="List of ValueRange objects, each with 'range' and 'values'"
|
| 2105 |
+
)
|
| 2106 |
+
include_values_in_response: Optional[bool] = Field(None, description="Whether responses should include updated values")
|
| 2107 |
+
value_render_option: Optional[str] = Field(None, pattern="^(FORMATTED_VALUE|UNFORMATTED_VALUE|FORMULA)$", description="Render option for values in the response")
|
| 2108 |
+
response_value_render_option: Optional[str] = Field(None, pattern="^(FORMATTED_VALUE|UNFORMATTED_VALUE|FORMULA)$", description="Render option for response values")
|
| 2109 |
+
response_date_time_render_option: Optional[str] = Field(None, pattern="^(SERIAL_NUMBER|FORMATTED_STRING)$", description="Date/time render option for response values")
|
| 2110 |
+
|
| 2111 |
+
|
| 2112 |
+
class SheetsBatchUpdateByDataFilterRequest(BaseModel):
|
| 2113 |
+
"""Body for writing multiple DataFilter ranges (values.batchUpdateByDataFilter)."""
|
| 2114 |
+
|
| 2115 |
+
data: List[Dict[str, Any]] = Field(
|
| 2116 |
+
...,
|
| 2117 |
+
min_length=1,
|
| 2118 |
+
description="List of DataFilterValueRange objects, each with 'dataFilter', optional 'majorDimension' and 'values'",
|
| 2119 |
+
)
|
| 2120 |
+
include_values_in_response: Optional[bool] = Field(None, description="Whether responses should include updated values")
|
| 2121 |
+
value_render_option: Optional[str] = Field(None, pattern="^(FORMATTED_VALUE|UNFORMATTED_VALUE|FORMULA)$", description="Render option for values in the response")
|
| 2122 |
+
response_value_render_option: Optional[str] = Field(None, pattern="^(FORMATTED_VALUE|UNFORMATTED_VALUE|FORMULA)$", description="Render option for response values")
|
| 2123 |
+
response_date_time_render_option: Optional[str] = Field(None, pattern="^(SERIAL_NUMBER|FORMATTED_STRING)$", description="Date/time render option for response values")
|
| 2124 |
+
|
| 2125 |
+
|
| 2126 |
+
class SheetsBatchClearRequest(BaseModel):
|
| 2127 |
+
"""Body for clearing multiple ranges in one call (values.batchClear)."""
|
| 2128 |
+
|
| 2129 |
+
ranges: List[str] = Field(
|
| 2130 |
+
..., min_length=1, description="A1 notation ranges to clear, e.g. ['Sheet1!A1:B2']"
|
| 2131 |
+
)
|
| 2132 |
+
|
| 2133 |
+
|
| 2134 |
+
class SheetsBatchClearByDataFilterRequest(BaseModel):
|
| 2135 |
+
"""Body for clearing multiple DataFilter ranges (values.batchClearByDataFilter)."""
|
| 2136 |
+
|
| 2137 |
+
data_filters: List[Dict[str, Any]] = Field(..., min_length=1, description="List of DataFilter objects")
|
| 2138 |
+
|
| 2139 |
+
|
| 2140 |
+
class SheetsSearchDeveloperMetadataRequest(BaseModel):
|
| 2141 |
+
"""Body for searching developer metadata (developerMetadata.search)."""
|
| 2142 |
+
|
| 2143 |
+
data_filters: List[Dict[str, Any]] = Field(
|
| 2144 |
+
default_factory=list,
|
| 2145 |
+
description="Optional list of DataFilter objects, each with developerMetadataLookup; empty searches all metadata",
|
| 2146 |
+
)
|
| 2147 |
+
|
| 2148 |
+
|
| 2149 |
class SheetsGenericResponse(BaseModel):
|
| 2150 |
"""Standard Google Sheets operation envelope."""
|
| 2151 |
|
app/services/gmail_service.py
CHANGED
|
@@ -1024,3 +1024,63 @@ class GmailService:
|
|
| 1024 |
await self._request(
|
| 1025 |
creds, "DELETE", f"/users/{user_id}/settings/delegates/{delegate_email}"
|
| 1026 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1024 |
await self._request(
|
| 1025 |
creds, "DELETE", f"/users/{user_id}/settings/delegates/{delegate_email}"
|
| 1026 |
)
|
| 1027 |
+
|
| 1028 |
+
async def list_smime_info(
|
| 1029 |
+
self, creds: GmailCredentials, send_as_email: str, user_id: str = "me"
|
| 1030 |
+
) -> Dict[str, Any]:
|
| 1031 |
+
return await self._request(
|
| 1032 |
+
creds, "GET", f"/users/{user_id}/settings/sendAs/{send_as_email}/smimeInfo"
|
| 1033 |
+
)
|
| 1034 |
+
|
| 1035 |
+
async def get_smime_info(
|
| 1036 |
+
self,
|
| 1037 |
+
creds: GmailCredentials,
|
| 1038 |
+
send_as_email: str,
|
| 1039 |
+
smime_info_id: str,
|
| 1040 |
+
user_id: str = "me",
|
| 1041 |
+
) -> Dict[str, Any]:
|
| 1042 |
+
return await self._request(
|
| 1043 |
+
creds,
|
| 1044 |
+
"GET",
|
| 1045 |
+
f"/users/{user_id}/settings/sendAs/{send_as_email}/smimeInfo/{smime_info_id}",
|
| 1046 |
+
)
|
| 1047 |
+
|
| 1048 |
+
async def insert_smime_info(
|
| 1049 |
+
self,
|
| 1050 |
+
creds: GmailCredentials,
|
| 1051 |
+
send_as_email: str,
|
| 1052 |
+
payload: Dict[str, Any],
|
| 1053 |
+
user_id: str = "me",
|
| 1054 |
+
) -> Dict[str, Any]:
|
| 1055 |
+
return await self._request(
|
| 1056 |
+
creds,
|
| 1057 |
+
"POST",
|
| 1058 |
+
f"/users/{user_id}/settings/sendAs/{send_as_email}/smimeInfo",
|
| 1059 |
+
json_body=payload,
|
| 1060 |
+
)
|
| 1061 |
+
|
| 1062 |
+
async def delete_smime_info(
|
| 1063 |
+
self,
|
| 1064 |
+
creds: GmailCredentials,
|
| 1065 |
+
send_as_email: str,
|
| 1066 |
+
smime_info_id: str,
|
| 1067 |
+
user_id: str = "me",
|
| 1068 |
+
) -> None:
|
| 1069 |
+
await self._request(
|
| 1070 |
+
creds,
|
| 1071 |
+
"DELETE",
|
| 1072 |
+
f"/users/{user_id}/settings/sendAs/{send_as_email}/smimeInfo/{smime_info_id}",
|
| 1073 |
+
)
|
| 1074 |
+
|
| 1075 |
+
async def set_default_smime_info(
|
| 1076 |
+
self,
|
| 1077 |
+
creds: GmailCredentials,
|
| 1078 |
+
send_as_email: str,
|
| 1079 |
+
smime_info_id: str,
|
| 1080 |
+
user_id: str = "me",
|
| 1081 |
+
) -> None:
|
| 1082 |
+
await self._request(
|
| 1083 |
+
creds,
|
| 1084 |
+
"POST",
|
| 1085 |
+
f"/users/{user_id}/settings/sendAs/{send_as_email}/smimeInfo/{smime_info_id}/setDefault",
|
| 1086 |
+
)
|
app/services/sheets_service.py
CHANGED
|
@@ -561,3 +561,175 @@ class SheetsService:
|
|
| 561 |
f"/spreadsheets/{spreadsheet_id}/values/{range_}:clear",
|
| 562 |
json_body={},
|
| 563 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
f"/spreadsheets/{spreadsheet_id}/values/{range_}:clear",
|
| 562 |
json_body={},
|
| 563 |
)
|
| 564 |
+
|
| 565 |
+
async def batch_update_values(
|
| 566 |
+
self,
|
| 567 |
+
creds: SheetsCredentials,
|
| 568 |
+
spreadsheet_id: str,
|
| 569 |
+
*,
|
| 570 |
+
data: List[Dict[str, Any]],
|
| 571 |
+
value_input_option: str = "RAW",
|
| 572 |
+
include_values_in_response: Optional[bool] = None,
|
| 573 |
+
value_render_option: Optional[str] = None,
|
| 574 |
+
response_value_render_option: Optional[str] = None,
|
| 575 |
+
response_date_time_render_option: Optional[str] = None,
|
| 576 |
+
) -> Dict[str, Any]:
|
| 577 |
+
body: Dict[str, Any] = {"data": data}
|
| 578 |
+
if include_values_in_response is not None:
|
| 579 |
+
body["includeValuesInResponse"] = include_values_in_response
|
| 580 |
+
if value_render_option:
|
| 581 |
+
body["valueRenderOption"] = value_render_option
|
| 582 |
+
if response_value_render_option:
|
| 583 |
+
body["responseValueRenderOption"] = response_value_render_option
|
| 584 |
+
if response_date_time_render_option:
|
| 585 |
+
body["responseDateTimeRenderOption"] = response_date_time_render_option
|
| 586 |
+
return await self._request(
|
| 587 |
+
creds,
|
| 588 |
+
"POST",
|
| 589 |
+
f"/spreadsheets/{spreadsheet_id}/values:batchUpdate",
|
| 590 |
+
params={"valueInputOption": value_input_option},
|
| 591 |
+
json_body=body,
|
| 592 |
+
)
|
| 593 |
+
|
| 594 |
+
async def batch_clear_values(
|
| 595 |
+
self,
|
| 596 |
+
creds: SheetsCredentials,
|
| 597 |
+
spreadsheet_id: str,
|
| 598 |
+
*,
|
| 599 |
+
ranges: List[str],
|
| 600 |
+
) -> Dict[str, Any]:
|
| 601 |
+
return await self._request(
|
| 602 |
+
creds,
|
| 603 |
+
"POST",
|
| 604 |
+
f"/spreadsheets/{spreadsheet_id}/values:batchClear",
|
| 605 |
+
json_body={"ranges": ranges},
|
| 606 |
+
)
|
| 607 |
+
|
| 608 |
+
async def get_spreadsheet_by_data_filter(
|
| 609 |
+
self,
|
| 610 |
+
creds: SheetsCredentials,
|
| 611 |
+
spreadsheet_id: str,
|
| 612 |
+
*,
|
| 613 |
+
data_filters: List[Dict[str, Any]],
|
| 614 |
+
include_grid_data: bool = False,
|
| 615 |
+
) -> Dict[str, Any]:
|
| 616 |
+
body: Dict[str, Any] = {"dataFilters": data_filters}
|
| 617 |
+
if include_grid_data:
|
| 618 |
+
body["includeGridData"] = True
|
| 619 |
+
return await self._request(
|
| 620 |
+
creds,
|
| 621 |
+
"POST",
|
| 622 |
+
f"/spreadsheets/{spreadsheet_id}:getByDataFilter",
|
| 623 |
+
json_body=body,
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
async def copy_sheet(
|
| 627 |
+
self,
|
| 628 |
+
creds: SheetsCredentials,
|
| 629 |
+
spreadsheet_id: str,
|
| 630 |
+
sheet_id: int,
|
| 631 |
+
*,
|
| 632 |
+
destination_spreadsheet_id: str,
|
| 633 |
+
) -> Dict[str, Any]:
|
| 634 |
+
return await self._request(
|
| 635 |
+
creds,
|
| 636 |
+
"POST",
|
| 637 |
+
f"/spreadsheets/{spreadsheet_id}/sheets/{sheet_id}:copyTo",
|
| 638 |
+
json_body={"destinationSpreadsheetId": destination_spreadsheet_id},
|
| 639 |
+
)
|
| 640 |
+
|
| 641 |
+
async def batch_get_values_by_data_filter(
|
| 642 |
+
self,
|
| 643 |
+
creds: SheetsCredentials,
|
| 644 |
+
spreadsheet_id: str,
|
| 645 |
+
*,
|
| 646 |
+
data_filters: List[Dict[str, Any]],
|
| 647 |
+
major_dimension: Optional[str] = None,
|
| 648 |
+
value_render_option: Optional[str] = None,
|
| 649 |
+
date_time_render_option: Optional[str] = None,
|
| 650 |
+
) -> Dict[str, Any]:
|
| 651 |
+
body: Dict[str, Any] = {"dataFilters": data_filters}
|
| 652 |
+
if major_dimension:
|
| 653 |
+
body["majorDimension"] = major_dimension
|
| 654 |
+
if value_render_option:
|
| 655 |
+
body["valueRenderOption"] = value_render_option
|
| 656 |
+
if date_time_render_option:
|
| 657 |
+
body["dateTimeRenderOption"] = date_time_render_option
|
| 658 |
+
return await self._request(
|
| 659 |
+
creds,
|
| 660 |
+
"POST",
|
| 661 |
+
f"/spreadsheets/{spreadsheet_id}/values:batchGetByDataFilter",
|
| 662 |
+
json_body=body,
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
async def batch_update_values_by_data_filter(
|
| 666 |
+
self,
|
| 667 |
+
creds: SheetsCredentials,
|
| 668 |
+
spreadsheet_id: str,
|
| 669 |
+
*,
|
| 670 |
+
data: List[Dict[str, Any]],
|
| 671 |
+
value_input_option: str = "RAW",
|
| 672 |
+
include_values_in_response: Optional[bool] = None,
|
| 673 |
+
value_render_option: Optional[str] = None,
|
| 674 |
+
response_value_render_option: Optional[str] = None,
|
| 675 |
+
response_date_time_render_option: Optional[str] = None,
|
| 676 |
+
) -> Dict[str, Any]:
|
| 677 |
+
body: Dict[str, Any] = {"data": data}
|
| 678 |
+
if include_values_in_response is not None:
|
| 679 |
+
body["includeValuesInResponse"] = include_values_in_response
|
| 680 |
+
if value_render_option:
|
| 681 |
+
body["valueRenderOption"] = value_render_option
|
| 682 |
+
if response_value_render_option:
|
| 683 |
+
body["responseValueRenderOption"] = response_value_render_option
|
| 684 |
+
if response_date_time_render_option:
|
| 685 |
+
body["responseDateTimeRenderOption"] = response_date_time_render_option
|
| 686 |
+
return await self._request(
|
| 687 |
+
creds,
|
| 688 |
+
"POST",
|
| 689 |
+
f"/spreadsheets/{spreadsheet_id}/values:batchUpdateByDataFilter",
|
| 690 |
+
params={"valueInputOption": value_input_option},
|
| 691 |
+
json_body=body,
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
async def batch_clear_values_by_data_filter(
|
| 695 |
+
self,
|
| 696 |
+
creds: SheetsCredentials,
|
| 697 |
+
spreadsheet_id: str,
|
| 698 |
+
*,
|
| 699 |
+
data_filters: List[Dict[str, Any]],
|
| 700 |
+
) -> Dict[str, Any]:
|
| 701 |
+
return await self._request(
|
| 702 |
+
creds,
|
| 703 |
+
"POST",
|
| 704 |
+
f"/spreadsheets/{spreadsheet_id}/values:batchClearByDataFilter",
|
| 705 |
+
json_body={"dataFilters": data_filters},
|
| 706 |
+
)
|
| 707 |
+
|
| 708 |
+
async def get_developer_metadata(
|
| 709 |
+
self,
|
| 710 |
+
creds: SheetsCredentials,
|
| 711 |
+
spreadsheet_id: str,
|
| 712 |
+
metadata_id: int,
|
| 713 |
+
) -> Dict[str, Any]:
|
| 714 |
+
return await self._request(
|
| 715 |
+
creds,
|
| 716 |
+
"GET",
|
| 717 |
+
f"/spreadsheets/{spreadsheet_id}/developerMetadata/{metadata_id}",
|
| 718 |
+
)
|
| 719 |
+
|
| 720 |
+
async def search_developer_metadata(
|
| 721 |
+
self,
|
| 722 |
+
creds: SheetsCredentials,
|
| 723 |
+
spreadsheet_id: str,
|
| 724 |
+
*,
|
| 725 |
+
data_filters: Optional[List[Dict[str, Any]]] = None,
|
| 726 |
+
) -> Dict[str, Any]:
|
| 727 |
+
body: Dict[str, Any] = {}
|
| 728 |
+
if data_filters:
|
| 729 |
+
body["dataFilters"] = data_filters
|
| 730 |
+
return await self._request(
|
| 731 |
+
creds,
|
| 732 |
+
"POST",
|
| 733 |
+
f"/spreadsheets/{spreadsheet_id}/developerMetadata:search",
|
| 734 |
+
json_body=body,
|
| 735 |
+
)
|