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

Files changed (31) hide show
  1. databricks/sdk/__init__.py +74 -22
  2. databricks/sdk/config.py +89 -48
  3. databricks/sdk/core.py +38 -9
  4. databricks/sdk/credentials_provider.py +134 -57
  5. databricks/sdk/data_plane.py +65 -0
  6. databricks/sdk/dbutils.py +81 -3
  7. databricks/sdk/mixins/files.py +12 -4
  8. databricks/sdk/oauth.py +8 -6
  9. databricks/sdk/service/apps.py +977 -0
  10. databricks/sdk/service/billing.py +602 -218
  11. databricks/sdk/service/catalog.py +263 -62
  12. databricks/sdk/service/compute.py +515 -94
  13. databricks/sdk/service/dashboards.py +1310 -2
  14. databricks/sdk/service/iam.py +99 -88
  15. databricks/sdk/service/jobs.py +159 -166
  16. databricks/sdk/service/marketplace.py +74 -58
  17. databricks/sdk/service/oauth2.py +149 -70
  18. databricks/sdk/service/pipelines.py +73 -53
  19. databricks/sdk/service/serving.py +332 -694
  20. databricks/sdk/service/settings.py +424 -4
  21. databricks/sdk/service/sharing.py +235 -26
  22. databricks/sdk/service/sql.py +2484 -553
  23. databricks/sdk/service/vectorsearch.py +75 -0
  24. databricks/sdk/useragent.py +144 -0
  25. databricks/sdk/version.py +1 -1
  26. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/METADATA +37 -16
  27. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/RECORD +31 -28
  28. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/WHEEL +1 -1
  29. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/LICENSE +0 -0
  30. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/NOTICE +0 -0
  31. {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/top_level.txt +0 -0
@@ -3,14 +3,20 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
+ import random
7
+ import time
6
8
  from dataclasses import dataclass
9
+ from datetime import timedelta
7
10
  from enum import Enum
8
- from typing import Dict, Optional
11
+ from typing import Callable, Dict, Iterator, List, Optional
9
12
 
10
- from ._internal import _enum
13
+ from ..errors import OperationFailed
14
+ from ._internal import Wait, _enum, _from_dict, _repeated_dict
11
15
 
12
16
  _LOG = logging.getLogger('databricks.sdk')
13
17
 
18
+ from databricks.sdk.service import sql
19
+
14
20
  # all definitions in this file are in alphabetical order
15
21
 
16
22
 
@@ -47,6 +53,94 @@ class CreateDashboardRequest:
47
53
  warehouse_id=d.get('warehouse_id', None))
48
54
 
49
55
 
56
+ @dataclass
57
+ class CreateScheduleRequest:
58
+ cron_schedule: CronSchedule
59
+ """The cron expression describing the frequency of the periodic refresh for this schedule."""
60
+
61
+ dashboard_id: Optional[str] = None
62
+ """UUID identifying the dashboard to which the schedule belongs."""
63
+
64
+ display_name: Optional[str] = None
65
+ """The display name for schedule."""
66
+
67
+ pause_status: Optional[SchedulePauseStatus] = None
68
+ """The status indicates whether this schedule is paused or not."""
69
+
70
+ def as_dict(self) -> dict:
71
+ """Serializes the CreateScheduleRequest into a dictionary suitable for use as a JSON request body."""
72
+ body = {}
73
+ if self.cron_schedule: body['cron_schedule'] = self.cron_schedule.as_dict()
74
+ if self.dashboard_id is not None: body['dashboard_id'] = self.dashboard_id
75
+ if self.display_name is not None: body['display_name'] = self.display_name
76
+ if self.pause_status is not None: body['pause_status'] = self.pause_status.value
77
+ return body
78
+
79
+ @classmethod
80
+ def from_dict(cls, d: Dict[str, any]) -> CreateScheduleRequest:
81
+ """Deserializes the CreateScheduleRequest from a dictionary."""
82
+ return cls(cron_schedule=_from_dict(d, 'cron_schedule', CronSchedule),
83
+ dashboard_id=d.get('dashboard_id', None),
84
+ display_name=d.get('display_name', None),
85
+ pause_status=_enum(d, 'pause_status', SchedulePauseStatus))
86
+
87
+
88
+ @dataclass
89
+ class CreateSubscriptionRequest:
90
+ subscriber: Subscriber
91
+ """Subscriber details for users and destinations to be added as subscribers to the schedule."""
92
+
93
+ dashboard_id: Optional[str] = None
94
+ """UUID identifying the dashboard to which the subscription belongs."""
95
+
96
+ schedule_id: Optional[str] = None
97
+ """UUID identifying the schedule to which the subscription belongs."""
98
+
99
+ def as_dict(self) -> dict:
100
+ """Serializes the CreateSubscriptionRequest into a dictionary suitable for use as a JSON request body."""
101
+ body = {}
102
+ if self.dashboard_id is not None: body['dashboard_id'] = self.dashboard_id
103
+ if self.schedule_id is not None: body['schedule_id'] = self.schedule_id
104
+ if self.subscriber: body['subscriber'] = self.subscriber.as_dict()
105
+ return body
106
+
107
+ @classmethod
108
+ def from_dict(cls, d: Dict[str, any]) -> CreateSubscriptionRequest:
109
+ """Deserializes the CreateSubscriptionRequest from a dictionary."""
110
+ return cls(dashboard_id=d.get('dashboard_id', None),
111
+ schedule_id=d.get('schedule_id', None),
112
+ subscriber=_from_dict(d, 'subscriber', Subscriber))
113
+
114
+
115
+ @dataclass
116
+ class CronSchedule:
117
+ quartz_cron_expression: str
118
+ """A cron expression using quartz syntax. EX: `0 0 8 * * ?` represents everyday at 8am. See [Cron
119
+ Trigger] for details.
120
+
121
+ [Cron Trigger]: http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html"""
122
+
123
+ timezone_id: str
124
+ """A Java timezone id. The schedule will be resolved with respect to this timezone. See [Java
125
+ TimeZone] for details.
126
+
127
+ [Java TimeZone]: https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html"""
128
+
129
+ def as_dict(self) -> dict:
130
+ """Serializes the CronSchedule into a dictionary suitable for use as a JSON request body."""
131
+ body = {}
132
+ if self.quartz_cron_expression is not None:
133
+ body['quartz_cron_expression'] = self.quartz_cron_expression
134
+ if self.timezone_id is not None: body['timezone_id'] = self.timezone_id
135
+ return body
136
+
137
+ @classmethod
138
+ def from_dict(cls, d: Dict[str, any]) -> CronSchedule:
139
+ """Deserializes the CronSchedule from a dictionary."""
140
+ return cls(quartz_cron_expression=d.get('quartz_cron_expression', None),
141
+ timezone_id=d.get('timezone_id', None))
142
+
143
+
50
144
  @dataclass
51
145
  class Dashboard:
52
146
  create_time: Optional[str] = None
@@ -111,12 +205,433 @@ class Dashboard:
111
205
  warehouse_id=d.get('warehouse_id', None))
112
206
 
113
207
 
