databricks-sdk 0.32.3__py3-none-any.whl → 0.34.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.

@@ -25,7 +25,12 @@ class App:
25
25
  It must be unique within the workspace."""
26
26
 
27
27
  active_deployment: Optional[AppDeployment] = None
28
- """The active deployment of the app."""
28
+ """The active deployment of the app. A deployment is considered active when it has been deployed to
29
+ the app compute."""
30
+
31
+ app_status: Optional[ApplicationStatus] = None
32
+
33
+ compute_status: Optional[ComputeStatus] = None
29
34
 
30
35
  create_time: Optional[str] = None
31
36
  """The creation time of the app. Formatted timestamp in ISO 6801."""
@@ -33,18 +38,24 @@ class App:
33
38
  creator: Optional[str] = None
34
39
  """The email of the user that created the app."""
35
40
 
41
+ default_source_code_path: Optional[str] = None
42
+ """The default workspace file system path of the source code from which app deployment are created.
43
+ This field tracks the workspace source code path of the last active deployment."""
44
+
36
45
  description: Optional[str] = None
37
46
  """The description of the app."""
38
47
 
39
48
  pending_deployment: Optional[AppDeployment] = None
40
- """The pending deployment of the app."""
49
+ """The pending deployment of the app. A deployment is considered pending when it is being prepared
50
+ for deployment to the app compute."""
51
+
52
+ resources: Optional[List[AppResource]] = None
53
+ """Resources for the app."""
41
54
 
42
55
  service_principal_id: Optional[int] = None
43
56
 
44
57
  service_principal_name: Optional[str] = None
45
58
 
46
- status: Optional[AppStatus] = None
47
-
48
59
  update_time: Optional[str] = None
49
60
  """The update time of the app. Formatted timestamp in ISO 6801."""
50
61
 
@@ -58,15 +69,19 @@ class App:
58
69
  """Serializes the App into a dictionary suitable for use as a JSON request body."""
59
70
  body = {}
60
71
  if self.active_deployment: body['active_deployment'] = self.active_deployment.as_dict()
72
+ if self.app_status: body['app_status'] = self.app_status.as_dict()
73
+ if self.compute_status: body['compute_status'] = self.compute_status.as_dict()
61
74
  if self.create_time is not None: body['create_time'] = self.create_time
62
75
  if self.creator is not None: body['creator'] = self.creator
76
+ if self.default_source_code_path is not None:
77
+ body['default_source_code_path'] = self.default_source_code_path
63
78
  if self.description is not None: body['description'] = self.description
64
79
  if self.name is not None: body['name'] = self.name
65
80
  if self.pending_deployment: body['pending_deployment'] = self.pending_deployment.as_dict()
81
+ if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
66
82
  if self.service_principal_id is not None: body['service_principal_id'] = self.service_principal_id
67
83
  if self.service_principal_name is not None:
68
84
  body['service_principal_name'] = self.service_principal_name
69
- if self.status: body['status'] = self.status.as_dict()
70
85
  if self.update_time is not None: body['update_time'] = self.update_time
71
86
  if self.updater is not None: body['updater'] = self.updater
72
87
  if self.url is not None: body['url'] = self.url
@@ -76,14 +91,17 @@ class App:
76
91
  def from_dict(cls, d: Dict[str, any]) -> App:
77
92
  """Deserializes the App from a dictionary."""
78
93
  return cls(active_deployment=_from_dict(d, 'active_deployment', AppDeployment),
94
+ app_status=_from_dict(d, 'app_status', ApplicationStatus),
95
+ compute_status=_from_dict(d, 'compute_status', ComputeStatus),
79
96
  create_time=d.get('create_time', None),
80
97
  creator=d.get('creator', None),
98
+ default_source_code_path=d.get('default_source_code_path', None),
81
99
  description=d.get('description', None),
82
100
  name=d.get('name', None),
83
101
  pending_deployment=_from_dict(d, 'pending_deployment', AppDeployment),
102
+ resources=_repeated_dict(d, 'resources', AppResource),
84
103
  service_principal_id=d.get('service_principal_id', None),
85
104
  service_principal_name=d.get('service_principal_name', None),
86
- status=_from_dict(d, 'status', AppStatus),
87
105
  update_time=d.get('update_time', None),
88
106
  updater=d.get('updater', None),
89
107
  url=d.get('url', None))
@@ -162,13 +180,6 @@ class AppAccessControlResponse:
162
180
 
163
181
  @dataclass
164
182
  class AppDeployment:
165
- source_code_path: str
166
- """The workspace file system path of the source code used to create the app deployment. This is
167
- different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
168
- app. The former refers to the original source code location of the app in the workspace during
169
- deployment creation, whereas the latter provides a system generated stable snapshotted source
170
- code path used by the deployment."""
171
-
172
183
  create_time: Optional[str] = None
173
184
  """The creation time of the deployment. Formatted timestamp in ISO 6801."""
174
185
 
@@ -184,6 +195,13 @@ class AppDeployment:
184
195
  mode: Optional[AppDeploymentMode] = None
185
196
  """The mode of which the deployment will manage the source code."""
186
197
 
198
+ source_code_path: Optional[str] = None
199
+ """The workspace file system path of the source code used to create the app deployment. This is
200
+ different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
201
+ app. The former refers to the original source code location of the app in the workspace during
202
+ deployment creation, whereas the latter provides a system generated stable snapshotted source
203
+ code path used by the deployment."""
204
+
187
205
  status: Optional[AppDeploymentStatus] = None
188
206
  """Status and status message of the deployment"""
189
207
 
@@ -241,9 +259,9 @@ class AppDeploymentMode(Enum):
241
259
 
242
260
  class AppDeploymentState(Enum):
243
261
 
262
+ CANCELLED = 'CANCELLED'
244
263
  FAILED = 'FAILED'
245
264
  IN_PROGRESS = 'IN_PROGRESS'
246
- STOPPED = 'STOPPED'
247
265
  SUCCEEDED = 'SUCCEEDED'
248
266
 
249
267
 
@@ -368,57 +386,254 @@ class AppPermissionsRequest:
368
386
  app_name=d.get('app_name', None))
369
387
 
370
388
 
371
- class AppState(Enum):
389
+ @dataclass
390
+ class AppResource:
391
+ name: str
392
+ """Name of the App Resource."""
393
+
394
+ description: Optional[str] = None
395
+ """Description of the App Resource."""
396
+
397
+ job: Optional[AppResourceJob] = None
398
+
399
+ secret: Optional[AppResourceSecret] = None
400
+
401
+ serving_endpoint: Optional[AppResourceServingEndpoint] = None
402
+
403
+ sql_warehouse: Optional[AppResourceSqlWarehouse] = None
404
+
405
+ def as_dict(self) -> dict:
406
+ """Serializes the AppResource into a dictionary suitable for use as a JSON request body."""
407
+ body = {}
408
+ if self.description is not None: body['description'] = self.description
409
+ if self.job: body['job'] = self.job.as_dict()
410
+ if self.name is not None: body['name'] = self.name
411
+ if self.secret: body['secret'] = self.secret.as_dict()
412
+ if self.serving_endpoint: body['serving_endpoint'] = self.serving_endpoint.as_dict()
413
+ if self.sql_warehouse: body['sql_warehouse'] = self.sql_warehouse.as_dict()
414
+ return body
415
+
416
+ @classmethod
417
+ def from_dict(cls, d: Dict[str, any]) -> AppResource:
418
+ """Deserializes the AppResource from a dictionary."""
419
+ return cls(description=d.get('description', None),
420
+ job=_from_dict(d, 'job', AppResourceJob),
421
+ name=d.get('name', None),
422
+ secret=_from_dict(d, 'secret', AppResourceSecret),
423
+ serving_endpoint=_from_dict(d, 'serving_endpoint', AppResourceServingEndpoint),
424
+ sql_warehouse=_from_dict(d, 'sql_warehouse', AppResourceSqlWarehouse))
425
+
426
+
427
+ @dataclass
428
+ class AppResourceJob:
429
+ id: str
430
+ """Id of the job to grant permission on."""
431
+
432
+ permission: AppResourceJobJobPermission
433
+ """Permissions to grant on the Job. Supported permissions are: "CAN_MANAGE", "IS_OWNER",
434
+ "CAN_MANAGE_RUN", "CAN_VIEW"."""
435
+
436
+ def as_dict(self) -> dict:
437
+ """Serializes the AppResourceJob into a dictionary suitable for use as a JSON request body."""
438
+ body = {}
439
+ if self.id is not None: body['id'] = self.id
440
+ if self.permission is not None: body['permission'] = self.permission.value
441
+ return body
442
+
443
+ @classmethod
444
+ def from_dict(cls, d: Dict[str, any]) -> AppResourceJob:
445
+ """Deserializes the AppResourceJob from a dictionary."""
446
+ return cls(id=d.get('id', None), permission=_enum(d, 'permission', AppResourceJobJobPermission))
447
+
448
+
449
+ class AppResourceJobJobPermission(Enum):
450
+
451
+ CAN_MANAGE = 'CAN_MANAGE'
452
+ CAN_MANAGE_RUN = 'CAN_MANAGE_RUN'
453
+ CAN_VIEW = 'CAN_VIEW'
454
+ IS_OWNER = 'IS_OWNER'
372
455
 
373
- CREATING = 'CREATING'
374
- DELETED = 'DELETED'
456
+
457
+ @dataclass
458
+ class AppResourceSecret:
459
+ scope: str
460
+ """Scope of the secret to grant permission on."""
461
+
462
+ key: str
463
+ """Key of the secret to grant permission on."""
464
+
465
+ permission: AppResourceSecretSecretPermission
466
+ """Permission to grant on the secret scope. For secrets, only one permission is allowed. Permission
467
+ must be one of: "READ", "WRITE", "MANAGE"."""
468
+
469
+ def as_dict(self) -> dict:
470
+ """Serializes the AppResourceSecret into a dictionary suitable for use as a JSON request body."""
471
+ body = {}
472
+ if self.key is not None: body['key'] = self.key
473
+ if self.permission is not None: body['permission'] = self.permission.value
474
+ if self.scope is not None: body['scope'] = self.scope
475
+ return body
476
+
477
+ @classmethod
478
+ def from_dict(cls, d: Dict[str, any]) -> AppResourceSecret:
479
+ """Deserializes the AppResourceSecret from a dictionary."""
480
+ return cls(key=d.get('key', None),
481
+ permission=_enum(d, 'permission', AppResourceSecretSecretPermission),
482
+ scope=d.get('scope', None))
483
+
484
+
485
+ class AppResourceSecretSecretPermission(Enum):
486
+ """Permission to grant on the secret scope. Supported permissions are: "READ", "WRITE", "MANAGE"."""
487
+
488
+ MANAGE = 'MANAGE'
489
+ READ = 'READ'
490
+ WRITE = 'WRITE'
491
+
492
+
493
+ @dataclass
494
+ class AppResourceServingEndpoint:
495
+ name: str
496
+ """Name of the serving endpoint to grant permission on."""
497
+
498
+ permission: AppResourceServingEndpointServingEndpointPermission
499
+ """Permission to grant on the serving endpoint. Supported permissions are: "CAN_MANAGE",
500
+ "CAN_QUERY", "CAN_VIEW"."""
501
+
502
+ def as_dict(self) -> dict:
503
+ """Serializes the AppResourceServingEndpoint into a dictionary suitable for use as a JSON request body."""
504
+ body = {}
505
+ if self.name is not None: body['name'] = self.name
506
+ if self.permission is not None: body['permission'] = self.permission.value
507
+ return body
508
+
509
+ @classmethod
510
+ def from_dict(cls, d: Dict[str, any]) -> AppResourceServingEndpoint:
511
+ """Deserializes the AppResourceServingEndpoint from a dictionary."""
512
+ return cls(name=d.get('name', None),
513
+ permission=_enum(d, 'permission', AppResourceServingEndpointServingEndpointPermission))
514
+
515
+
516
+ class AppResourceServingEndpointServingEndpointPermission(Enum):
517
+
518
+ CAN_MANAGE = 'CAN_MANAGE'
519
+ CAN_QUERY = 'CAN_QUERY'
520
+ CAN_VIEW = 'CAN_VIEW'
521
+
522
+
523
+ @dataclass
524
+ class AppResourceSqlWarehouse:
525
+ id: str
526
+ """Id of the SQL warehouse to grant permission on."""
527
+
528
+ permission: AppResourceSqlWarehouseSqlWarehousePermission
529
+ """Permission to grant on the SQL warehouse. Supported permissions are: "CAN_MANAGE", "CAN_USE",
530
+ "IS_OWNER"."""
531
+
532
+ def as_dict(self) -> dict:
533
+ """Serializes the AppResourceSqlWarehouse into a dictionary suitable for use as a JSON request body."""
534
+ body = {}
535
+ if self.id is not None: body['id'] = self.id
536
+ if self.permission is not None: body['permission'] = self.permission.value
537
+ return body
538
+
539
+ @classmethod
540
+ def from_dict(cls, d: Dict[str, any]) -> AppResourceSqlWarehouse:
541
+ """Deserializes the AppResourceSqlWarehouse from a dictionary."""
542
+ return cls(id=d.get('id', None),
543
+ permission=_enum(d, 'permission', AppResourceSqlWarehouseSqlWarehousePermission))
544
+
545
+
546
+ class AppResourceSqlWarehouseSqlWarehousePermission(Enum):
547
+
548
+ CAN_MANAGE = 'CAN_MANAGE'
549
+ CAN_USE = 'CAN_USE'
550
+ IS_OWNER = 'IS_OWNER'
551
+
552
+
553
+ class ApplicationState(Enum):
554
+
555
+ CRASHED = 'CRASHED'
556
+ DEPLOYING = 'DEPLOYING'
557
+ RUNNING = 'RUNNING'
558
+ UNAVAILABLE = 'UNAVAILABLE'
559
+
560
+
561
+ @dataclass
562
+ class ApplicationStatus:
563
+ message: Optional[str] = None
564
+ """Application status message"""
565
+
566
+ state: Optional[ApplicationState] = None
567
+ """State of the application."""
568
+
569
+ def as_dict(self) -> dict:
570
+ """Serializes the ApplicationStatus into a dictionary suitable for use as a JSON request body."""
571
+ body = {}
572
+ if self.message is not None: body['message'] = self.message
573
+ if self.state is not None: body['state'] = self.state.value
574
+ return body
575
+
576
+ @classmethod
577
+ def from_dict(cls, d: Dict[str, any]) -> ApplicationStatus:
578
+ """Deserializes the ApplicationStatus from a dictionary."""
579
+ return cls(message=d.get('message', None), state=_enum(d, 'state', ApplicationState))
580
+
581
+
582
+ class ComputeState(Enum):
583
+
584
+ ACTIVE = 'ACTIVE'
375
585
  DELETING = 'DELETING'
376
586
  ERROR = 'ERROR'
377
- IDLE = 'IDLE'
378
- RUNNING = 'RUNNING'
379
587
  STARTING = 'STARTING'
588
+ STOPPED = 'STOPPED'
589
+ STOPPING = 'STOPPING'
590
+ UPDATING = 'UPDATING'
380
591
 
381
592
 
382
593
  @dataclass
383
- class AppStatus:
594
+ class ComputeStatus:
384
595
  message: Optional[str] = None
385
- """Message corresponding with the app state."""
596
+ """Compute status message"""
386
597
 
387
- state: Optional[AppState] = None
388
- """State of the app."""
598
+ state: Optional[ComputeState] = None
599
+ """State of the app compute."""
389
600
 
390
601
  def as_dict(self) -> dict:
391
- """Serializes the AppStatus into a dictionary suitable for use as a JSON request body."""
602
+ """Serializes the ComputeStatus into a dictionary suitable for use as a JSON request body."""
392
603
  body = {}
393
604
  if self.message is not None: body['message'] = self.message
394
605
  if self.state is not None: body['state'] = self.state.value
395
606
  return body
396
607
 
397
608
  @classmethod
398
- def from_dict(cls, d: Dict[str, any]) -> AppStatus:
399
- """Deserializes the AppStatus from a dictionary."""
400
- return cls(message=d.get('message', None), state=_enum(d, 'state', AppState))
609
+ def from_dict(cls, d: Dict[str, any]) -> ComputeStatus:
610
+ """Deserializes the ComputeStatus from a dictionary."""
611
+ return cls(message=d.get('message', None), state=_enum(d, 'state', ComputeState))
401
612
 
402
613
 
403
614
  @dataclass
404
615
  class CreateAppDeploymentRequest:
405
- source_code_path: str
406
- """The workspace file system path of the source code used to create the app deployment. This is
407
- different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
408
- app. The former refers to the original source code location of the app in the workspace during
409
- deployment creation, whereas the latter provides a system generated stable snapshotted source
410
- code path used by the deployment."""
411
-
412
616
  app_name: Optional[str] = None
413
617
  """The name of the app."""
414
618
 
619
+ deployment_id: Optional[str] = None
620
+ """The unique id of the deployment."""
621
+
415
622
  mode: Optional[AppDeploymentMode] = None
416
623
  """The mode of which the deployment will manage the source code."""
417
624
 
625
+ source_code_path: Optional[str] = None
626
+ """The workspace file system path of the source code used to create the app deployment. This is
627
+ different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
628
+ app. The former refers to the original source code location of the app in the workspace during
629
+ deployment creation, whereas the latter provides a system generated stable snapshotted source
630
+ code path used by the deployment."""
631
+
418
632
  def as_dict(self) -> dict:
419
633
  """Serializes the CreateAppDeploymentRequest into a dictionary suitable for use as a JSON request body."""
420
634
  body = {}
421
635
  if self.app_name is not None: body['app_name'] = self.app_name
636
+ if self.deployment_id is not None: body['deployment_id'] = self.deployment_id
422
637
  if self.mode is not None: body['mode'] = self.mode.value
423
638
  if self.source_code_path is not None: body['source_code_path'] = self.source_code_path
424
639
  return body
@@ -427,6 +642,7 @@ class CreateAppDeploymentRequest:
427
642
  def from_dict(cls, d: Dict[str, any]) -> CreateAppDeploymentRequest:
428
643
  """Deserializes the CreateAppDeploymentRequest from a dictionary."""
429
644
  return cls(app_name=d.get('app_name', None),
645
+ deployment_id=d.get('deployment_id', None),
430
646
  mode=_enum(d, 'mode', AppDeploymentMode),
431
647
  source_code_path=d.get('source_code_path', None))
432
648
 
@@ -440,31 +656,23 @@ class CreateAppRequest:
440
656
  description: Optional[str] = None
441
657
  """The description of the app."""
442
658
 
659
+ resources: Optional[List[AppResource]] = None
660
+ """Resources for the app."""
661
+
443
662
  def as_dict(self) -> dict:
444
663
  """Serializes the CreateAppRequest into a dictionary suitable for use as a JSON request body."""
445
664
  body = {}
446
665
  if self.description is not None: body['description'] = self.description
447
666
  if self.name is not None: body['name'] = self.name
667
+ if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
448
668
  return body
449
669
 
450
670
  @classmethod
451
671
  def from_dict(cls, d: Dict[str, any]) -> CreateAppRequest:
452
672
  """Deserializes the CreateAppRequest from a dictionary."""
453
- return cls(description=d.get('description', None), name=d.get('name', None))
454
-
455
-
456
- @dataclass
457
- class DeleteResponse:
458
-
459
- def as_dict(self) -> dict:
460
- """Serializes the DeleteResponse into a dictionary suitable for use as a JSON request body."""
461
- body = {}
462
- return body
463
-
464
- @classmethod
465
- def from_dict(cls, d: Dict[str, any]) -> DeleteResponse:
466
- """Deserializes the DeleteResponse from a dictionary."""
467
- return cls()
673
+ return cls(description=d.get('description', None),
674
+ name=d.get('name', None),
675
+ resources=_repeated_dict(d, 'resources', AppResource))
468
676
 
469
677
 
470
678
  @dataclass
@@ -538,20 +746,6 @@ class StopAppRequest:
538
746
  """The name of the app."""
539
747
 
540
748
 
541
- @dataclass
542
- class StopAppResponse:
543
-
544
- def as_dict(self) -> dict:
545
- """Serializes the StopAppResponse into a dictionary suitable for use as a JSON request body."""
546
- body = {}
547
- return body
548
-
549
- @classmethod
550
- def from_dict(cls, d: Dict[str, any]) -> StopAppResponse:
551
- """Deserializes the StopAppResponse from a dictionary."""
552
- return cls()
553
-
554
-
555
749
  @dataclass
556
750
  class UpdateAppRequest:
557
751
  name: str
@@ -561,17 +755,23 @@ class UpdateAppRequest:
561
755
  description: Optional[str] = None
562
756
  """The description of the app."""
563
757
 
758
+ resources: Optional[List[AppResource]] = None
759
+ """Resources for the app."""
760
+
564
761
  def as_dict(self) -> dict:
565
762
  """Serializes the UpdateAppRequest into a dictionary suitable for use as a JSON request body."""
566
763
  body = {}
567
764
  if self.description is not None: body['description'] = self.description
568
765
  if self.name is not None: body['name'] = self.name
766
+ if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
569
767
  return body
570
768
 
571
769
  @classmethod
572
770
  def from_dict(cls, d: Dict[str, any]) -> UpdateAppRequest:
573
771
  """Deserializes the UpdateAppRequest from a dictionary."""
574
- return cls(description=d.get('description', None), name=d.get('name', None))
772
+ return cls(description=d.get('description', None),
773
+ name=d.get('name', None),
774
+ resources=_repeated_dict(d, 'resources', AppResource))
575
775
 
576
776
 
577
777
  class AppsAPI:
@@ -581,27 +781,59 @@ class AppsAPI:
581
781
  def __init__(self, api_client):
582
782
  self._api = api_client
583
783
 
584
- def wait_get_app_idle(self,
585
- name: str,
586
- timeout=timedelta(minutes=20),
587
- callback: Optional[Callable[[App], None]] = None) -> App:
784
+ def wait_get_app_active(self,
785
+ name: str,
786
+ timeout=timedelta(minutes=20),
787
+ callback: Optional[Callable[[App], None]] = None) -> App:
588
788
  deadline = time.time() + timeout.total_seconds()
589
- target_states = (AppState.IDLE, )
590
- failure_states = (AppState.ERROR, )
789
+ target_states = (ComputeState.ACTIVE, )
790
+ failure_states = (ComputeState.ERROR, )
591
791
  status_message = 'polling...'
592
792
  attempt = 1
593
793
  while time.time() < deadline:
594
794
  poll = self.get(name=name)
595
- status = poll.status.state
795
+ status = poll.compute_status.state
596
796
  status_message = f'current status: {status}'
597
- if poll.status:
598
- status_message = poll.status.message
797
+ if poll.compute_status:
798
+ status_message = poll.compute_status.message
599
799
  if status in target_states:
600
800
  return poll
601
801
  if callback:
602
802
  callback(poll)
603
803
  if status in failure_states:
604
- msg = f'failed to reach IDLE, got {status}: {status_message}'
804
+ msg = f'failed to reach ACTIVE, got {status}: {status_message}'
805
+ raise OperationFailed(msg)
806
+ prefix = f"name={name}"
807
+ sleep = attempt
808
+ if sleep > 10:
809
+ # sleep 10s max per attempt
810
+ sleep = 10
811
+ _LOG.debug(f'{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)')
812
+ time.sleep(sleep + random.random())
813
+ attempt += 1
814
+ raise TimeoutError(f'timed out after {timeout}: {status_message}')
815
+
816
+ def wait_get_app_stopped(self,
817
+ name: str,
818
+ timeout=timedelta(minutes=20),
819
+ callback: Optional[Callable[[App], None]] = None) -> App:
820
+ deadline = time.time() + timeout.total_seconds()
821
+ target_states = (ComputeState.STOPPED, )
822
+ failure_states = (ComputeState.ERROR, )
823
+ status_message = 'polling...'
824
+ attempt = 1
825
+ while time.time() < deadline:
826
+ poll = self.get(name=name)
827
+ status = poll.compute_status.state
828
+ status_message = f'current status: {status}'
829
+ if poll.compute_status:
830
+ status_message = poll.compute_status.message
831
+ if status in target_states:
832
+ return poll
833
+ if callback:
834
+ callback(poll)
835
+ if status in failure_states:
836
+ msg = f'failed to reach STOPPED, got {status}: {status_message}'
605
837
  raise OperationFailed(msg)
606
838
  prefix = f"name={name}"
607
839
  sleep = attempt
@@ -647,7 +879,11 @@ class AppsAPI:
647
879
  attempt += 1
648
880
  raise TimeoutError(f'timed out after {timeout}: {status_message}')
649
881
 
650
- def create(self, name: str, *, description: Optional[str] = None) -> Wait[App]:
882
+ def create(self,
883
+ name: str,
884
+ *,
885
+ description: Optional[str] = None,
886
+ resources: Optional[List[AppResource]] = None) -> Wait[App]:
651
887
  """Create an app.
