databricks-sdk 0.62.0__py3-none-any.whl → 0.64.0__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.

Potentially problematic release.


This version of databricks-sdk might be problematic. Click here for more details.

@@ -32,8 +32,6 @@ class App:
32
32
  app_status: Optional[ApplicationStatus] = None
33
33
 
34
34
  budget_policy_id: Optional[str] = None
35
- """TODO: Deprecate this field after serverless entitlements are released to all prod stages and the
36
- new usage_policy_id is properly populated and used."""
37
35
 
38
36
  compute_status: Optional[ComputeStatus] = None
39
37
 
@@ -51,8 +49,6 @@ class App:
51
49
  """The description of the app."""
52
50
 
53
51
  effective_budget_policy_id: Optional[str] = None
54
- """TODO: Deprecate this field after serverless entitlements are released to all prod stages and the
55
- new usage_policy_id is properly populated and used."""
56
52
 
57
53
  effective_user_api_scopes: Optional[List[str]] = None
58
54
  """The effective api scopes granted to the user access token."""
@@ -487,6 +483,312 @@ class AppDeploymentStatus:
487
483
  return cls(message=d.get("message", None), state=_enum(d, "state", AppDeploymentState))
488
484
 
489
485
 
486
+ @dataclass
487
+ class AppManifest:
488
+ """App manifest definition"""
489
+
490
+ version: int
491
+ """The manifest schema version, for now only 1 is allowed"""
492
+
493
+ name: str
494
+ """Name of the app defined by manifest author / publisher"""
495
+
496
+ description: Optional[str] = None
497
+ """Description of the app defined by manifest author / publisher"""
498
+
499
+ resource_specs: Optional[List[AppManifestAppResourceSpec]] = None
500
+
501
+ def as_dict(self) -> dict:
502
+ """Serializes the AppManifest into a dictionary suitable for use as a JSON request body."""
503
+ body = {}
504
+ if self.description is not None:
505
+ body["description"] = self.description
506
+ if self.name is not None:
507
+ body["name"] = self.name
508
+ if self.resource_specs:
509
+ body["resource_specs"] = [v.as_dict() for v in self.resource_specs]
510
+ if self.version is not None:
511
+ body["version"] = self.version
512
+ return body
513
+
514
+ def as_shallow_dict(self) -> dict:
515
+ """Serializes the AppManifest into a shallow dictionary of its immediate attributes."""
516
+ body = {}
517
+ if self.description is not None:
518
+ body["description"] = self.description
519
+ if self.name is not None:
520
+ body["name"] = self.name
521
+ if self.resource_specs:
522
+ body["resource_specs"] = self.resource_specs
523
+ if self.version is not None:
524
+ body["version"] = self.version
525
+ return body
526
+
527
+ @classmethod
528
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifest:
529
+ """Deserializes the AppManifest from a dictionary."""
530
+ return cls(
531
+ description=d.get("description", None),
532
+ name=d.get("name", None),
533
+ resource_specs=_repeated_dict(d, "resource_specs", AppManifestAppResourceSpec),
534
+ version=d.get("version", None),
535
+ )
536
+
537
+
538
+ @dataclass
539
+ class AppManifestAppResourceJobSpec:
540
+ permission: AppManifestAppResourceJobSpecJobPermission
541
+ """Permissions to grant on the Job. Supported permissions are: "CAN_MANAGE", "IS_OWNER",
542
+ "CAN_MANAGE_RUN", "CAN_VIEW"."""
543
+
544
+ def as_dict(self) -> dict:
545
+ """Serializes the AppManifestAppResourceJobSpec into a dictionary suitable for use as a JSON request body."""
546
+ body = {}
547
+ if self.permission is not None:
548
+ body["permission"] = self.permission.value
549
+ return body
550
+
551
+ def as_shallow_dict(self) -> dict:
552
+ """Serializes the AppManifestAppResourceJobSpec into a shallow dictionary of its immediate attributes."""
553
+ body = {}
554
+ if self.permission is not None:
555
+ body["permission"] = self.permission
556
+ return body
557
+
558
+ @classmethod
559
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceJobSpec:
560
+ """Deserializes the AppManifestAppResourceJobSpec from a dictionary."""
561
+ return cls(permission=_enum(d, "permission", AppManifestAppResourceJobSpecJobPermission))
562
+
563
+
564
+ class AppManifestAppResourceJobSpecJobPermission(Enum):
565
+
566
+ CAN_MANAGE = "CAN_MANAGE"
567
+ CAN_MANAGE_RUN = "CAN_MANAGE_RUN"
568
+ CAN_VIEW = "CAN_VIEW"
569
+ IS_OWNER = "IS_OWNER"
570
+
571
+
572
+ @dataclass
573
+ class AppManifestAppResourceSecretSpec:
574
+ permission: AppManifestAppResourceSecretSpecSecretPermission
575
+ """Permission to grant on the secret scope. For secrets, only one permission is allowed. Permission
576
+ must be one of: "READ", "WRITE", "MANAGE"."""
577
+
578
+ def as_dict(self) -> dict:
579
+ """Serializes the AppManifestAppResourceSecretSpec into a dictionary suitable for use as a JSON request body."""
580
+ body = {}
581
+ if self.permission is not None:
582
+ body["permission"] = self.permission.value
583
+ return body
584
+
585
+ def as_shallow_dict(self) -> dict:
586
+ """Serializes the AppManifestAppResourceSecretSpec into a shallow dictionary of its immediate attributes."""
587
+ body = {}
588
+ if self.permission is not None:
589
+ body["permission"] = self.permission
590
+ return body
591
+
592
+ @classmethod
593
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSecretSpec:
594
+ """Deserializes the AppManifestAppResourceSecretSpec from a dictionary."""
595
+ return cls(permission=_enum(d, "permission", AppManifestAppResourceSecretSpecSecretPermission))
596
+
597
+
598
+ class AppManifestAppResourceSecretSpecSecretPermission(Enum):
599
+ """Permission to grant on the secret scope. Supported permissions are: "READ", "WRITE", "MANAGE"."""
600
+
601
+ MANAGE = "MANAGE"
602
+ READ = "READ"
603
+ WRITE = "WRITE"
604
+
605
+
606
+ @dataclass
607
+ class AppManifestAppResourceServingEndpointSpec:
608
+ permission: AppManifestAppResourceServingEndpointSpecServingEndpointPermission
609
+ """Permission to grant on the serving endpoint. Supported permissions are: "CAN_MANAGE",
610
+ "CAN_QUERY", "CAN_VIEW"."""
611
+
612
+ def as_dict(self) -> dict:
613
+ """Serializes the AppManifestAppResourceServingEndpointSpec into a dictionary suitable for use as a JSON request body."""
614
+ body = {}
615
+ if self.permission is not None:
616
+ body["permission"] = self.permission.value
617
+ return body
618
+
619
+ def as_shallow_dict(self) -> dict:
620
+ """Serializes the AppManifestAppResourceServingEndpointSpec into a shallow dictionary of its immediate attributes."""
621
+ body = {}
622
+ if self.permission is not None:
623
+ body["permission"] = self.permission
624
+ return body
625
+
626
+ @classmethod
627
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceServingEndpointSpec:
628
+ """Deserializes the AppManifestAppResourceServingEndpointSpec from a dictionary."""
629
+ return cls(
630
+ permission=_enum(d, "permission", AppManifestAppResourceServingEndpointSpecServingEndpointPermission)
631
+ )
632
+
633
+
634
+ class AppManifestAppResourceServingEndpointSpecServingEndpointPermission(Enum):
635
+
636
+ CAN_MANAGE = "CAN_MANAGE"
637
+ CAN_QUERY = "CAN_QUERY"
638
+ CAN_VIEW = "CAN_VIEW"
639
+
640
+
641
+ @dataclass
642
+ class AppManifestAppResourceSpec:
643
+ """AppResource related fields are copied from app.proto but excludes resource identifiers (e.g.
644
+ name, id, key, scope, etc.)"""
645
+
646
+ name: str
647
+ """Name of the App Resource."""
648
+
649
+ description: Optional[str] = None
650
+ """Description of the App Resource."""
651
+
652
+ job_spec: Optional[AppManifestAppResourceJobSpec] = None
653
+
654
+ secret_spec: Optional[AppManifestAppResourceSecretSpec] = None
655
+
656
+ serving_endpoint_spec: Optional[AppManifestAppResourceServingEndpointSpec] = None
657
+
658
+ sql_warehouse_spec: Optional[AppManifestAppResourceSqlWarehouseSpec] = None
659
+
660
+ uc_securable_spec: Optional[AppManifestAppResourceUcSecurableSpec] = None
661
+
662
+ def as_dict(self) -> dict:
663
+ """Serializes the AppManifestAppResourceSpec into a dictionary suitable for use as a JSON request body."""
664
+ body = {}
665
+ if self.description is not None:
666
+ body["description"] = self.description
667
+ if self.job_spec:
668
+ body["job_spec"] = self.job_spec.as_dict()
669
+ if self.name is not None:
670
+ body["name"] = self.name
671
+ if self.secret_spec:
672
+ body["secret_spec"] = self.secret_spec.as_dict()
673
+ if self.serving_endpoint_spec:
674
+ body["serving_endpoint_spec"] = self.serving_endpoint_spec.as_dict()
675
+ if self.sql_warehouse_spec:
676
+ body["sql_warehouse_spec"] = self.sql_warehouse_spec.as_dict()
677
+ if self.uc_securable_spec:
678
+ body["uc_securable_spec"] = self.uc_securable_spec.as_dict()
679
+ return body
680
+
681
+ def as_shallow_dict(self) -> dict:
682
+ """Serializes the AppManifestAppResourceSpec into a shallow dictionary of its immediate attributes."""
683
+ body = {}
684
+ if self.description is not None:
685
+ body["description"] = self.description
686
+ if self.job_spec:
687
+ body["job_spec"] = self.job_spec
688
+ if self.name is not None:
689
+ body["name"] = self.name
690
+ if self.secret_spec:
691
+ body["secret_spec"] = self.secret_spec
692
+ if self.serving_endpoint_spec:
693
+ body["serving_endpoint_spec"] = self.serving_endpoint_spec
694
+ if self.sql_warehouse_spec:
695
+ body["sql_warehouse_spec"] = self.sql_warehouse_spec
696
+ if self.uc_securable_spec:
697
+ body["uc_securable_spec"] = self.uc_securable_spec
698
+ return body
699
+
700
+ @classmethod
701
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSpec:
702
+ """Deserializes the AppManifestAppResourceSpec from a dictionary."""
703
+ return cls(
704
+ description=d.get("description", None),
705
+ job_spec=_from_dict(d, "job_spec", AppManifestAppResourceJobSpec),
706
+ name=d.get("name", None),
707
+ secret_spec=_from_dict(d, "secret_spec", AppManifestAppResourceSecretSpec),
708
+ serving_endpoint_spec=_from_dict(d, "serving_endpoint_spec", AppManifestAppResourceServingEndpointSpec),
709
+ sql_warehouse_spec=_from_dict(d, "sql_warehouse_spec", AppManifestAppResourceSqlWarehouseSpec),
710
+ uc_securable_spec=_from_dict(d, "uc_securable_spec", AppManifestAppResourceUcSecurableSpec),
711
+ )
712
+
713
+
714
+ @dataclass
715
+ class AppManifestAppResourceSqlWarehouseSpec:
716
+ permission: AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission
717
+ """Permission to grant on the SQL warehouse. Supported permissions are: "CAN_MANAGE", "CAN_USE",
718
+ "IS_OWNER"."""
719
+
720
+ def as_dict(self) -> dict:
721
+ """Serializes the AppManifestAppResourceSqlWarehouseSpec into a dictionary suitable for use as a JSON request body."""
722
+ body = {}
723
+ if self.permission is not None:
724
+ body["permission"] = self.permission.value
725
+ return body
726
+
727
+ def as_shallow_dict(self) -> dict:
728
+ """Serializes the AppManifestAppResourceSqlWarehouseSpec into a shallow dictionary of its immediate attributes."""
729
+ body = {}
730
+ if self.permission is not None:
731
+ body["permission"] = self.permission
732
+ return body
733
+
734
+ @classmethod
735
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceSqlWarehouseSpec:
736
+ """Deserializes the AppManifestAppResourceSqlWarehouseSpec from a dictionary."""
737
+ return cls(permission=_enum(d, "permission", AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission))
738
+
739
+
740
+ class AppManifestAppResourceSqlWarehouseSpecSqlWarehousePermission(Enum):
741
+
742
+ CAN_MANAGE = "CAN_MANAGE"
743
+ CAN_USE = "CAN_USE"
744
+ IS_OWNER = "IS_OWNER"
745
+
746
+
747
+ @dataclass
748
+ class AppManifestAppResourceUcSecurableSpec:
749
+ securable_type: AppManifestAppResourceUcSecurableSpecUcSecurableType
750
+
751
+ permission: AppManifestAppResourceUcSecurableSpecUcSecurablePermission
752
+
753
+ def as_dict(self) -> dict:
754
+ """Serializes the AppManifestAppResourceUcSecurableSpec into a dictionary suitable for use as a JSON request body."""
755
+ body = {}
756
+ if self.permission is not None:
757
+ body["permission"] = self.permission.value
758
+ if self.securable_type is not None:
759
+ body["securable_type"] = self.securable_type.value
760
+ return body
761
+
762
+ def as_shallow_dict(self) -> dict:
763
+ """Serializes the AppManifestAppResourceUcSecurableSpec into a shallow dictionary of its immediate attributes."""
764
+ body = {}
765
+ if self.permission is not None:
766
+ body["permission"] = self.permission
767
+ if self.securable_type is not None:
768
+ body["securable_type"] = self.securable_type
769
+ return body
770
+
771
+ @classmethod
772
+ def from_dict(cls, d: Dict[str, Any]) -> AppManifestAppResourceUcSecurableSpec:
773
+ """Deserializes the AppManifestAppResourceUcSecurableSpec from a dictionary."""
774
+ return cls(
775
+ permission=_enum(d, "permission", AppManifestAppResourceUcSecurableSpecUcSecurablePermission),
776
+ securable_type=_enum(d, "securable_type", AppManifestAppResourceUcSecurableSpecUcSecurableType),
777
+ )
778
+
779
+
780
+ class AppManifestAppResourceUcSecurableSpecUcSecurablePermission(Enum):
781
+
782
+ MANAGE = "MANAGE"
783
+ READ_VOLUME = "READ_VOLUME"
784
+ WRITE_VOLUME = "WRITE_VOLUME"
785
+
786
+
787
+ class AppManifestAppResourceUcSecurableSpecUcSecurableType(Enum):
788
+
789
+ VOLUME = "VOLUME"
790
+
791
+
490
792
  @dataclass