208
+ class DashboardView(Enum):
209
+
210
+ DASHBOARD_VIEW_BASIC = 'DASHBOARD_VIEW_BASIC'
211
+
212
+
213
+ @dataclass
214
+ class DeleteScheduleResponse:
215
+
216
+ def as_dict(self) -> dict:
217
+ """Serializes the DeleteScheduleResponse into a dictionary suitable for use as a JSON request body."""
218
+ body = {}
219
+ return body
220
+
221
+ @classmethod
222
+ def from_dict(cls, d: Dict[str, any]) -> DeleteScheduleResponse:
223
+ """Deserializes the DeleteScheduleResponse from a dictionary."""
224
+ return cls()
225
+
226
+
227
+ @dataclass
228
+ class DeleteSubscriptionResponse:
229
+
230
+ def as_dict(self) -> dict:
231
+ """Serializes the DeleteSubscriptionResponse into a dictionary suitable for use as a JSON request body."""
232
+ body = {}
233
+ return body
234
+
235
+ @classmethod
236
+ def from_dict(cls, d: Dict[str, any]) -> DeleteSubscriptionResponse:
237
+ """Deserializes the DeleteSubscriptionResponse from a dictionary."""
238
+ return cls()
239
+
240
+
241
+ @dataclass
242
+ class GenieAttachment:
243
+ """Genie AI Response"""
244
+
245
+ query: Optional[QueryAttachment] = None
246
+
247
+ text: Optional[TextAttachment] = None
248
+
249
+ def as_dict(self) -> dict:
250
+ """Serializes the GenieAttachment into a dictionary suitable for use as a JSON request body."""
251
+ body = {}
252
+ if self.query: body['query'] = self.query.as_dict()
253
+ if self.text: body['text'] = self.text.as_dict()
254
+ return body
255
+
256
+ @classmethod
257
+ def from_dict(cls, d: Dict[str, any]) -> GenieAttachment:
258
+ """Deserializes the GenieAttachment from a dictionary."""
259
+ return cls(query=_from_dict(d, 'query', QueryAttachment), text=_from_dict(d, 'text', TextAttachment))
260
+
261
+
262
+ @dataclass
263
+ class GenieConversation:
264
+ id: str
265
+ """Conversation ID"""
266
+
267
+ space_id: str
268
+ """Genie space ID"""
269
+
270
+ user_id: int
271
+ """ID of the user who created the conversation"""
272
+
273
+ title: str
274
+ """Conversation title"""
275
+
276
+ created_timestamp: Optional[int] = None
277
+ """Timestamp when the message was created"""
278
+
279
+ last_updated_timestamp: Optional[int] = None
280
+ """Timestamp when the message was last updated"""
281
+
282
+ def as_dict(self) -> dict:
283
+ """Serializes the GenieConversation into a dictionary suitable for use as a JSON request body."""
284
+ body = {}
285
+ if self.created_timestamp is not None: body['created_timestamp'] = self.created_timestamp
286
+ if self.id is not None: body['id'] = self.id
287
+ if self.last_updated_timestamp is not None:
288
+ body['last_updated_timestamp'] = self.last_updated_timestamp
289
+ if self.space_id is not None: body['space_id'] = self.space_id
290
+ if self.title is not None: body['title'] = self.title
291
+ if self.user_id is not None: body['user_id'] = self.user_id
292
+ return body
293
+
294
+ @classmethod
295
+ def from_dict(cls, d: Dict[str, any]) -> GenieConversation:
296
+ """Deserializes the GenieConversation from a dictionary."""
297
+ return cls(created_timestamp=d.get('created_timestamp', None),
298
+ id=d.get('id', None),
299
+ last_updated_timestamp=d.get('last_updated_timestamp', None),
300
+ space_id=d.get('space_id', None),
301
+ title=d.get('title', None),
302
+ user_id=d.get('user_id', None))
303
+
304
+
305
+ @dataclass
306
+ class GenieCreateConversationMessageRequest:
307
+ content: str
308
+ """User message content."""
309
+
310
+ conversation_id: Optional[str] = None
311
+ """The ID associated with the conversation."""
312
+
313
+ space_id: Optional[str] = None
314
+ """The ID associated with the Genie space where the conversation is started."""
315
+
316
+ def as_dict(self) -> dict:
317
+ """Serializes the GenieCreateConversationMessageRequest into a dictionary suitable for use as a JSON request body."""
318
+ body = {}
319
+ if self.content is not None: body['content'] = self.content
320
+ if self.conversation_id is not None: body['conversation_id'] = self.conversation_id
321
+ if self.space_id is not None: body['space_id'] = self.space_id
322
+ return body
323
+
324
+ @classmethod
325
+ def from_dict(cls, d: Dict[str, any]) -> GenieCreateConversationMessageRequest:
326
+ """Deserializes the GenieCreateConversationMessageRequest from a dictionary."""
327
+ return cls(content=d.get('content', None),
328
+ conversation_id=d.get('conversation_id', None),
329
+ space_id=d.get('space_id', None))
330
+
331
+
332
+ @dataclass
333
+ class GenieGetMessageQueryResultResponse:
334
+ statement_response: Optional[sql.StatementResponse] = None
335
+ """SQL Statement Execution response. See [Get status, manifest, and result first
336
+ chunk](:method:statementexecution/getstatement) for more details."""
337
+
338
+ def as_dict(self) -> dict:
339
+ """Serializes the GenieGetMessageQueryResultResponse into a dictionary suitable for use as a JSON request body."""
340
+ body = {}
341
+ if self.statement_response: body['statement_response'] = self.statement_response.as_dict()
342
+ return body
343
+
344
+ @classmethod
345
+ def from_dict(cls, d: Dict[str, any]) -> GenieGetMessageQueryResultResponse:
346
+ """Deserializes the GenieGetMessageQueryResultResponse from a dictionary."""
347
+ return cls(statement_response=_from_dict(d, 'statement_response', sql.StatementResponse))
348
+
349
+
350
+ @dataclass
351
+ class GenieMessage:
352
+ id: str
353
+ """Message ID"""
354
+
355
+ space_id: str
356
+ """Genie space ID"""
357
+
358
+ conversation_id: str
359
+ """Conversation ID"""
360
+
361
+ content: str
362
+ """User message content"""
363
+
364
+ attachments: Optional[List[GenieAttachment]] = None
365
+ """AI produced response to the message"""
366
+
367
+ created_timestamp: Optional[int] = None
368
+ """Timestamp when the message was created"""
369
+
370
+ error: Optional[MessageError] = None
371
+ """Error message if AI failed to respond to the message"""
372
+
373
+ last_updated_timestamp: Optional[int] = None
374
+ """Timestamp when the message was last updated"""
375
+
376
+ query_result: Optional[Result] = None
377
+ """The result of SQL query if the message has a query attachment"""
378
+
379
+ status: Optional[MessageStatus] = None
380
+ """MesssageStatus. The possible values are: * `FETCHING_METADATA`: Fetching metadata from the data
381
+ sources. * `ASKING_AI`: Waiting for the LLM to respond to the users question. *
382
+ `EXECUTING_QUERY`: Executing AI provided SQL query. Get the SQL query result by calling
383
+ [getMessageQueryResult](:method:genie/getMessageQueryResult) API. **Important: The message
384
+ status will stay in the `EXECUTING_QUERY` until a client calls
385
+ [getMessageQueryResult](:method:genie/getMessageQueryResult)**. * `FAILED`: Generating a
386
+ response or the executing the query failed. Please see `error` field. * `COMPLETED`: Message
387
+ processing is completed. Results are in the `attachments` field. Get the SQL query result by
388
+ calling [getMessageQueryResult](:method:genie/getMessageQueryResult) API. * `SUBMITTED`: Message
389
+ has been submitted. * `QUERY_RESULT_EXPIRED`: SQL result is not available anymore. The user
390
+ needs to execute the query again. * `CANCELLED`: Message has been cancelled."""
391
+
392
+ user_id: Optional[int] = None
393
+ """ID of the user who created the message"""
394
+
395
+ def as_dict(self) -> dict:
396
+ """Serializes the GenieMessage into a dictionary suitable for use as a JSON request body."""
397
+ body = {}
398
+ if self.attachments: body['attachments'] = [v.as_dict() for v in self.attachments]
399
+ if self.content is not None: body['content'] = self.content
400
+ if self.conversation_id is not None: body['conversation_id'] = self.conversation_id
401
+ if self.created_timestamp is not None: body['created_timestamp'] = self.created_timestamp
402
+ if self.error: body['error'] = self.error.as_dict()
403
+ if self.id is not None: body['id'] = self.id
404
+ if self.last_updated_timestamp is not None:
405
+ body['last_updated_timestamp'] = self.last_updated_timestamp
406
+ if self.query_result: body['query_result'] = self.query_result.as_dict()
407
+ if self.space_id is not None: body['space_id'] = self.space_id
408
+ if self.status is not None: body['status'] = self.status.value
409
+ if self.user_id is not None: body['user_id'] = self.user_id
410
+ return body
411
+
412
+ @classmethod
413
+ def from_dict(cls, d: Dict[str, any]) -> GenieMessage:
414
+ """Deserializes the GenieMessage from a dictionary."""
415
+ return cls(attachments=_repeated_dict(d, 'attachments', GenieAttachment),
416
+ content=d.get('content', None),
417
+ conversation_id=d.get('conversation_id', None),
418
+ created_timestamp=d.get('created_timestamp', None),
419
+ error=_from_dict(d, 'error', MessageError),
420
+ id=d.get('id', None),
421
+ last_updated_timestamp=d.get('last_updated_timestamp', None),
422
+ query_result=_from_dict(d, 'query_result', Result),
423
+ space_id=d.get('space_id', None),
424
+ status=_enum(d, 'status', MessageStatus),
425
+ user_id=d.get('user_id', None))
426
+
427
+
428
+ @dataclass
429
+ class GenieStartConversationMessageRequest:
430
+ content: str
431
+ """The text of the message that starts the conversation."""
432
+
433
+ space_id: Optional[str] = None
434
+ """The ID associated with the Genie space where you want to start a conversation."""
435
+
436
+ def as_dict(self) -> dict:
437
+ """Serializes the GenieStartConversationMessageRequest into a dictionary suitable for use as a JSON request body."""
438
+ body = {}
439
+ if self.content is not None: body['content'] = self.content
440
+ if self.space_id is not None: body['space_id'] = self.space_id
441
+ return body
442
+
443
+ @classmethod
444
+ def from_dict(cls, d: Dict[str, any]) -> GenieStartConversationMessageRequest:
445
+ """Deserializes the GenieStartConversationMessageRequest from a dictionary."""
446
+ return cls(content=d.get('content', None), space_id=d.get('space_id', None))
447
+
448
+
449
+ @dataclass
450
+ class GenieStartConversationResponse:
451
+ message_id: str
452
+ """Message ID"""
453
+
454
+ conversation_id: str
455
+ """Conversation ID"""
456
+
457
+ conversation: Optional[GenieConversation] = None
458
+
459
+ message: Optional[GenieMessage] = None
460
+
461
+ def as_dict(self) -> dict:
462
+ """Serializes the GenieStartConversationResponse into a dictionary suitable for use as a JSON request body."""
463
+ body = {}
464
+ if self.conversation: body['conversation'] = self.conversation.as_dict()
465
+ if self.conversation_id is not None: body['conversation_id'] = self.conversation_id
466
+ if self.message: body['message'] = self.message.as_dict()
467
+ if self.message_id is not None: body['message_id'] = self.message_id
468
+ return body
469
+
470
+ @classmethod
471
+ def from_dict(cls, d: Dict[str, any]) -> GenieStartConversationResponse:
472
+ """Deserializes the GenieStartConversationResponse from a dictionary."""
473
+ return cls(conversation=_from_dict(d, 'conversation', GenieConversation),
474
+ conversation_id=d.get('conversation_id', None),
475
+ message=_from_dict(d, 'message', GenieMessage),
476
+ message_id=d.get('message_id', None))
477
+
478
+
114
479
  class LifecycleState(Enum):