652
888
 
653
889
  Creates a new app.
@@ -657,27 +893,31 @@ class AppsAPI:
657
893
  must be unique within the workspace.
658
894
  :param description: str (optional)
659
895
  The description of the app.
896
+ :param resources: List[:class:`AppResource`] (optional)
897
+ Resources for the app.
660
898
 
661
899
  :returns:
662
900
  Long-running operation waiter for :class:`App`.
663
- See :method:wait_get_app_idle for more details.
901
+ See :method:wait_get_app_active for more details.
664
902
  """
665
903
  body = {}
666
904
  if description is not None: body['description'] = description
667
905
  if name is not None: body['name'] = name
906
+ if resources is not None: body['resources'] = [v.as_dict() for v in resources]
668
907
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
669
908
 
670
- op_response = self._api.do('POST', '/api/2.0/preview/apps', body=body, headers=headers)
671
- return Wait(self.wait_get_app_idle, response=App.from_dict(op_response), name=op_response['name'])
909
+ op_response = self._api.do('POST', '/api/2.0/apps', body=body, headers=headers)
910
+ return Wait(self.wait_get_app_active, response=App.from_dict(op_response), name=op_response['name'])
672
911
 
673
912
  def create_and_wait(self,
674
913
  name: str,
675
914
  *,
676
915
  description: Optional[str] = None,
916
+ resources: Optional[List[AppResource]] = None,
677
917
  timeout=timedelta(minutes=20)) -> App:
678
- return self.create(description=description, name=name).result(timeout=timeout)
918
+ return self.create(description=description, name=name, resources=resources).result(timeout=timeout)
679
919
 
680
- def delete(self, name: str):
920
+ def delete(self, name: str) -> App:
681
921
  """Delete an app.
