|
15 | 15 | # |
16 | 16 | """ |
17 | 17 | ############################################################################ |
18 | | -NAME: |
19 | | -LabKey Query API |
| 18 | +NAME: |
| 19 | +LabKey Query API |
20 | 20 |
|
21 | | -SUMMARY: |
| 21 | +SUMMARY: |
22 | 22 | This module provides functions for interacting with data on a LabKey Server. |
23 | 23 |
|
24 | 24 | DESCRIPTION: |
25 | | -This module is designed to simplify querying and manipulating data in LabKey Server. |
26 | | -Its APIs are modeled after the LabKey Server JavaScript APIs of the same names. |
| 25 | +This module is designed to simplify querying and manipulating data in LabKey Server. |
| 26 | +Its APIs are modeled after the LabKey Server JavaScript APIs of the same names. |
27 | 27 |
|
28 | 28 | Installation and Setup for the LabKey Python API: |
29 | 29 | https://github.com/LabKey/labkey-api-python/blob/master/README.md |
|
41 | 41 | ############################################################################ |
42 | 42 | """ |
43 | 43 | import functools |
44 | | -from typing import List, TextIO |
| 44 | +from typing import List, Literal, NotRequired, TextIO, TypedDict |
45 | 45 |
|
46 | 46 | from .server_context import ServerContext |
47 | 47 | from .utils import waf_encode |
@@ -196,7 +196,7 @@ def delete_rows( |
196 | 196 | :param transacted: whether all of the updates should be done in a single transaction |
197 | 197 | :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior |
198 | 198 | :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records |
199 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 199 | + :param timeout: timeout of request in seconds (defaults to 300s) |
200 | 200 | :return: |
201 | 201 | """ |
202 | 202 | url = server_context.build_url("query", "deleteRows.api", container_path=container_path) |
@@ -232,7 +232,7 @@ def truncate_table( |
232 | 232 | :param schema_name: schema of table |
233 | 233 | :param query_name: table name to delete from |
234 | 234 | :param container_path: labkey container path if not already set in context |
235 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 235 | + :param timeout: timeout of request in seconds (defaults to 300s) |
236 | 236 | :return: |
237 | 237 | """ |
238 | 238 | url = server_context.build_url("query", "truncateTable.api", container_path=container_path) |
@@ -275,7 +275,7 @@ def execute_sql( |
275 | 275 | :param save_in_session: save query result as a named view to the session |
276 | 276 | :param parameters: parameter values to pass through to a parameterized query |
277 | 277 | :param required_version: Api version of response |
278 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 278 | + :param timeout: timeout of request in seconds (defaults to 300s) |
279 | 279 | :param waf_encode_sql: WAF encode sql in request (defaults to True) |
280 | 280 | :return: |
281 | 281 | """ |
@@ -331,7 +331,7 @@ def insert_rows( |
331 | 331 | :param transacted: whether all of the updates should be done in a single transaction |
332 | 332 | :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior |
333 | 333 | :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records |
334 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 334 | + :param timeout: timeout of request in seconds (defaults to 300s) |
335 | 335 | :return: |
336 | 336 | """ |
337 | 337 | url = server_context.build_url("query", "insertRows.api", container_path=container_path) |
@@ -407,6 +407,93 @@ def import_rows( |
407 | 407 | return server_context.make_request(url, payload, method="POST", file_payload=file_payload) |
408 | 408 |
|
409 | 409 |
|
| 410 | +class Command(TypedDict): |
| 411 | + """ |
| 412 | + TypedDict representing a command for saveRows API. |
| 413 | + """ |
| 414 | + |
| 415 | + audit_behavior: NotRequired[AuditBehavior] |
| 416 | + audit_user_comment: NotRequired[str] |
| 417 | + command: Literal["insert", "update", "delete"] |
| 418 | + container_path: NotRequired[str] |
| 419 | + extra_context: NotRequired[dict] |
| 420 | + query_name: str |
| 421 | + rows: List[any] |
| 422 | + schema_name: str |
| 423 | + skip_reselect_rows: NotRequired[bool] |
| 424 | + |
| 425 | + |
| 426 | +def save_rows( |
| 427 | + server_context: ServerContext, |
| 428 | + commands: List[Command], |
| 429 | + api_version: float = None, |
| 430 | + container_path: str = None, |
| 431 | + extra_context: dict = None, |
| 432 | + timeout: int = _default_timeout, |
| 433 | + transacted: bool = None, |
| 434 | + validate_only: bool = None, |
| 435 | +): |
| 436 | + """ |
| 437 | + Save inserts, updates, and/or deletes to potentially multiple tables with a single request. |
| 438 | + :param server_context: A LabKey server context. See utils.create_server_context. |
| 439 | + :param commands: A List of the update/insert/delete operations to be performed. |
| 440 | + :param api_version: decimal value that indicates the response version of the api. If this is 13.2 or higher, a |
| 441 | + request that fails validation will be returned as a successful response. Use the 'errorCount' and 'committed' |
| 442 | + properties in the response to tell if it committed or not. |
| 443 | + :param container_path: folder path if not already part of server_context |
| 444 | + :param extra_context: Extra context object passed into the transformation/validation script environment. |
| 445 | + :param timeout: Request timeout in seconds (defaults to 300s) |
| 446 | + :param transacted: Whether all the commands should be done in a single transaction, so they all succeed or all |
| 447 | + fail. Defaults to true. |
| 448 | + :param validate_only: Whether the server should attempt to proceed through all the commands but not commit them to |
| 449 | + the database. Useful for scenarios like giving incremental validation feedback as a user fills out a UI form but |
| 450 | + does not save anything until they explicitly request a save. |
| 451 | + """ |
| 452 | + url = server_context.build_url("query", "saveRows.api", container_path=container_path) |
| 453 | + |
| 454 | + json_commands = [] |
| 455 | + for command in commands: |
| 456 | + json_command = { |
| 457 | + "command": command["command"], |
| 458 | + "queryName": command["query_name"], |
| 459 | + "schemaName": command["schema_name"], |
| 460 | + "rows": command["rows"], |
| 461 | + } |
| 462 | + |
| 463 | + if command.get("audit_behavior") is not None: |
| 464 | + json_command["auditBehavior"] = command["audit_behavior"] |
| 465 | + |
| 466 | + if command.get("audit_user_comment") is not None: |
| 467 | + json_command["auditUserComment"] = command["audit_user_comment"] |
| 468 | + |
| 469 | + if command.get("container_path") is not None: |
| 470 | + json_command["containerPath"] = command["container_path"] |
| 471 | + |
| 472 | + if command.get("extra_context") is not None: |
| 473 | + json_command["extraContext"] = command["extra_context"] |
| 474 | + |
| 475 | + if command.get("skip_reselect_rows") is not None: |
| 476 | + json_command["skipReselectRows"] = command["skip_reselect_rows"] |
| 477 | + |
| 478 | + json_commands.append(json_command) |
| 479 | + |
| 480 | + payload = {"commands": json_commands} |
| 481 | + |
| 482 | + if api_version is not None: |
| 483 | + payload["apiVersion"] = api_version |
| 484 | + |
| 485 | + if extra_context is not None: |
| 486 | + payload["extraContext"] = extra_context |
| 487 | + |
| 488 | + if transacted is not None: |
| 489 | + payload["transacted"] = transacted |
| 490 | + |
| 491 | + if validate_only is not None: |
| 492 | + payload["validateOnly"] = validate_only |
| 493 | + |
| 494 | + return server_context.make_request(url, json=payload, timeout=timeout) |
| 495 | + |
| 496 | + |
410 | 497 | def select_rows( |
411 | 498 | server_context: ServerContext, |
412 | 499 | schema_name: str, |
@@ -450,7 +537,7 @@ def select_rows( |
450 | 537 | :param include_update_column: Boolean value that indicates whether to include an Update link column in results |
451 | 538 | :param selection_key: |
452 | 539 | :param required_version: decimal value that indicates the response version of the api |
453 | | - :param timeout: Request timeout in seconds (defaults to 30s) |
| 540 | + :param timeout: Request timeout in seconds (defaults to 300s) |
454 | 541 | :param ignore_filter: Boolean, if true, the command will ignore any filter that may be part of the chosen view. |
455 | 542 | :return: |
456 | 543 | """ |
@@ -534,7 +621,7 @@ def update_rows( |
534 | 621 | :param transacted: whether all of the updates should be done in a single transaction |
535 | 622 | :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior |
536 | 623 | :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records |
537 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 624 | + :param timeout: timeout of request in seconds (defaults to 300s) |
538 | 625 | :return: |
539 | 626 | """ |
540 | 627 | url = server_context.build_url("query", "updateRows.api", container_path=container_path) |
@@ -580,7 +667,7 @@ def move_rows( |
580 | 667 | :param transacted: whether all of the updates should be done in a single transaction |
581 | 668 | :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior |
582 | 669 | :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records |
583 | | - :param timeout: timeout of request in seconds (defaults to 30s) |
| 670 | + :param timeout: timeout of request in seconds (defaults to 300s) |
584 | 671 | :return: |
585 | 672 | """ |
586 | 673 | url = server_context.build_url("query", "moveRows.api", container_path=container_path) |
@@ -726,6 +813,28 @@ def import_rows( |
726 | 813 | import_lookup_by_alternate_key, |
727 | 814 | ) |
728 | 815 |
|
| 816 | + @functools.wraps(save_rows) |
| 817 | + def save_rows( |
| 818 | + self, |
| 819 | + commands: List[Command], |
| 820 | + api_version: float = None, |
| 821 | + container_path: str = None, |
| 822 | + extra_context: dict = None, |
| 823 | + timeout: int = _default_timeout, |
| 824 | + transacted: bool = None, |
| 825 | + validate_only: bool = None, |
| 826 | + ): |
| 827 | + return save_rows( |
| 828 | + self.server_context, |
| 829 | + commands, |
| 830 | + api_version, |
| 831 | + container_path, |
| 832 | + extra_context, |
| 833 | + timeout, |
| 834 | + transacted, |
| 835 | + validate_only, |
| 836 | + ) |
| 837 | + |
729 | 838 | @functools.wraps(select_rows) |
730 | 839 | def select_rows( |
731 | 840 | self, |
|
0 commit comments