115
480
 
116
481
  ACTIVE = 'ACTIVE'
117
482
  TRASHED = 'TRASHED'
118
483
 
119
484
 
485
+ @dataclass
486
+ class ListDashboardsResponse:
487
+ dashboards: Optional[List[Dashboard]] = None
488
+
489
+ next_page_token: Optional[str] = None
490
+ """A token, which can be sent as `page_token` to retrieve the next page. If this field is omitted,
491
+ there are no subsequent dashboards."""
492
+
493
+ def as_dict(self) -> dict:
494
+ """Serializes the ListDashboardsResponse into a dictionary suitable for use as a JSON request body."""
495
+ body = {}
496
+ if self.dashboards: body['dashboards'] = [v.as_dict() for v in self.dashboards]
497
+ if self.next_page_token is not None: body['next_page_token'] = self.next_page_token
498
+ return body
499
+
500
+ @classmethod
501
+ def from_dict(cls, d: Dict[str, any]) -> ListDashboardsResponse:
502
+ """Deserializes the ListDashboardsResponse from a dictionary."""
503
+ return cls(dashboards=_repeated_dict(d, 'dashboards', Dashboard),
504
+ next_page_token=d.get('next_page_token', None))
505
+
506
+
507
+ @dataclass
508
+ class ListSchedulesResponse:
509
+ next_page_token: Optional[str] = None
510
+ """A token that can be used as a `page_token` in subsequent requests to retrieve the next page of
511
+ results. If this field is omitted, there are no subsequent schedules."""
512
+
513
+ schedules: Optional[List[Schedule]] = None
514
+
515
+ def as_dict(self) -> dict:
516
+ """Serializes the ListSchedulesResponse into a dictionary suitable for use as a JSON request body."""
517
+ body = {}
518
+ if self.next_page_token is not None: body['next_page_token'] = self.next_page_token
519
+ if self.schedules: body['schedules'] = [v.as_dict() for v in self.schedules]
520
+ return body
521
+
522
+ @classmethod
523
+ def from_dict(cls, d: Dict[str, any]) -> ListSchedulesResponse:
524
+ """Deserializes the ListSchedulesResponse from a dictionary."""
525
+ return cls(next_page_token=d.get('next_page_token', None),
526
+ schedules=_repeated_dict(d, 'schedules', Schedule))
527
+
528
+
529
+ @dataclass
530
+ class ListSubscriptionsResponse:
531
+ next_page_token: Optional[str] = None
532
+ """A token that can be used as a `page_token` in subsequent requests to retrieve the next page of
533
+ results. If this field is omitted, there are no subsequent subscriptions."""
534
+
535
+ subscriptions: Optional[List[Subscription]] = None
536
+
537
+ def as_dict(self) -> dict:
538
+ """Serializes the ListSubscriptionsResponse into a dictionary suitable for use as a JSON request body."""
539
+ body = {}
540
+ if self.next_page_token is not None: body['next_page_token'] = self.next_page_token
541
+ if self.subscriptions: body['subscriptions'] = [v.as_dict() for v in self.subscriptions]
542
+ return body
543
+
544
+ @classmethod
545
+ def from_dict(cls, d: Dict[str, any]) -> ListSubscriptionsResponse:
546
+ """Deserializes the ListSubscriptionsResponse from a dictionary."""
547
+ return cls(next_page_token=d.get('next_page_token', None),
548
+ subscriptions=_repeated_dict(d, 'subscriptions', Subscription))
549
+
550
+
551
+ @dataclass
552
+ class MessageError:
553
+ error: Optional[str] = None
554
+
555
+ type: Optional[MessageErrorType] = None
556
+
557
+ def as_dict(self) -> dict:
558
+ """Serializes the MessageError into a dictionary suitable for use as a JSON request body."""
559
+ body = {}
560
+ if self.error is not None: body['error'] = self.error
561
+ if self.type is not None: body['type'] = self.type.value
562
+ return body
563
+
564
+ @classmethod
565
+ def from_dict(cls, d: Dict[str, any]) -> MessageError:
566
+ """Deserializes the MessageError from a dictionary."""
567
+ return cls(error=d.get('error', None), type=_enum(d, 'type', MessageErrorType))
568
+
569
+
570
+ class MessageErrorType(Enum):
571
+
572
+ BLOCK_MULTIPLE_EXECUTIONS_EXCEPTION = 'BLOCK_MULTIPLE_EXECUTIONS_EXCEPTION'
573
+ CHAT_COMPLETION_CLIENT_EXCEPTION = 'CHAT_COMPLETION_CLIENT_EXCEPTION'
574
+ CHAT_COMPLETION_CLIENT_TIMEOUT_EXCEPTION = 'CHAT_COMPLETION_CLIENT_TIMEOUT_EXCEPTION'
575
+ CHAT_COMPLETION_NETWORK_EXCEPTION = 'CHAT_COMPLETION_NETWORK_EXCEPTION'
576
+ CONTENT_FILTER_EXCEPTION = 'CONTENT_FILTER_EXCEPTION'
577
+ CONTEXT_EXCEEDED_EXCEPTION = 'CONTEXT_EXCEEDED_EXCEPTION'
578
+ COULD_NOT_GET_UC_SCHEMA_EXCEPTION = 'COULD_NOT_GET_UC_SCHEMA_EXCEPTION'
579
+ DEPLOYMENT_NOT_FOUND_EXCEPTION = 'DEPLOYMENT_NOT_FOUND_EXCEPTION'
580
+ FUNCTIONS_NOT_AVAILABLE_EXCEPTION = 'FUNCTIONS_NOT_AVAILABLE_EXCEPTION'
581
+ FUNCTION_ARGUMENTS_INVALID_EXCEPTION = 'FUNCTION_ARGUMENTS_INVALID_EXCEPTION'
582
+ FUNCTION_ARGUMENTS_INVALID_JSON_EXCEPTION = 'FUNCTION_ARGUMENTS_INVALID_JSON_EXCEPTION'
583
+ FUNCTION_CALL_MISSING_PARAMETER_EXCEPTION = 'FUNCTION_CALL_MISSING_PARAMETER_EXCEPTION'
584
+ GENERIC_CHAT_COMPLETION_EXCEPTION = 'GENERIC_CHAT_COMPLETION_EXCEPTION'
585
+ GENERIC_CHAT_COMPLETION_SERVICE_EXCEPTION = 'GENERIC_CHAT_COMPLETION_SERVICE_EXCEPTION'
586
+ GENERIC_SQL_EXEC_API_CALL_EXCEPTION = 'GENERIC_SQL_EXEC_API_CALL_EXCEPTION'
587
+ ILLEGAL_PARAMETER_DEFINITION_EXCEPTION = 'ILLEGAL_PARAMETER_DEFINITION_EXCEPTION'
588
+ INVALID_CERTIFIED_ANSWER_FUNCTION_EXCEPTION = 'INVALID_CERTIFIED_ANSWER_FUNCTION_EXCEPTION'
589
+ INVALID_CERTIFIED_ANSWER_IDENTIFIER_EXCEPTION = 'INVALID_CERTIFIED_ANSWER_IDENTIFIER_EXCEPTION'
590
+ INVALID_CHAT_COMPLETION_JSON_EXCEPTION = 'INVALID_CHAT_COMPLETION_JSON_EXCEPTION'
591
+ INVALID_COMPLETION_REQUEST_EXCEPTION = 'INVALID_COMPLETION_REQUEST_EXCEPTION'
592
+ INVALID_FUNCTION_CALL_EXCEPTION = 'INVALID_FUNCTION_CALL_EXCEPTION'
593
+ INVALID_TABLE_IDENTIFIER_EXCEPTION = 'INVALID_TABLE_IDENTIFIER_EXCEPTION'
594
+ LOCAL_CONTEXT_EXCEEDED_EXCEPTION = 'LOCAL_CONTEXT_EXCEEDED_EXCEPTION'
595
+ MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION = 'MESSAGE_DELETED_WHILE_EXECUTING_EXCEPTION'
596
+ MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION = 'MESSAGE_UPDATED_WHILE_EXECUTING_EXCEPTION'
597
+ NO_TABLES_TO_QUERY_EXCEPTION = 'NO_TABLES_TO_QUERY_EXCEPTION'
598
+ RATE_LIMIT_EXCEEDED_GENERIC_EXCEPTION = 'RATE_LIMIT_EXCEEDED_GENERIC_EXCEPTION'
599
+ RATE_LIMIT_EXCEEDED_SPECIFIED_WAIT_EXCEPTION = 'RATE_LIMIT_EXCEEDED_SPECIFIED_WAIT_EXCEPTION'
600
+ REPLY_PROCESS_TIMEOUT_EXCEPTION = 'REPLY_PROCESS_TIMEOUT_EXCEPTION'
601
+ RETRYABLE_PROCESSING_EXCEPTION = 'RETRYABLE_PROCESSING_EXCEPTION'
602
+ SQL_EXECUTION_EXCEPTION = 'SQL_EXECUTION_EXCEPTION'
603
+ TABLES_MISSING_EXCEPTION = 'TABLES_MISSING_EXCEPTION'
604
+ TOO_MANY_CERTIFIED_ANSWERS_EXCEPTION = 'TOO_MANY_CERTIFIED_ANSWERS_EXCEPTION'
605
+ TOO_MANY_TABLES_EXCEPTION = 'TOO_MANY_TABLES_EXCEPTION'
606
+ UNEXPECTED_REPLY_PROCESS_EXCEPTION = 'UNEXPECTED_REPLY_PROCESS_EXCEPTION'
607
+ UNKNOWN_AI_MODEL = 'UNKNOWN_AI_MODEL'
608
+ WAREHOUSE_ACCESS_MISSING_EXCEPTION = 'WAREHOUSE_ACCESS_MISSING_EXCEPTION'
609
+ WAREHOUSE_NOT_FOUND_EXCEPTION = 'WAREHOUSE_NOT_FOUND_EXCEPTION'
610
+
611
+
612
+ class MessageStatus(Enum):
613
+ """MesssageStatus. The possible values are: * `FETCHING_METADATA`: Fetching metadata from the data
614
+ sources. * `ASKING_AI`: Waiting for the LLM to respond to the users question. *
615
+ `EXECUTING_QUERY`: Executing AI provided SQL query. Get the SQL query result by calling
616
+ [getMessageQueryResult](:method:genie/getMessageQueryResult) API. **Important: The message
617
+ status will stay in the `EXECUTING_QUERY` until a client calls
618
+ [getMessageQueryResult](:method:genie/getMessageQueryResult)**. * `FAILED`: Generating a
619
+ response or the executing the query failed. Please see `error` field. * `COMPLETED`: Message
620
+ processing is completed. Results are in the `attachments` field. Get the SQL query result by
621
+ calling [getMessageQueryResult](:method:genie/getMessageQueryResult) API. * `SUBMITTED`: Message
622
+ has been submitted. * `QUERY_RESULT_EXPIRED`: SQL result is not available anymore. The user
623
+ needs to execute the query again. * `CANCELLED`: Message has been cancelled."""
624
+
625
+ ASKING_AI = 'ASKING_AI'
626
+ CANCELLED = 'CANCELLED'
627
+ COMPLETED = 'COMPLETED'
628
+ EXECUTING_QUERY = 'EXECUTING_QUERY'
629
+ FAILED = 'FAILED'
630
+ FETCHING_METADATA = 'FETCHING_METADATA'
631
+ QUERY_RESULT_EXPIRED = 'QUERY_RESULT_EXPIRED'
632
+ SUBMITTED = 'SUBMITTED'
633
+
634
+
120
635
  @dataclass