491
793
  class AppPermission:
492
794
  inherited: Optional[bool] = None
@@ -1040,6 +1342,81 @@ class ComputeStatus:
1040
1342
  return cls(message=d.get("message", None), state=_enum(d, "state", ComputeState))
1041
1343
 
1042
1344
 
1345
+ @dataclass
1346
+ class CustomTemplate:
1347
+ name: str
1348
+ """The name of the template. It must contain only alphanumeric characters, hyphens, underscores,
1349
+ and whitespaces. It must be unique within the workspace."""
1350
+
1351
+ git_repo: str
1352
+ """The Git repository URL that the template resides in."""
1353
+
1354
+ path: str
1355
+ """The path to the template within the Git repository."""
1356
+
1357
+ manifest: AppManifest
1358
+ """The manifest of the template. It defines fields and default values when installing the template."""
1359
+
1360
+ git_provider: str
1361
+ """The Git provider of the template."""
1362
+
1363
+ creator: Optional[str] = None
1364
+
1365
+ description: Optional[str] = None
1366
+ """The description of the template."""
1367
+
1368
+ def as_dict(self) -> dict:
1369
+ """Serializes the CustomTemplate into a dictionary suitable for use as a JSON request body."""
1370
+ body = {}
1371
+ if self.creator is not None:
1372
+ body["creator"] = self.creator
1373
+ if self.description is not None:
1374
+ body["description"] = self.description
1375
+ if self.git_provider is not None:
1376
+ body["git_provider"] = self.git_provider
1377
+ if self.git_repo is not None:
1378
+ body["git_repo"] = self.git_repo
1379
+ if self.manifest:
1380
+ body["manifest"] = self.manifest.as_dict()
1381
+ if self.name is not None:
1382
+ body["name"] = self.name
1383
+ if self.path is not None:
1384
+ body["path"] = self.path
1385
+ return body
1386
+
1387
+ def as_shallow_dict(self) -> dict:
1388
+ """Serializes the CustomTemplate into a shallow dictionary of its immediate attributes."""
1389
+ body = {}
1390
+ if self.creator is not None:
1391
+ body["creator"] = self.creator
1392
+ if self.description is not None:
1393
+ body["description"] = self.description
1394
+ if self.git_provider is not None:
1395
+ body["git_provider"] = self.git_provider
1396
+ if self.git_repo is not None:
1397
+ body["git_repo"] = self.git_repo
1398
+ if self.manifest:
1399
+ body["manifest"] = self.manifest
1400
+ if self.name is not None:
1401
+ body["name"] = self.name
1402
+ if self.path is not None:
1403
+ body["path"] = self.path
1404
+ return body
1405
+
1406
+ @classmethod
1407
+ def from_dict(cls, d: Dict[str, Any]) -> CustomTemplate:
1408
+ """Deserializes the CustomTemplate from a dictionary."""
1409
+ return cls(
1410
+ creator=d.get("creator", None),
1411
+ description=d.get("description", None),
1412
+ git_provider=d.get("git_provider", None),
1413
+ git_repo=d.get("git_repo", None),
1414
+ manifest=_from_dict(d, "manifest", AppManifest),
1415
+ name=d.get("name", None),
1416
+ path=d.get("path", None),
1417
+ )
1418
+
1419
+
1043
1420
  @dataclass