682
922
 
683
923
  Deletes an app.
@@ -685,44 +925,49 @@ class AppsAPI:
685
925
  :param name: str
686
926
  The name of the app.
687
927
 
688
-
928
+ :returns: :class:`App`
689
929
  """
690
930
 
691
931
  headers = {'Accept': 'application/json', }
692
932
 
693
- self._api.do('DELETE', f'/api/2.0/preview/apps/{name}', headers=headers)
933
+ res = self._api.do('DELETE', f'/api/2.0/apps/{name}', headers=headers)
934
+ return App.from_dict(res)
694
935
 
695
936
  def deploy(self,
696
937
  app_name: str,
697
- source_code_path: str,
698
938
  *,
699
- mode: Optional[AppDeploymentMode] = None) -> Wait[AppDeployment]:
939
+ deployment_id: Optional[str] = None,
940
+ mode: Optional[AppDeploymentMode] = None,
941
+ source_code_path: Optional[str] = None) -> Wait[AppDeployment]:
700
942
  """Create an app deployment.
701
943
 
702
944
  Creates an app deployment for the app with the supplied name.
703
945
 
704
946
  :param app_name: str
705
947
  The name of the app.
706
- :param source_code_path: str
948
+ :param deployment_id: str (optional)
949
+ The unique id of the deployment.
950
+ :param mode: :class:`AppDeploymentMode` (optional)
951
+ The mode of which the deployment will manage the source code.
952
+ :param source_code_path: str (optional)
707
953
  The workspace file system path of the source code used to create the app deployment. This is