121
636
  class MigrateDashboardRequest:
122
637
  source_dashboard_id: str
@@ -204,6 +719,269 @@ class PublishedDashboard:
204
719
  warehouse_id=d.get('warehouse_id', None))
205
720
 
206
721
 
722
+ @dataclass
723
+ class QueryAttachment:
724
+ description: Optional[str] = None
725
+ """Description of the query"""
726
+
727
+ id: Optional[str] = None
728
+
729
+ instruction_id: Optional[str] = None
730
+ """If the query was created on an instruction (trusted asset) we link to the id"""
731
+
732
+ instruction_title: Optional[str] = None
733
+ """Always store the title next to the id in case the original instruction title changes or the
734
+ instruction is deleted."""
735
+
736
+ last_updated_timestamp: Optional[int] = None
737
+ """Time when the user updated the query last"""
738
+
739
+ query: Optional[str] = None
740
+ """AI generated SQL query"""
741
+
742
+ title: Optional[str] = None
743
+ """Name of the query"""
744
+
745
+ def as_dict(self) -> dict:
746
+ """Serializes the QueryAttachment into a dictionary suitable for use as a JSON request body."""
747
+ body = {}
748
+ if self.description is not None: body['description'] = self.description
749
+ if self.id is not None: body['id'] = self.id
750
+ if self.instruction_id is not None: body['instruction_id'] = self.instruction_id
751
+ if self.instruction_title is not None: body['instruction_title'] = self.instruction_title
752
+ if self.last_updated_timestamp is not None:
753
+ body['last_updated_timestamp'] = self.last_updated_timestamp
754
+ if self.query is not None: body['query'] = self.query
755
+ if self.title is not None: body['title'] = self.title
756
+ return body
757
+
758
+ @classmethod
759
+ def from_dict(cls, d: Dict[str, any]) -> QueryAttachment:
760
+ """Deserializes the QueryAttachment from a dictionary."""
761
+ return cls(description=d.get('description', None),
762
+ id=d.get('id', None),
763
+ instruction_id=d.get('instruction_id', None),
764
+ instruction_title=d.get('instruction_title', None),
765
+ last_updated_timestamp=d.get('last_updated_timestamp', None),
766
+ query=d.get('query', None),
767
+ title=d.get('title', None))
768
+
769
+
770
+ @dataclass
771
+ class Result:
772
+ row_count: Optional[int] = None
773
+ """Row count of the result"""
774
+
775
+ statement_id: Optional[str] = None
776
+ """Statement Execution API statement id. Use [Get status, manifest, and result first
777
+ chunk](:method:statementexecution/getstatement) to get the full result data."""
778
+
779
+ def as_dict(self) -> dict:
780
+ """Serializes the Result into a dictionary suitable for use as a JSON request body."""
781
+ body = {}
782
+ if self.row_count is not None: body['row_count'] = self.row_count
783
+ if self.statement_id is not None: body['statement_id'] = self.statement_id
784
+ return body
785
+
786
+ @classmethod
787
+ def from_dict(cls, d: Dict[str, any]) -> Result:
788
+ """Deserializes the Result from a dictionary."""
789
+ return cls(row_count=d.get('row_count', None), statement_id=d.get('statement_id', None))
790
+
791
+
792
+ @dataclass
793
+ class Schedule:
794
+ cron_schedule: CronSchedule
795
+ """The cron expression describing the frequency of the periodic refresh for this schedule."""
796
+
797
+ create_time: Optional[str] = None
798
+ """A timestamp indicating when the schedule was created."""
799
+
800
+ dashboard_id: Optional[str] = None
801
+ """UUID identifying the dashboard to which the schedule belongs."""
802
+
803
+ display_name: Optional[str] = None
804
+ """The display name for schedule."""
805
+
806
+ etag: Optional[str] = None
807
+ """The etag for the schedule. Must be left empty on create, must be provided on updates to ensure
808
+ that the schedule has not been modified since the last read, and can be optionally provided on
809
+ delete."""
810
+
811
+ pause_status: Optional[SchedulePauseStatus] = None
812
+ """The status indicates whether this schedule is paused or not."""
813
+
814
+ schedule_id: Optional[str] = None
815
+ """UUID identifying the schedule."""
816
+
817
+ update_time: Optional[str] = None
818
+ """A timestamp indicating when the schedule was last updated."""
819
+
820
+ def as_dict(self) -> dict:
821
+ """Serializes the Schedule into a dictionary suitable for use as a JSON request body."""
822
+ body = {}
823
+ if self.create_time is not None: body['create_time'] = self.create_time
824
+ if self.cron_schedule: body['cron_schedule'] = self.cron_schedule.as_dict()
825
+ if self.dashboard_id is not None: body['dashboard_id'] = self.dashboard_id
826
+ if self.display_name is not None: body['display_name'] = self.display_name
827
+ if self.etag is not None: body['etag'] = self.etag
828
+ if self.pause_status is not None: body['pause_status'] = self.pause_status.value
829
+ if self.schedule_id is not None: body['schedule_id'] = self.schedule_id
830
+ if self.update_time is not None: body['update_time'] = self.update_time
831
+ return body
832
+
833
+ @classmethod
834
+ def from_dict(cls, d: Dict[str, any]) -> Schedule:
835
+ """Deserializes the Schedule from a dictionary."""
836
+ return cls(create_time=d.get('create_time', None),
837
+ cron_schedule=_from_dict(d, 'cron_schedule', CronSchedule),
838
+ dashboard_id=d.get('dashboard_id', None),
839
+ display_name=d.get('display_name', None),
840
+ etag=d.get('etag', None),
841
+ pause_status=_enum(d, 'pause_status', SchedulePauseStatus),
842
+ schedule_id=d.get('schedule_id', None),
843
+ update_time=d.get('update_time', None))
844
+
845
+
846
+ class SchedulePauseStatus(Enum):
847
+
848
+ PAUSED = 'PAUSED'
849
+ UNPAUSED = 'UNPAUSED'
850
+
851
+
852
+ @dataclass
853
+ class Subscriber:
854
+ destination_subscriber: Optional[SubscriptionSubscriberDestination] = None
855
+ """The destination to receive the subscription email. This parameter is mutually exclusive with
856
+ `user_subscriber`."""
857
+
858
+ user_subscriber: Optional[SubscriptionSubscriberUser] = None
859
+ """The user to receive the subscription email. This parameter is mutually exclusive with
860
+ `destination_subscriber`."""
861
+
862
+ def as_dict(self) -> dict:
863
+ """Serializes the Subscriber into a dictionary suitable for use as a JSON request body."""
864
+ body = {}
865
+ if self.destination_subscriber: body['destination_subscriber'] = self.destination_subscriber.as_dict()
866
+ if self.user_subscriber: body['user_subscriber'] = self.user_subscriber.as_dict()
867
+ return body
868
+
869
+ @classmethod
870
+ def from_dict(cls, d: Dict[str, any]) -> Subscriber:
871
+ """Deserializes the Subscriber from a dictionary."""
872
+ return cls(destination_subscriber=_from_dict(d, 'destination_subscriber',
873
+ SubscriptionSubscriberDestination),
874
+ user_subscriber=_from_dict(d, 'user_subscriber', SubscriptionSubscriberUser))
875
+
876
+
877
+ @dataclass
878
+ class Subscription:
879
+ subscriber: Subscriber
880
+ """Subscriber details for users and destinations to be added as subscribers to the schedule."""
881
+
882
+ create_time: Optional[str] = None
883
+ """A timestamp indicating when the subscription was created."""
884
+
885
+ created_by_user_id: Optional[int] = None
886
+ """UserId of the user who adds subscribers (users or notification destinations) to the dashboard's
887
+ schedule."""
888
+
889
+ dashboard_id: Optional[str] = None
890
+ """UUID identifying the dashboard to which the subscription belongs."""
891
+
892
+ etag: Optional[str] = None
893
+ """The etag for the subscription. Must be left empty on create, can be optionally provided on
894
+ delete to ensure that the subscription has not been deleted since the last read."""
895
+
896
+ schedule_id: Optional[str] = None
897
+ """UUID identifying the schedule to which the subscription belongs."""
898
+
899
+ subscription_id: Optional[str] = None
900
+ """UUID identifying the subscription."""
901
+
902
+ update_time: Optional[str] = None
903
+ """A timestamp indicating when the subscription was last updated."""
904
+
905
+ def as_dict(self) -> dict:
906
+ """Serializes the Subscription into a dictionary suitable for use as a JSON request body."""
907
+ body = {}
908
+ if self.create_time is not None: body['create_time'] = self.create_time
909
+ if self.created_by_user_id is not None: body['created_by_user_id'] = self.created_by_user_id
910
+ if self.dashboard_id is not None: body['dashboard_id'] = self.dashboard_id
911
+ if self.etag is not None: body['etag'] = self.etag
912
+ if self.schedule_id is not None: body['schedule_id'] = self.schedule_id
913
+ if self.subscriber: body['subscriber'] = self.subscriber.as_dict()
914
+ if self.subscription_id is not None: body['subscription_id'] = self.subscription_id
915
+ if self.update_time is not None: body['update_time'] = self.update_time
916
+ return body
917
+
918
+ @classmethod
919
+ def from_dict(cls, d: Dict[str, any]) -> Subscription:
920
+ """Deserializes the Subscription from a dictionary."""
921
+ return cls(create_time=d.get('create_time', None),
922
+ created_by_user_id=d.get('created_by_user_id', None),
923
+ dashboard_id=d.get('dashboard_id', None),
924
+ etag=d.get('etag', None),
925
+ schedule_id=d.get('schedule_id', None),
926
+ subscriber=_from_dict(d, 'subscriber', Subscriber),
927
+ subscription_id=d.get('subscription_id', None),
928
+ update_time=d.get('update_time', None))
929
+
930
+
931
+ @dataclass
932
+ class SubscriptionSubscriberDestination:
933
+ destination_id: str
934
+ """The canonical identifier of the destination to receive email notification."""
935
+
936
+ def as_dict(self) -> dict:
937
+ """Serializes the SubscriptionSubscriberDestination into a dictionary suitable for use as a JSON request body."""
938
+ body = {}
939
+ if self.destination_id is not None: body['destination_id'] = self.destination_id
940
+ return body
941
+
942
+ @classmethod
943
+ def from_dict(cls, d: Dict[str, any]) -> SubscriptionSubscriberDestination:
944
+ """Deserializes the SubscriptionSubscriberDestination from a dictionary."""
945
+ return cls(destination_id=d.get('destination_id', None))
946
+
947
+
948
+ @dataclass
949
+ class SubscriptionSubscriberUser:
950
+ user_id: int
951
+ """UserId of the subscriber."""
952
+
953
+ def as_dict(self) -> dict:
954
+ """Serializes the SubscriptionSubscriberUser into a dictionary suitable for use as a JSON request body."""
955
+ body = {}
956
+ if self.user_id is not None: body['user_id'] = self.user_id
957
+ return body
958
+
959
+ @classmethod
960
+ def from_dict(cls, d: Dict[str, any]) -> SubscriptionSubscriberUser:
961
+ """Deserializes the SubscriptionSubscriberUser from a dictionary."""
962
+ return cls(user_id=d.get('user_id', None))
963
+
964
+
965
+ @dataclass
966
+ class TextAttachment:
967
+ content: Optional[str] = None
968
+ """AI generated message"""
969
+
970
+ id: Optional[str] = None
971
+
972
+ def as_dict(self) -> dict:
973
+ """Serializes the TextAttachment into a dictionary suitable for use as a JSON request body."""
974
+ body = {}
975
+ if self.content is not None: body['content'] = self.content
976
+ if self.id is not None: body['id'] = self.id
977
+ return body
978
+
979
+ @classmethod
980
+ def from_dict(cls, d: Dict[str, any]) -> TextAttachment:
981
+ """Deserializes the TextAttachment from a dictionary."""
982
+ return cls(content=d.get('content', None), id=d.get('id', None))
983
+
984
+
207
985
  @dataclass