1044
1421
  class GetAppPermissionLevelsResponse:
1045
1422
  permission_levels: Optional[List[AppPermissionsDescription]] = None
@@ -1131,6 +1508,39 @@ class ListAppsResponse:
1131
1508
  return cls(apps=_repeated_dict(d, "apps", App), next_page_token=d.get("next_page_token", None))
1132
1509
 
1133
1510
 
1511
+ @dataclass
1512
+ class ListCustomTemplatesResponse:
1513
+ next_page_token: Optional[str] = None
1514
+ """Pagination token to request the next page of custom templates."""
1515
+
1516
+ templates: Optional[List[CustomTemplate]] = None
1517
+
1518
+ def as_dict(self) -> dict:
1519
+ """Serializes the ListCustomTemplatesResponse into a dictionary suitable for use as a JSON request body."""
1520
+ body = {}
1521
+ if self.next_page_token is not None:
1522
+ body["next_page_token"] = self.next_page_token
1523
+ if self.templates:
1524
+ body["templates"] = [v.as_dict() for v in self.templates]
1525
+ return body
1526
+
1527
+ def as_shallow_dict(self) -> dict:
1528
+ """Serializes the ListCustomTemplatesResponse into a shallow dictionary of its immediate attributes."""
1529
+ body = {}
1530
+ if self.next_page_token is not None:
1531
+ body["next_page_token"] = self.next_page_token
1532
+ if self.templates:
1533
+ body["templates"] = self.templates
1534
+ return body
1535
+
1536
+ @classmethod
1537
+ def from_dict(cls, d: Dict[str, Any]) -> ListCustomTemplatesResponse:
1538
+ """Deserializes the ListCustomTemplatesResponse from a dictionary."""
1539
+ return cls(
1540
+ next_page_token=d.get("next_page_token", None), templates=_repeated_dict(d, "templates", CustomTemplate)
1541
+ )
1542
+
1543
+
1134
1544
  class AppsAPI:
1135
1545
  """Apps run directly on a customer’s Databricks instance, integrate with their data, use and extend
1136
1546
  Databricks services, and enable users to interact through single sign-on."""
@@ -1546,3 +1956,108 @@ class AppsAPI:
1546
1956
 
1547
1957
  res = self._api.do("PATCH", f"/api/2.0/permissions/apps/{app_name}", body=body, headers=headers)
1548
1958
  return AppPermissions.from_dict(res)
1959
+
1960
+
1961
+ class AppsSettingsAPI:
1962
+ """Apps Settings manage the settings for the Apps service on a customer's Databricks instance."""
1963
+
1964
+ def __init__(self, api_client):
1965
+ self._api = api_client
1966
+
1967
+ def create_custom_template(self, template: CustomTemplate) -> CustomTemplate:
1968
+ """Creates a custom template.
1969
+
1970
+ :param template: :class:`CustomTemplate`
1971
+
1972
+ :returns: :class:`CustomTemplate`
1973
+ """
1974
+ body = template.as_dict()
1975
+ headers = {
1976
+ "Accept": "application/json",
1977
+ "Content-Type": "application/json",
1978
+ }
1979
+
1980
+ res = self._api.do("POST", "/api/2.0/apps-settings/templates", body=body, headers=headers)
1981
+ return CustomTemplate.from_dict(res)
1982
+
1983
+ def delete_custom_template(self, name: str) -> CustomTemplate:
1984
+ """Deletes the custom template with the specified name.
1985
+
1986
+ :param name: str
1987
+ The name of the custom template.
1988
+
1989
+ :returns: :class:`CustomTemplate`
1990
+ """
1991
+
1992
+ headers = {
1993
+ "Accept": "application/json",
1994
+ }
1995
+
1996
+ res = self._api.do("DELETE", f"/api/2.0/apps-settings/templates/{name}", headers=headers)
1997
+ return CustomTemplate.from_dict(res)
1998
+
1999
+ def get_custom_template(self, name: str) -> CustomTemplate:
2000
+ """Gets the custom template with the specified name.
2001
+
2002
+ :param name: str
2003
+ The name of the custom template.
2004
+
2005
+ :returns: :class:`CustomTemplate`
2006
+ """
2007
+
2008
+ headers = {
2009
+ "Accept": "application/json",
2010
+ }
2011
+
2012
+ res = self._api.do("GET", f"/api/2.0/apps-settings/templates/{name}", headers=headers)
2013
+ return CustomTemplate.from_dict(res)
2014
+
2015
+ def list_custom_templates(
2016
+ self, *, page_size: Optional[int] = None, page_token: Optional[str] = None
2017
+ ) -> Iterator[CustomTemplate]:
2018
+ """Lists all custom templates in the workspace.
2019
+
2020
+ :param page_size: int (optional)
2021
+ Upper bound for items returned.
2022
+ :param page_token: str (optional)
2023
+ Pagination token to go to the next page of custom templates. Requests first page if absent.
2024
+
2025
+ :returns: Iterator over :class:`CustomTemplate`
2026
+ """
2027
+
2028
+ query = {}
2029
+ if page_size is not None:
2030
+ query["page_size"] = page_size
2031
+ if page_token is not None:
2032
+ query["page_token"] = page_token
2033
+ headers = {
2034
+ "Accept": "application/json",
2035
+ }
2036
+
2037
+ while True:
2038
+ json = self._api.do("GET", "/api/2.0/apps-settings/templates", query=query, headers=headers)
2039
+ if "templates" in json:
2040
+ for v in json["templates"]:
2041
+ yield CustomTemplate.from_dict(v)
2042
+ if "next_page_token" not in json or not json["next_page_token"]:
2043
+ return
2044
+ query["page_token"] = json["next_page_token"]
2045
+
2046
+ def update_custom_template(self, name: str, template: CustomTemplate) -> CustomTemplate:
2047
+ """Updates the custom template with the specified name. Note that the template name cannot be updated.
2048
+
2049
+ :param name: str
2050
+ The name of the template. It must contain only alphanumeric characters, hyphens, underscores, and
2051
+ whitespaces. It must be unique within the workspace.
2052
+ :param template: :class:`CustomTemplate`
2053
+
2054
+ :returns: :class:`CustomTemplate`
2055
+ """
2056
+ body = template.as_dict()
2057
+ headers = {
2058
+ "Accept": "application/json",
2059
+ "Content-Type": "application/json",
2060
+ }
2061
+
2062
+ res = self._api.do("PUT", f"/api/2.0/apps-settings/templates/{name}", body=body, headers=headers)
2063
+ return CustomTemplate.from_dict(res)