708
954
  different from `deployment_artifacts.source_code_path`, which is the path used by the deployed app.
709
955
  The former refers to the original source code location of the app in the workspace during deployment
710
956
  creation, whereas the latter provides a system generated stable snapshotted source code path used by
711
957
  the deployment.
712
- :param mode: :class:`AppDeploymentMode` (optional)
713
- The mode of which the deployment will manage the source code.
714
958
 
715
959
  :returns:
716
960
  Long-running operation waiter for :class:`AppDeployment`.
717
961
  See :method:wait_get_deployment_app_succeeded for more details.
718
962
  """
719
963
  body = {}
964
+ if deployment_id is not None: body['deployment_id'] = deployment_id
720
965
  if mode is not None: body['mode'] = mode.value
721
966
  if source_code_path is not None: body['source_code_path'] = source_code_path
722
967
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
723
968
 
724
969
  op_response = self._api.do('POST',
725
- f'/api/2.0/preview/apps/{app_name}/deployments',
970
+ f'/api/2.0/apps/{app_name}/deployments',
726
971
  body=body,
727
972
  headers=headers)
728
973
  return Wait(self.wait_get_deployment_app_succeeded,
@@ -733,11 +978,14 @@ class AppsAPI:
733
978
  def deploy_and_wait(
734
979
  self,
735
980
  app_name: str,
736
- source_code_path: str,
737
981
  *,
982
+ deployment_id: Optional[str] = None,
738
983
  mode: Optional[AppDeploymentMode] = None,
984
+ source_code_path: Optional[str] = None,
739
985
  timeout=timedelta(minutes=20)) -> AppDeployment:
740
- return self.deploy(app_name=app_name, mode=mode,
986
+ return self.deploy(app_name=app_name,
987
+ deployment_id=deployment_id,
988
+ mode=mode,
741
989
  source_code_path=source_code_path).result(timeout=timeout)
742
990
 
743
991
  def get(self, name: str) -> App:
@@ -753,7 +1001,7 @@ class AppsAPI:
753
1001
 
754
1002
  headers = {'Accept': 'application/json', }
755
1003
 
756
- res = self._api.do('GET', f'/api/2.0/preview/apps/{name}', headers=headers)
1004
+ res = self._api.do('GET', f'/api/2.0/apps/{name}', headers=headers)
757
1005
  return App.from_dict(res)
758
1006
 
759
1007
  def get_deployment(self, app_name: str, deployment_id: str) -> AppDeployment:
@@ -771,9 +1019,7 @@ class AppsAPI:
771
1019
 
772
1020
  headers = {'Accept': 'application/json', }
773
1021
 
774
- res = self._api.do('GET',
775
- f'/api/2.0/preview/apps/{app_name}/deployments/{deployment_id}',
776
- headers=headers)
1022
+ res = self._api.do('GET', f'/api/2.0/apps/{app_name}/deployments/{deployment_id}', headers=headers)
777
1023
  return AppDeployment.from_dict(res)
778
1024
 
779
1025
  def get_permission_levels(self, app_name: str) -> GetAppPermissionLevelsResponse:
@@ -827,7 +1073,7 @@ class AppsAPI:
827
1073
  headers = {'Accept': 'application/json', }
828
1074
 
829
1075
  while True:
830
- json = self._api.do('GET', '/api/2.0/preview/apps', query=query, headers=headers)
1076
+ json = self._api.do('GET', '/api/2.0/apps', query=query, headers=headers)
831
1077
  if 'apps' in json:
832
1078
  for v in json['apps']:
833
1079
  yield App.from_dict(v)
@@ -860,10 +1106,7 @@ class AppsAPI:
860
1106
  headers = {'Accept': 'application/json', }
861
1107
 
862
1108
  while True:
863
- json = self._api.do('GET',
864
- f'/api/2.0/preview/apps/{app_name}/deployments',
865
- query=query,
866
- headers=headers)
1109
+ json = self._api.do('GET', f'/api/2.0/apps/{app_name}/deployments', query=query, headers=headers)
867
1110
  if 'app_deployments' in json:
868
1111
  for v in json['app_deployments']:
869
1112
  yield AppDeployment.from_dict(v)
@@ -894,7 +1137,7 @@ class AppsAPI:
894
1137
  res = self._api.do('PUT', f'/api/2.0/permissions/apps/{app_name}', body=body, headers=headers)
895
1138
  return AppPermissions.from_dict(res)
896
1139
 
897
- def start(self, name: str) -> Wait[AppDeployment]:
1140
+ def start(self, name: str) -> Wait[App]:
898
1141
  """Start an app.