208
986
  class TrashDashboardResponse:
209
987
 
@@ -270,6 +1048,237 @@ class UpdateDashboardRequest:
270
1048
  warehouse_id=d.get('warehouse_id', None))
271
1049
 
272
1050
 
1051
+ @dataclass
1052
+ class UpdateScheduleRequest:
1053
+ cron_schedule: CronSchedule
1054
+ """The cron expression describing the frequency of the periodic refresh for this schedule."""
1055
+
1056
+ dashboard_id: Optional[str] = None
1057
+ """UUID identifying the dashboard to which the schedule belongs."""
1058
+
1059
+ display_name: Optional[str] = None
1060
+ """The display name for schedule."""
1061
+
1062
+ etag: Optional[str] = None
1063
+ """The etag for the schedule. Must be left empty on create, must be provided on updates to ensure
1064
+ that the schedule has not been modified since the last read, and can be optionally provided on
1065
+ delete."""
1066
+
1067
+ pause_status: Optional[SchedulePauseStatus] = None
1068
+ """The status indicates whether this schedule is paused or not."""
1069
+
1070
+ schedule_id: Optional[str] = None
1071
+ """UUID identifying the schedule."""
1072
+
1073
+ def as_dict(self) -> dict:
1074
+ """Serializes the UpdateScheduleRequest into a dictionary suitable for use as a JSON request body."""
1075
+ body = {}
1076
+ if self.cron_schedule: body['cron_schedule'] = self.cron_schedule.as_dict()
1077
+ if self.dashboard_id is not None: body['dashboard_id'] = self.dashboard_id
1078
+ if self.display_name is not None: body['display_name'] = self.display_name
1079
+ if self.etag is not None: body['etag'] = self.etag
1080
+ if self.pause_status is not None: body['pause_status'] = self.pause_status.value
1081
+ if self.schedule_id is not None: body['schedule_id'] = self.schedule_id
1082
+ return body
1083
+
1084
+ @classmethod
1085
+ def from_dict(cls, d: Dict[str, any]) -> UpdateScheduleRequest:
1086
+ """Deserializes the UpdateScheduleRequest from a dictionary."""
1087
+ return cls(cron_schedule=_from_dict(d, 'cron_schedule', CronSchedule),
1088
+ dashboard_id=d.get('dashboard_id', None),
1089
+ display_name=d.get('display_name', None),
1090
+ etag=d.get('etag', None),
1091
+ pause_status=_enum(d, 'pause_status', SchedulePauseStatus),
1092
+ schedule_id=d.get('schedule_id', None))
1093
+
1094
+
1095
+ class GenieAPI:
1096
+ """Genie provides a no-code experience for business users, powered by AI/BI. Analysts set up spaces that
1097
+ business users can use to ask questions using natural language. Genie uses data registered to Unity
1098
+ Catalog and requires at least CAN USE permission on a Pro or Serverless SQL warehouse. Also, Databricks
1099
+ Assistant must be enabled."""
1100
+
1101
+ def __init__(self, api_client):
1102
+ self._api = api_client
1103
+
1104
+ def wait_get_message_genie_completed(
1105
+ self,
1106
+ conversation_id: str,
1107
+ message_id: str,
1108
+ space_id: str,
1109
+ timeout=timedelta(minutes=20),
1110
+ callback: Optional[Callable[[GenieMessage], None]] = None) -> GenieMessage:
1111
+ deadline = time.time() + timeout.total_seconds()
1112
+ target_states = (MessageStatus.COMPLETED, )
1113
+ failure_states = (MessageStatus.FAILED, )
1114
+ status_message = 'polling...'
1115
+ attempt = 1
1116
+ while time.time() < deadline:
1117
+ poll = self.get_message(conversation_id=conversation_id, message_id=message_id, space_id=space_id)
1118
+ status = poll.status
1119
+ status_message = f'current status: {status}'
1120
+ if status in target_states:
1121
+ return poll
1122
+ if callback:
1123
+ callback(poll)
1124
+ if status in failure_states:
1125
+ msg = f'failed to reach COMPLETED, got {status}: {status_message}'
1126
+ raise OperationFailed(msg)
1127
+ prefix = f"conversation_id={conversation_id}, message_id={message_id}, space_id={space_id}"
1128
+ sleep = attempt
1129
+ if sleep > 10:
1130
+ # sleep 10s max per attempt
1131
+ sleep = 10
1132
+ _LOG.debug(f'{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)')
1133
+ time.sleep(sleep + random.random())
1134
+ attempt += 1
1135
+ raise TimeoutError(f'timed out after {timeout}: {status_message}')
1136
+
1137
+ def create_message(self, space_id: str, conversation_id: str, content: str) -> Wait[GenieMessage]:
1138
+ """Create conversation message.
1139
+
1140
+ Create new message in [conversation](:method:genie/startconversation). The AI response uses all
1141
+ previously created messages in the conversation to respond.
1142
+
1143
+ :param space_id: str
1144
+ The ID associated with the Genie space where the conversation is started.
1145
+ :param conversation_id: str
1146
+ The ID associated with the conversation.
1147
+ :param content: str
1148
+ User message content.
1149
+
1150
+ :returns:
1151
+ Long-running operation waiter for :class:`GenieMessage`.
1152
+ See :method:wait_get_message_genie_completed for more details.
1153
+ """
1154
+ body = {}
1155
+ if content is not None: body['content'] = content
1156
+ headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
1157
+
1158
+ op_response = self._api.do(
1159
+ 'POST',
1160
+ f'/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages',
1161
+ body=body,
1162
+ headers=headers)
1163
+ return Wait(self.wait_get_message_genie_completed,
1164
+ response=GenieMessage.from_dict(op_response),
1165
+ conversation_id=conversation_id,
1166
+ message_id=op_response['id'],
1167
+ space_id=space_id)
1168
+
1169
+ def create_message_and_wait(self,
1170
+ space_id: str,
1171
+ conversation_id: str,
1172
+ content: str,
1173
+ timeout=timedelta(minutes=20)) -> GenieMessage:
1174
+ return self.create_message(content=content, conversation_id=conversation_id,
1175
+ space_id=space_id).result(timeout=timeout)
1176
+
1177
+ def execute_message_query(self, space_id: str, conversation_id: str,
1178
+ message_id: str) -> GenieGetMessageQueryResultResponse:
1179
+ """Execute SQL query in a conversation message.
1180
+
1181
+ Execute the SQL query in the message.
1182
+
1183
+ :param space_id: str
1184
+ Genie space ID
1185
+ :param conversation_id: str
1186
+ Conversation ID
1187
+ :param message_id: str
1188
+ Message ID
1189
+
1190
+ :returns: :class:`GenieGetMessageQueryResultResponse`
1191
+ """
1192
+
1193
+ headers = {'Accept': 'application/json', }
1194
+
1195
+ res = self._api.do(
1196
+ 'POST',
1197
+ f'/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages/{message_id}/execute-query',
1198
+ headers=headers)
1199
+ return GenieGetMessageQueryResultResponse.from_dict(res)
1200
+
1201
+ def get_message(self, space_id: str, conversation_id: str, message_id: str) -> GenieMessage:
1202
+ """Get conversation message.
1203
+
1204
+ Get message from conversation.
1205
+
1206
+ :param space_id: str
1207
+ The ID associated with the Genie space where the target conversation is located.
1208
+ :param conversation_id: str
1209
+ The ID associated with the target conversation.
1210
+ :param message_id: str
1211
+ The ID associated with the target message from the identified conversation.
1212
+
1213
+ :returns: :class:`GenieMessage`
1214
+ """
1215
+
1216
+ headers = {'Accept': 'application/json', }
1217
+
1218
+ res = self._api.do(
1219
+ 'GET',
1220
+ f'/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages/{message_id}',
1221
+ headers=headers)
1222
+ return GenieMessage.from_dict(res)
1223
+
1224
+ def get_message_query_result(self, space_id: str, conversation_id: str,
1225
+ message_id: str) -> GenieGetMessageQueryResultResponse:
1226
+ """Get conversation message SQL query result.
1227
+
1228
+ Get the result of SQL query if the message has a query attachment. This is only available if a message
1229
+ has a query attachment and the message status is `EXECUTING_QUERY`.
1230
+
1231
+ :param space_id: str
1232
+ Genie space ID
1233
+ :param conversation_id: str
1234
+ Conversation ID
1235
+ :param message_id: str
1236
+ Message ID
1237
+
1238
+ :returns: :class:`GenieGetMessageQueryResultResponse`
1239
+ """
1240
+
1241
+ headers = {'Accept': 'application/json', }
1242
+
1243
+ res = self._api.do(
1244
+ 'GET',
1245
+ f'/api/2.0/genie/spaces/{space_id}/conversations/{conversation_id}/messages/{message_id}/query-result',
1246
+ headers=headers)
1247
+ return GenieGetMessageQueryResultResponse.from_dict(res)
1248
+
1249
+ def start_conversation(self, space_id: str, content: str) -> Wait[GenieMessage]:
1250
+ """Start conversation.
1251
+
1252
+ Start a new conversation.
1253
+
1254
+ :param space_id: str
1255
+ The ID associated with the Genie space where you want to start a conversation.
1256
+ :param content: str
1257
+ The text of the message that starts the conversation.
1258
+
1259
+ :returns:
1260
+ Long-running operation waiter for :class:`GenieMessage`.
1261
+ See :method:wait_get_message_genie_completed for more details.
1262
+ """
1263
+ body = {}
1264
+ if content is not None: body['content'] = content
1265
+ headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
1266
+
1267
+ op_response = self._api.do('POST',
1268
+ f'/api/2.0/genie/spaces/{space_id}/start-conversation',
1269
+ body=body,
1270
+ headers=headers)
1271
+ return Wait(self.wait_get_message_genie_completed,
1272
+ response=GenieStartConversationResponse.from_dict(op_response),
1273
+ conversation_id=op_response['conversation_id'],
1274
+ message_id=op_response['message_id'],
1275
+ space_id=space_id)
1276
+
1277
+ def start_conversation_and_wait(self, space_id: str, content: str,
1278
+ timeout=timedelta(minutes=20)) -> GenieMessage:
1279
+ return self.start_conversation(content=content, space_id=space_id).result(timeout=timeout)
1280
+
1281
+
273
1282
  class LakeviewAPI:
274
1283
  """These APIs provide specific management operations for Lakeview dashboards. Generic resource management can
275
1284
  be done with Workspace API (import, export, get-status, list, delete)."""
@@ -309,6 +1318,115 @@ class LakeviewAPI:
309
1318
  res = self._api.do('POST', '/api/2.0/lakeview/dashboards', body=body, headers=headers)
310
1319
  return Dashboard.from_dict(res)
311
1320
 
1321
+ def create_schedule(self,
1322
+ dashboard_id: str,
1323
+ cron_schedule: CronSchedule,
1324
+ *,
1325
+ display_name: Optional[str] = None,
1326
+ pause_status: Optional[SchedulePauseStatus] = None) -> Schedule:
1327
+ """Create dashboard schedule.
1328
+
1329
+ :param dashboard_id: str
1330
+ UUID identifying the dashboard to which the schedule belongs.
1331
+ :param cron_schedule: :class:`CronSchedule`
1332
+ The cron expression describing the frequency of the periodic refresh for this schedule.
1333
+ :param display_name: str (optional)
1334
+ The display name for schedule.
1335
+ :param pause_status: :class:`SchedulePauseStatus` (optional)
1336
+ The status indicates whether this schedule is paused or not.
1337
+
1338
+ :returns: :class:`Schedule`
1339
+ """
1340
+ body = {}
1341
+ if cron_schedule is not None: body['cron_schedule'] = cron_schedule.as_dict()
1342
+ if display_name is not None: body['display_name'] = display_name
1343
+ if pause_status is not None: body['pause_status'] = pause_status.value
1344
+ headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
1345
+
1346
+ res = self._api.do('POST',
1347
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules',
1348
+ body=body,
1349
+ headers=headers)
1350
+ return Schedule.from_dict(res)
1351
+
1352
+ def create_subscription(self, dashboard_id: str, schedule_id: str,
1353
+ subscriber: Subscriber) -> Subscription:
1354
+ """Create schedule subscription.
1355
+
1356
+ :param dashboard_id: str
1357
+ UUID identifying the dashboard to which the subscription belongs.
1358
+ :param schedule_id: str
1359
+ UUID identifying the schedule to which the subscription belongs.
1360
+ :param subscriber: :class:`Subscriber`
1361
+ Subscriber details for users and destinations to be added as subscribers to the schedule.
1362
+
1363
+ :returns: :class:`Subscription`
1364
+ """
1365
+ body = {}
1366
+ if subscriber is not None: body['subscriber'] = subscriber.as_dict()
1367
+ headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
1368
+
1369
+ res = self._api.do(
1370
+ 'POST',
1371
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}/subscriptions',
1372
+ body=body,
1373
+ headers=headers)
1374
+ return Subscription.from_dict(res)
1375
+
1376
+ def delete_schedule(self, dashboard_id: str, schedule_id: str, *, etag: Optional[str] = None):
1377
+ """Delete dashboard schedule.
1378
+
1379
+ :param dashboard_id: str
1380
+ UUID identifying the dashboard to which the schedule belongs.
1381
+ :param schedule_id: str
1382
+ UUID identifying the schedule.
1383
+ :param etag: str (optional)
1384
+ The etag for the schedule. Optionally, it can be provided to verify that the schedule has not been
1385
+ modified from its last retrieval.
1386
+
1387
+
1388
+ """
1389
+
1390
+ query = {}
1391
+ if etag is not None: query['etag'] = etag
1392
+ headers = {'Accept': 'application/json', }
1393
+
1394
+ self._api.do('DELETE',
1395
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}',
1396
+ query=query,
1397
+ headers=headers)
1398
+
1399
+ def delete_subscription(self,
1400
+ dashboard_id: str,
1401
+ schedule_id: str,
1402
+ subscription_id: str,
1403
+ *,
1404
+ etag: Optional[str] = None):
1405
+ """Delete schedule subscription.
1406
+
1407
+ :param dashboard_id: str
1408
+ UUID identifying the dashboard which the subscription belongs.
1409
+ :param schedule_id: str
1410
+ UUID identifying the schedule which the subscription belongs.
1411
+ :param subscription_id: str
1412
+ UUID identifying the subscription.
1413
+ :param etag: str (optional)
1414
+ The etag for the subscription. Can be optionally provided to ensure that the subscription has not
1415
+ been modified since the last read.
1416
+
1417
+
1418
+ """
1419
+
1420
+ query = {}
1421
+ if etag is not None: query['etag'] = etag
1422
+ headers = {'Accept': 'application/json', }
1423
+
1424
+ self._api.do(
1425
+ 'DELETE',
1426
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}/subscriptions/{subscription_id}',
1427
+ query=query,
1428
+ headers=headers)
1429
+
312
1430
  def get(self, dashboard_id: str) -> Dashboard:
313
1431
  """Get dashboard.
314
1432
 
@@ -341,6 +1459,157 @@ class LakeviewAPI:
341
1459
  res = self._api.do('GET', f'/api/2.0/lakeview/dashboards/{dashboard_id}/published', headers=headers)
342
1460
  return PublishedDashboard.from_dict(res)
343
1461
 
1462
+ def get_schedule(self, dashboard_id: str, schedule_id: str) -> Schedule:
1463
+ """Get dashboard schedule.
1464
+
1465
+ :param dashboard_id: str
1466
+ UUID identifying the dashboard to which the schedule belongs.
1467
+ :param schedule_id: str
1468
+ UUID identifying the schedule.
1469
+
1470
+ :returns: :class:`Schedule`
1471
+ """
1472
+
1473
+ headers = {'Accept': 'application/json', }
1474
+
1475
+ res = self._api.do('GET',
1476
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}',
1477
+ headers=headers)
1478
+ return Schedule.from_dict(res)
1479
+
1480
+ def get_subscription(self, dashboard_id: str, schedule_id: str, subscription_id: str) -> Subscription:
1481
+ """Get schedule subscription.
1482
+
1483
+ :param dashboard_id: str
1484
+ UUID identifying the dashboard which the subscription belongs.
1485
+ :param schedule_id: str
1486
+ UUID identifying the schedule which the subscription belongs.
1487
+ :param subscription_id: str
1488
+ UUID identifying the subscription.
1489
+
1490
+ :returns: :class:`Subscription`
1491
+ """
1492
+
1493
+ headers = {'Accept': 'application/json', }
1494
+
1495
+ res = self._api.do(
1496
+ 'GET',
1497
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}/subscriptions/{subscription_id}',
1498
+ headers=headers)
1499
+ return Subscription.from_dict(res)
1500
+
1501
+ def list(self,
1502
+ *,
1503
+ page_size: Optional[int] = None,
1504
+ page_token: Optional[str] = None,
1505
+ show_trashed: Optional[bool] = None,
1506
+ view: Optional[DashboardView] = None) -> Iterator[Dashboard]:
1507
+ """List dashboards.
1508
+
1509
+ :param page_size: int (optional)
1510
+ The number of dashboards to return per page.
1511
+ :param page_token: str (optional)
1512
+ A page token, received from a previous `ListDashboards` call. This token can be used to retrieve the
1513
+ subsequent page.
1514
+ :param show_trashed: bool (optional)
1515
+ The flag to include dashboards located in the trash. If unspecified, only active dashboards will be
1516
+ returned.
1517
+ :param view: :class:`DashboardView` (optional)
1518
+ `DASHBOARD_VIEW_BASIC`only includes summary metadata from the dashboard.
1519
+
1520
+ :returns: Iterator over :class:`Dashboard`
1521
+ """
1522
+
1523
+ query = {}
1524
+ if page_size is not None: query['page_size'] = page_size
1525
+ if page_token is not None: query['page_token'] = page_token
1526
+ if show_trashed is not None: query['show_trashed'] = show_trashed
1527
+ if view is not None: query['view'] = view.value
1528
+ headers = {'Accept': 'application/json', }
1529
+
1530
+ while True:
1531
+ json = self._api.do('GET', '/api/2.0/lakeview/dashboards', query=query, headers=headers)
1532
+ if 'dashboards' in json:
1533
+ for v in json['dashboards']:
1534
+ yield Dashboard.from_dict(v)
1535
+ if 'next_page_token' not in json or not json['next_page_token']:
1536
+ return
1537
+ query['page_token'] = json['next_page_token']
1538
+
1539
+ def list_schedules(self,
1540
+ dashboard_id: str,
1541
+ *,
1542
+ page_size: Optional[int] = None,
1543
+ page_token: Optional[str] = None) -> Iterator[Schedule]:
1544
+ """List dashboard schedules.
1545
+
1546
+ :param dashboard_id: str
1547
+ UUID identifying the dashboard to which the schedule belongs.
1548
+ :param page_size: int (optional)
1549
+ The number of schedules to return per page.
1550
+ :param page_token: str (optional)
1551
+ A page token, received from a previous `ListSchedules` call. Use this to retrieve the subsequent
1552
+ page.
1553
+
1554
+ :returns: Iterator over :class:`Schedule`
1555
+ """
1556
+
1557
+ query = {}
1558
+ if page_size is not None: query['page_size'] = page_size
1559
+ if page_token is not None: query['page_token'] = page_token
1560
+ headers = {'Accept': 'application/json', }
1561
+
1562
+ while True:
1563
+ json = self._api.do('GET',
1564
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules',
1565
+ query=query,
1566
+ headers=headers)
1567
+ if 'schedules' in json:
1568
+ for v in json['schedules']:
1569
+ yield Schedule.from_dict(v)
1570
+ if 'next_page_token' not in json or not json['next_page_token']:
1571
+ return
1572
+ query['page_token'] = json['next_page_token']
1573
+
1574
+ def list_subscriptions(self,
1575
+ dashboard_id: str,
1576
+ schedule_id: str,
1577
+ *,
1578
+ page_size: Optional[int] = None,
1579
+ page_token: Optional[str] = None) -> Iterator[Subscription]:
1580
+ """List schedule subscriptions.
1581
+
1582
+ :param dashboard_id: str
1583
+ UUID identifying the dashboard to which the subscription belongs.
1584
+ :param schedule_id: str
1585
+ UUID identifying the schedule to which the subscription belongs.
1586
+ :param page_size: int (optional)
1587
+ The number of subscriptions to return per page.
1588
+ :param page_token: str (optional)
1589
+ A page token, received from a previous `ListSubscriptions` call. Use this to retrieve the subsequent
1590
+ page.
1591
+
1592
+ :returns: Iterator over :class:`Subscription`
1593
+ """
1594
+
1595
+ query = {}
1596
+ if page_size is not None: query['page_size'] = page_size
1597
+ if page_token is not None: query['page_token'] = page_token
1598
+ headers = {'Accept': 'application/json', }
1599
+
1600
+ while True:
1601
+ json = self._api.do(
1602
+ 'GET',
1603
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}/subscriptions',
1604
+ query=query,
1605
+ headers=headers)
1606
+ if 'subscriptions' in json:
1607
+ for v in json['subscriptions']:
1608
+ yield Subscription.from_dict(v)
1609
+ if 'next_page_token' not in json or not json['next_page_token']:
1610
+ return
1611
+ query['page_token'] = json['next_page_token']
1612
+
344
1613
  def migrate(self,
345
1614
  source_dashboard_id: str,
346
1615
  *,
@@ -465,3 +1734,42 @@ class LakeviewAPI:
465
1734
  body=body,
466
1735
  headers=headers)
467
1736
  return Dashboard.from_dict(res)
1737
+
1738
+ def update_schedule(self,
1739
+ dashboard_id: str,
1740
+ schedule_id: str,
1741
+ cron_schedule: CronSchedule,
1742
+ *,
1743
+ display_name: Optional[str] = None,
1744
+ etag: Optional[str] = None,
1745
+ pause_status: Optional[SchedulePauseStatus] = None) -> Schedule:
1746
+ """Update dashboard schedule.
1747
+
1748
+ :param dashboard_id: str
1749
+ UUID identifying the dashboard to which the schedule belongs.
1750
+ :param schedule_id: str
1751
+ UUID identifying the schedule.
1752
+ :param cron_schedule: :class:`CronSchedule`
1753
+ The cron expression describing the frequency of the periodic refresh for this schedule.
1754
+ :param display_name: str (optional)
1755
+ The display name for schedule.
1756
+ :param etag: str (optional)
1757
+ The etag for the schedule. Must be left empty on create, must be provided on updates to ensure that
1758
+ the schedule has not been modified since the last read, and can be optionally provided on delete.
1759
+ :param pause_status: :class:`SchedulePauseStatus` (optional)
1760
+ The status indicates whether this schedule is paused or not.
1761
+
1762
+ :returns: :class:`Schedule`
1763
+ """
1764
+ body = {}
1765
+ if cron_schedule is not None: body['cron_schedule'] = cron_schedule.as_dict()
1766
+ if display_name is not None: body['display_name'] = display_name
1767
+ if etag is not None: body['etag'] = etag
1768
+ if pause_status is not None: body['pause_status'] = pause_status.value
1769
+ headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
1770
+
1771
+ res = self._api.do('PUT',
1772
+ f'/api/2.0/lakeview/dashboards/{dashboard_id}/schedules/{schedule_id}',
1773
+ body=body,
1774
+ headers=headers)
1775
+ return Schedule.from_dict(res)