899
1142
 
900
1143
  Start the last active deployment of the app in the workspace.
@@ -903,22 +1146,19 @@ class AppsAPI:
903
1146
  The name of the app.
904
1147
 
905
1148
  :returns:
906
- Long-running operation waiter for :class:`AppDeployment`.
907
- See :method:wait_get_deployment_app_succeeded for more details.
1149
+ Long-running operation waiter for :class:`App`.
1150
+ See :method:wait_get_app_active for more details.
908
1151
  """
909
1152
 
910
1153
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
911
1154
 
912
- op_response = self._api.do('POST', f'/api/2.0/preview/apps/{name}/start', headers=headers)
913
- return Wait(self.wait_get_deployment_app_succeeded,
914
- response=AppDeployment.from_dict(op_response),
915
- app_name=name,
916
- deployment_id=op_response['deployment_id'])
1155
+ op_response = self._api.do('POST', f'/api/2.0/apps/{name}/start', headers=headers)
1156
+ return Wait(self.wait_get_app_active, response=App.from_dict(op_response), name=op_response['name'])
917
1157
 
918
- def start_and_wait(self, name: str, timeout=timedelta(minutes=20)) -> AppDeployment:
1158
+ def start_and_wait(self, name: str, timeout=timedelta(minutes=20)) -> App:
919
1159
  return self.start(name=name).result(timeout=timeout)
920
1160
 
921
- def stop(self, name: str):
1161
+ def stop(self, name: str) -> Wait[App]:
922
1162
  """Stop an app.
923
1163
 
924
1164
  Stops the active deployment of the app in the workspace.
@@ -926,14 +1166,24 @@ class AppsAPI:
926
1166
  :param name: str
927
1167
  The name of the app.
928
1168
 
929
-
1169
+ :returns:
1170
+ Long-running operation waiter for :class:`App`.
1171
+ See :method:wait_get_app_stopped for more details.
930
1172
  """
931
1173
 
932
1174
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
933
1175
 
934
- self._api.do('POST', f'/api/2.0/preview/apps/{name}/stop', headers=headers)
1176
+ op_response = self._api.do('POST', f'/api/2.0/apps/{name}/stop', headers=headers)
1177
+ return Wait(self.wait_get_app_stopped, response=App.from_dict(op_response), name=op_response['name'])
1178
+
1179
+ def stop_and_wait(self, name: str, timeout=timedelta(minutes=20)) -> App:
1180
+ return self.stop(name=name).result(timeout=timeout)
935
1181
 
936
- def update(self, name: str, *, description: Optional[str] = None) -> App:
1182
+ def update(self,
1183
+ name: str,
1184
+ *,
1185
+ description: Optional[str] = None,
1186
+ resources: Optional[List[AppResource]] = None) -> App:
937
1187
  """Update an app.
938
1188
 
939
1189
  Updates the app with the supplied name.
@@ -943,14 +1193,17 @@ class AppsAPI:
943
1193
  must be unique within the workspace.
944
1194
  :param description: str (optional)
945
1195
  The description of the app.
1196
+ :param resources: List[:class:`AppResource`] (optional)
1197
+ Resources for the app.
946
1198
 
947
1199
  :returns: :class:`App`
948
1200
  """
949
1201
  body = {}
950
1202
  if description is not None: body['description'] = description
1203
+ if resources is not None: body['resources'] = [v.as_dict() for v in resources]
951
1204
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
952
1205
 
953
- res = self._api.do('PATCH', f'/api/2.0/preview/apps/{name}', body=body, headers=headers)
1206
+ res = self._api.do('PATCH', f'/api/2.0/apps/{name}', body=body, headers=headers)
954
1207
  return App.from_dict(res)
955
1208
 
956
1209
  def update_permissions(