MindsDB 25.3.3.0__py3-none-any.whl → 25.3.4.1__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 MindsDB might be problematic. Click here for more details.

Files changed (45) hide show
  1. mindsdb/__about__.py +2 -2
  2. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +2 -6
  3. mindsdb/api/executor/datahub/datanodes/mindsdb_tables.py +1 -1
  4. mindsdb/api/http/namespaces/agents.py +9 -5
  5. mindsdb/api/http/namespaces/chatbots.py +6 -5
  6. mindsdb/api/http/namespaces/databases.py +5 -6
  7. mindsdb/api/http/namespaces/skills.py +5 -4
  8. mindsdb/api/http/namespaces/views.py +6 -7
  9. mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +23 -2
  10. mindsdb/integrations/handlers/confluence_handler/confluence_api_client.py +164 -0
  11. mindsdb/integrations/handlers/confluence_handler/confluence_handler.py +54 -59
  12. mindsdb/integrations/handlers/confluence_handler/confluence_tables.py +753 -0
  13. mindsdb/integrations/handlers/confluence_handler/connection_args.py +8 -8
  14. mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +16 -6
  15. mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +64 -83
  16. mindsdb/integrations/handlers/huggingface_handler/requirements.txt +5 -4
  17. mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +5 -5
  18. mindsdb/integrations/handlers/langchain_handler/requirements.txt +1 -1
  19. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +3 -3
  20. mindsdb/integrations/handlers/litellm_handler/requirements.txt +1 -1
  21. mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
  22. mindsdb/integrations/handlers/ms_one_drive_handler/ms_graph_api_one_drive_client.py +1 -1
  23. mindsdb/integrations/handlers/ms_teams_handler/ms_graph_api_teams_client.py +278 -0
  24. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_handler.py +114 -70
  25. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_tables.py +431 -0
  26. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +2 -0
  27. mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +18 -4
  28. mindsdb/integrations/handlers/ray_serve_handler/ray_serve_handler.py +18 -16
  29. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +26 -1
  30. mindsdb/integrations/libs/vectordatabase_handler.py +2 -2
  31. mindsdb/integrations/utilities/files/file_reader.py +3 -3
  32. mindsdb/integrations/utilities/handlers/api_utilities/microsoft/ms_graph_api_utilities.py +36 -2
  33. mindsdb/integrations/utilities/rag/settings.py +1 -0
  34. mindsdb/interfaces/chatbot/chatbot_controller.py +6 -4
  35. mindsdb/interfaces/jobs/jobs_controller.py +1 -4
  36. mindsdb/interfaces/knowledge_base/controller.py +9 -28
  37. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +1 -1
  38. mindsdb/interfaces/skills/skills_controller.py +8 -7
  39. {mindsdb-25.3.3.0.dist-info → mindsdb-25.3.4.1.dist-info}/METADATA +237 -237
  40. {mindsdb-25.3.3.0.dist-info → mindsdb-25.3.4.1.dist-info}/RECORD +43 -41
  41. {mindsdb-25.3.3.0.dist-info → mindsdb-25.3.4.1.dist-info}/WHEEL +1 -1
  42. mindsdb/integrations/handlers/confluence_handler/confluence_table.py +0 -193
  43. mindsdb/integrations/handlers/confluence_handler/requirements.txt +0 -1
  44. {mindsdb-25.3.3.0.dist-info → mindsdb-25.3.4.1.dist-info/licenses}/LICENSE +0 -0
  45. {mindsdb-25.3.3.0.dist-info → mindsdb-25.3.4.1.dist-info}/top_level.txt +0 -0
mindsdb/__about__.py CHANGED
@@ -1,10 +1,10 @@
1
1
  __title__ = 'MindsDB'
2
2
  __package_name__ = 'mindsdb'
3
- __version__ = '25.3.3.0'
3
+ __version__ = '25.3.4.1'
4
4
  __description__ = "MindsDB's AI SQL Server enables developers to build AI tools that need access to real-time data to perform their tasks"
5
5
  __email__ = "jorge@mindsdb.com"
6
6
  __author__ = 'MindsDB Inc'
7
7
  __github__ = 'https://github.com/mindsdb/mindsdb'
8
8
  __pypi__ = 'https://pypi.org/project/mindsdb/'
9
- __license__ = 'SSPL v1'
9
+ __license__ = 'Elastic License 2.0'
10
10
  __copyright__ = 'Copyright(c) 2018 MindsDB, Inc'
@@ -3,12 +3,8 @@ import pandas as pd
3
3
  from mindsdb_sql_parser.ast.base import ASTNode
4
4
 
5
5
  from mindsdb.api.executor.datahub.datanodes.datanode import DataNode
6
- from mindsdb.api.executor.datahub.datanodes.integration_datanode import (
7
- IntegrationDataNode,
8
- )
9
- from mindsdb.api.executor.datahub.datanodes.project_datanode import (
10
- ProjectDataNode,
11
- )
6
+ from mindsdb.api.executor.datahub.datanodes.integration_datanode import IntegrationDataNode
7
+ from mindsdb.api.executor.datahub.datanodes.project_datanode import ProjectDataNode
12
8
  from mindsdb.api.executor import exceptions as exc
13
9
  from mindsdb.api.executor.utilities.sql import query_df
14
10
  from mindsdb.api.executor.utilities.sql import get_query_tables
@@ -310,7 +310,7 @@ class ChatbotsTable(MdbTable):
310
310
  ):
311
311
  project_name = query.where.args[1].value
312
312
 
313
- chatbot_data = chatbot_controller.get_chatbots(project_name)
313
+ chatbot_data = chatbot_controller.get_chatbots(project_name=project_name)
314
314
 
315
315
  columns = cls.columns
316
316
  columns_lower = [col.lower() for col in columns]
@@ -47,7 +47,7 @@ def create_agent(project_name, name, agent):
47
47
 
48
48
  try:
49
49
  existing_agent = agents_controller.get_agent(name, project_name=project_name)
50
- except ValueError:
50
+ except (ValueError, EntityNotExistsError):
51
51
  # Project must exist.
52
52
  return http_error(
53
53
  HTTPStatus.NOT_FOUND,
@@ -141,7 +141,7 @@ class AgentResource(Resource):
141
141
  f'Agent with name {agent_name} does not exist'
142
142
  )
143
143
  return existing_agent.as_dict()
144
- except ValueError:
144
+ except (ValueError, EntityNotExistsError):
145
145
  # Project needs to exist.
146
146
  return http_error(
147
147
  HTTPStatus.NOT_FOUND,
@@ -173,7 +173,11 @@ class AgentResource(Resource):
173
173
  f'Project with name {project_name} does not exist'
174
174
  )
175
175
  if existing_agent_record is None:
176
- raise Exception
176
+ return http_error(
177
+ HTTPStatus.BAD_REQUEST,
178
+ 'Creation is not allowed',
179
+ 'Creation of an agent using the PUT method is not allowed.'
180
+ )
177
181
 
178
182
  agent = request.json['agent']
179
183
  name = agent.get('name', None)
@@ -272,7 +276,7 @@ class AgentResource(Resource):
272
276
  'Agent not found',
273
277
  f'Agent with name {agent_name} does not exist'
274
278
  )
275
- except ValueError:
279
+ except (ValueError, EntityNotExistsError):
276
280
  # Project needs to exist.
277
281
  return http_error(
278
282
  HTTPStatus.NOT_FOUND,
@@ -435,7 +439,7 @@ class AgentCompletions(Resource):
435
439
  'Agent not found',
436
440
  f'Agent with name {agent_name} does not exist'
437
441
  )
438
- except ValueError:
442
+ except (ValueError, EntityNotExistsError):
439
443
  # Project needs to exist.
440
444
  return http_error(
441
445
  HTTPStatus.NOT_FOUND,
@@ -11,6 +11,7 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
11
11
  from mindsdb.interfaces.chatbot.chatbot_controller import ChatBotController
12
12
  from mindsdb.interfaces.model.functions import PredictorRecordNotFound
13
13
  from mindsdb.interfaces.storage.db import Predictor
14
+ from mindsdb.utilities.exception import EntityNotExistsError
14
15
 
15
16
 
16
17
  def create_chatbot(project_name, name, chatbot):
@@ -59,7 +60,7 @@ def create_chatbot(project_name, name, chatbot):
59
60
 
60
61
  try:
61
62
  existing_chatbot = chatbot_controller.get_chatbot(name, project_name=project_name)
62
- except ValueError:
63
+ except EntityNotExistsError:
63
64
  # Project must exist.
64
65
  return http_error(
65
66
  HTTPStatus.NOT_FOUND,
@@ -152,7 +153,7 @@ class ChatBotsResource(Resource):
152
153
  chatbot_controller = ChatBotController()
153
154
  try:
154
155
  all_bots = chatbot_controller.get_chatbots(project_name)
155
- except ValueError:
156
+ except (ValueError, EntityNotExistsError):
156
157
  # Project needs to exist.
157
158
  return http_error(
158
159
  HTTPStatus.NOT_FOUND,
@@ -197,7 +198,7 @@ class ChatBotResource(Resource):
197
198
  f'Chatbot with name {chatbot_name} does not exist'
198
199
  )
199
200
  return existing_chatbot
200
- except ValueError:
201
+ except (ValueError, EntityNotExistsError):
201
202
  # Project needs to exist.
202
203
  return http_error(
203
204
  HTTPStatus.NOT_FOUND,
@@ -221,7 +222,7 @@ class ChatBotResource(Resource):
221
222
 
222
223
  try:
223
224
  existing_chatbot = chatbot_controller.get_chatbot(chatbot_name, project_name=project_name)
224
- except ValueError:
225
+ except EntityNotExistsError:
225
226
  # Project needs to exist.
226
227
  return http_error(
227
228
  HTTPStatus.NOT_FOUND,
@@ -306,7 +307,7 @@ class ChatBotResource(Resource):
306
307
  'Chatbot not found',
307
308
  f'Chatbot with name {chatbot_name} does not exist'
308
309
  )
309
- except ValueError:
310
+ except EntityNotExistsError:
310
311
  # Project needs to exist.
311
312
  return http_error(
312
313
  HTTPStatus.NOT_FOUND,
@@ -1,10 +1,9 @@
1
- from http import HTTPStatus
2
- import tempfile
3
1
  import time
2
+ import shutil
3
+ import tempfile
4
+ from http import HTTPStatus
4
5
  from typing import Dict
5
6
  from pathlib import Path
6
- import shutil
7
- from sqlalchemy.exc import NoResultFound
8
7
 
9
8
  from flask import request
10
9
  from flask_restx import Resource
@@ -337,7 +336,7 @@ class TablesList(Resource):
337
336
  HTTPStatus.BAD_REQUEST, 'Error',
338
337
  error_message
339
338
  )
340
- except NoResultFound:
339
+ except EntityNotExistsError:
341
340
  # Only support creating tables from integrations.
342
341
  pass
343
342
 
@@ -419,7 +418,7 @@ class TableResource(Resource):
419
418
  + f'If you want to delete a model or view, use the projects/{database_name}/models/{table_name} or ' \
420
419
  + f'projects/{database_name}/views/{table_name} endpoints instead.'
421
420
  return http_error(HTTPStatus.BAD_REQUEST, 'Error', error_message)
422
- except NoResultFound:
421
+ except EntityNotExistsError:
423
422
  # Only support dropping tables from integrations.
424
423
  pass
425
424
 
@@ -7,6 +7,7 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
7
7
  from mindsdb.api.http.namespaces.configs.projects import ns_conf
8
8
  from mindsdb.api.http.utils import http_error
9
9
  from mindsdb.interfaces.skills.skills_controller import SkillsController
10
+ from mindsdb.utilities.exception import EntityNotExistsError
10
11
 
11
12
 
12
13
  def create_skill(project_name, skill):
@@ -52,7 +53,7 @@ class SkillsResource(Resource):
52
53
  skills_controller = SkillsController()
53
54
  try:
54
55
  all_skills = skills_controller.get_skills(project_name)
55
- except ValueError:
56
+ except EntityNotExistsError:
56
57
  # Project needs to exist.
57
58
  return http_error(
58
59
  HTTPStatus.NOT_FOUND,
@@ -88,7 +89,7 @@ class SkillResource(Resource):
88
89
  skills_controller = SkillsController()
89
90
  try:
90
91
  existing_skill = skills_controller.get_skill(skill_name, project_name)
91
- except ValueError:
92
+ except EntityNotExistsError:
92
93
  # Project needs to exist
93
94
  return http_error(
94
95
  HTTPStatus.NOT_FOUND,
@@ -120,7 +121,7 @@ class SkillResource(Resource):
120
121
 
121
122
  try:
122
123
  existing_skill = skills_controller.get_skill(skill_name, project_name)
123
- except ValueError:
124
+ except EntityNotExistsError:
124
125
  # Project needs to exist
125
126
  return http_error(
126
127
  HTTPStatus.NOT_FOUND,
@@ -152,7 +153,7 @@ class SkillResource(Resource):
152
153
  skills_controller = SkillsController()
153
154
  try:
154
155
  existing_skill = skills_controller.get_skill(skill_name, project_name)
155
- except ValueError:
156
+ except EntityNotExistsError:
156
157
  # Project needs to exist
157
158
  return http_error(
158
159
  HTTPStatus.NOT_FOUND,
@@ -2,13 +2,12 @@ from http import HTTPStatus
2
2
 
3
3
  from flask import request
4
4
  from flask_restx import Resource
5
- from sqlalchemy.exc import NoResultFound
6
-
7
5
 
8
6
  from mindsdb.api.http.utils import http_error
9
7
  from mindsdb.api.http.namespaces.configs.projects import ns_conf
10
8
  from mindsdb.api.executor.controllers.session_controller import SessionController
11
9
  from mindsdb.metrics.metrics import api_endpoint_metrics
10
+ from mindsdb.utilities.exception import EntityNotExistsError
12
11
 
13
12
 
14
13
  @ns_conf.route('/<project_name>/views')
@@ -20,7 +19,7 @@ class ViewsList(Resource):
20
19
  session = SessionController()
21
20
  try:
22
21
  project = session.database_controller.get_project(project_name)
23
- except NoResultFound:
22
+ except EntityNotExistsError:
24
23
  return http_error(
25
24
  HTTPStatus.NOT_FOUND,
26
25
  'Project not found',
@@ -55,7 +54,7 @@ class ViewsList(Resource):
55
54
 
56
55
  try:
57
56
  project = session.database_controller.get_project(project_name)
58
- except NoResultFound:
57
+ except EntityNotExistsError:
59
58
  return http_error(HTTPStatus.NOT_FOUND, 'Not found', f'Project name {project_name} does not exist')
60
59
 
61
60
  if project.get_view(name) is not None:
@@ -82,7 +81,7 @@ class ViewResource(Resource):
82
81
  session = SessionController()
83
82
  try:
84
83
  project = session.database_controller.get_project(project_name)
85
- except NoResultFound:
84
+ except EntityNotExistsError:
86
85
  return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
87
86
 
88
87
  view = project.get_view(view_name)
@@ -106,7 +105,7 @@ class ViewResource(Resource):
106
105
  session = SessionController()
107
106
  try:
108
107
  project = session.database_controller.get_project(project_name)
109
- except NoResultFound:
108
+ except EntityNotExistsError:
110
109
  return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
111
110
 
112
111
  existing_view = project.get_view(view_name)
@@ -143,7 +142,7 @@ class ViewResource(Resource):
143
142
  session = SessionController()
144
143
  try:
145
144
  project = session.database_controller.get_project(project_name)
146
- except NoResultFound:
145
+ except EntityNotExistsError:
147
146
  return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
148
147
 
149
148
  if project.get_view(view_name) is None:
@@ -210,6 +210,7 @@ class ChromaDBHandler(VectorStoreHandler):
210
210
  chroma_db_conditions = []
211
211
  for condition in metadata_conditions:
212
212
  metadata_key = condition.column.split(".")[-1]
213
+
213
214
  chroma_db_conditions.append(
214
215
  {
215
216
  metadata_key: {
@@ -310,9 +311,29 @@ class ChromaDBHandler(VectorStoreHandler):
310
311
  payload = {column: payload[column] for column in columns}
311
312
 
312
313
  # always include distance
314
+ distance_filter = None
315
+ distance_col = TableField.DISTANCE.value
313
316
  if distances is not None:
314
- payload[TableField.DISTANCE.value] = distances
315
- return pd.DataFrame(payload)
317
+ payload[distance_col] = distances
318
+
319
+ for cond in conditions:
320
+ if cond.column == distance_col:
321
+ distance_filter = cond
322
+ break
323
+
324
+ df = pd.DataFrame(payload)
325
+ if distance_filter is not None:
326
+ op_map = {
327
+ '<': '__lt__',
328
+ '<=': '__le__',
329
+ '>': '__gt__',
330
+ '>=': '__ge__',
331
+ '=': '__eq__',
332
+ }
333
+ op = op_map.get(distance_filter.op.value)
334
+ if op:
335
+ df = df[getattr(df[distance_col], op)(distance_filter.value)]
336
+ return df
316
337
 
317
338
  def _dataframe_metadata_to_chroma_metadata(self, metadata: Union[Dict[str, str], str]) -> Optional[Dict[str, str]]:
318
339
  """Convert DataFrame metadata to ChromaDB compatible metadata format"""
@@ -0,0 +1,164 @@
1
+ from typing import List
2
+
3
+ import requests
4
+
5
+
6
+ class ConfluenceAPIClient:
7
+ def __init__(self, url: str, username: str, password: str):
8
+ self.url = url
9
+ self.username = username
10
+ self.password = password
11
+ self.session = requests.Session()
12
+ self.session.auth = (self.username, self.password)
13
+ self.session.headers.update({"Accept": "application/json"})
14
+
15
+ def get_spaces(
16
+ self,
17
+ ids: List[int] = None,
18
+ keys: List[str] = None,
19
+ space_type: str = None,
20
+ status: str = None,
21
+ sort_condition: str = None,
22
+ limit: int = None,
23
+ ):
24
+ url = f"{self.url}/wiki/api/v2/spaces"
25
+ params = {
26
+ "description-format": "view",
27
+ }
28
+ if ids:
29
+ params["ids"] = ids
30
+ if keys:
31
+ params["keys"] = keys
32
+ if space_type:
33
+ params["type"] = space_type
34
+ if status:
35
+ params["status"] = status
36
+ if sort_condition:
37
+ params["sort"] = sort_condition
38
+ if limit:
39
+ params["limit"] = limit
40
+
41
+ return self._paginate(url, params)
42
+
43
+ def get_pages(
44
+ self,
45
+ page_ids: List[int] = None,
46
+ space_ids: List[int] = None,
47
+ statuses: List[str] = None,
48
+ title: str = None,
49
+ sort_condition: str = None,
50
+ limit: int = None,
51
+ ) -> List[dict]:
52
+ url = f"{self.url}/wiki/api/v2/pages"
53
+ params = {
54
+ "body-format": "storage",
55
+ }
56
+ if page_ids:
57
+ params["id"] = page_ids
58
+ if space_ids:
59
+ params["space-id"] = space_ids
60
+ if statuses:
61
+ params["status"] = statuses
62
+ if title:
63
+ params["title"] = title
64
+ if sort_condition:
65
+ params["sort"] = sort_condition
66
+ if limit:
67
+ params["limit"] = limit
68
+
69
+ return self._paginate(url, params)
70
+
71
+ def get_blogposts(
72
+ self,
73
+ post_ids: List[int] = None,
74
+ space_ids: List[str] = None,
75
+ statuses: List[str] = None,
76
+ title: str = None,
77
+ sort_condition: str = None,
78
+ limit: int = None,
79
+ ) -> List[dict]:
80
+ url = f"{self.url}/wiki/api/v2/blogposts"
81
+ params = {
82
+ "body-format": "storage",
83
+ }
84
+ if post_ids:
85
+ params["id"] = post_ids
86
+ if space_ids:
87
+ params["space-id"] = space_ids
88
+ if statuses:
89
+ params["status"] = statuses
90
+ if title:
91
+ params["title"] = title
92
+ if sort_condition:
93
+ params["sort"] = sort_condition
94
+ if limit:
95
+ params["limit"] = limit
96
+
97
+ return self._paginate(url, params)
98
+
99
+ def get_whiteboard_by_id(self, whiteboard_id: int) -> dict:
100
+ url = f"{self.url}/wiki/api/v2/whiteboards/{whiteboard_id}"
101
+
102
+ return self._make_request("GET", url)
103
+
104
+ def get_database_by_id(self, database_id: int) -> dict:
105
+ url = f"{self.url}/wiki/api/v2/databases/{database_id}"
106
+
107
+ return self._make_request("GET", url)
108
+
109
+ def get_tasks(
110
+ self,
111
+ task_ids: List[int] = None,
112
+ space_ids: List[str] = None,
113
+ page_ids: List[str] = None,
114
+ blogpost_ids: List[str] = None,
115
+ created_by_ids: List[str] = None,
116
+ assigned_to_ids: List[str] = None,
117
+ completed_by_ids: List[str] = None,
118
+ status: str = None,
119
+ limit: int = None,
120
+ ) -> List[dict]:
121
+ url = f"{self.url}/wiki/api/v2/tasks"
122
+ params = {
123
+ "body-format": "storage",
124
+ }
125
+ if task_ids:
126
+ params["id"] = task_ids
127
+ if space_ids:
128
+ params["space-id"] = space_ids
129
+ if page_ids:
130
+ params["page-id"] = page_ids
131
+ if blogpost_ids:
132
+ params["blogpost-id"] = blogpost_ids
133
+ if created_by_ids:
134
+ params["created-by"] = created_by_ids
135
+ if assigned_to_ids:
136
+ params["assigned-to"] = assigned_to_ids
137
+ if completed_by_ids:
138
+ params["completed-by"] = completed_by_ids
139
+ if status:
140
+ params["status"] = status
141
+ if limit:
142
+ params["limit"] = limit
143
+
144
+ return self._paginate(url, params)
145
+
146
+ def _paginate(self, url: str, params: dict = None) -> List[dict]:
147
+ results = []
148
+ response = self._make_request("GET", url, params)
149
+ results.extend(response["results"])
150
+
151
+ while response["_links"].get("next"):
152
+ params["cursor"] = response["_links"].get("next")
153
+ response = self._make_request("GET", url, params)
154
+ results.extend(response["results"])
155
+
156
+ return results
157
+
158
+ def _make_request(self, method: str, url: str, params: dict = None, data: dict = None) -> dict:
159
+ response = self.session.request(method, url, params=params, json=data)
160
+
161
+ if response.status_code != 200:
162
+ raise Exception(f"Request failed with status code {response.status_code}: {response.text}")
163
+
164
+ return response.json()
@@ -1,83 +1,92 @@
1
- from typing import Optional, Union
1
+ from typing import Any, Dict
2
2
 
3
- import requests
4
- from atlassian import Confluence
5
- from mindsdb_sql_parser import parse_sql
6
-
7
- from mindsdb.integrations.handlers.confluence_handler.confluence_table import (
3
+ from mindsdb.integrations.handlers.confluence_handler.confluence_api_client import ConfluenceAPIClient
4
+ from mindsdb.integrations.handlers.confluence_handler.confluence_tables import (
5
+ ConfluenceBlogPostsTable,
6
+ ConfluenceDatabasesTable,
8
7
  ConfluencePagesTable,
8
+ ConfluenceSpacesTable,
9
+ ConfluenceTasksTable,
10
+ ConfluenceWhiteboardsTable,
9
11
  )
10
12
  from mindsdb.integrations.libs.api_handler import APIHandler
11
13
  from mindsdb.integrations.libs.response import (
12
14
  HandlerStatusResponse as StatusResponse,
13
15
  )
14
- from mindsdb_sql_parser import parse_sql
15
16
  from mindsdb.utilities import log
16
17
 
17
- from atlassian import Confluence
18
- from typing import Optional
19
- import requests
20
18
 
21
19
  logger = log.getLogger(__name__)
22
20
 
21
+
23
22
  class ConfluenceHandler(APIHandler):
24
- """Confluence handler implementation"""
25
-
26
- def __init__(self, name=None, **kwargs):
27
- """Initialize the Confluence handler.
28
- Parameters
29
- ----------
30
- name : str
31
- name of a handler instance
32
- """
33
- super().__init__(name)
23
+ """
24
+ This handler handles the connection and execution of SQL statements on Confluence.
25
+ """
26
+
27
+ name = "confluence"
34
28
 
35
- connection_data = kwargs.get("connection_data", {})
29
+ def __init__(self, name: str, connection_data: Dict, **kwargs: Any) -> None:
30
+ """
31
+ Initializes the handler.
36
32
 
37
- self.parser = parse_sql
38
- self.dialect = 'confluence'
33
+ Args:
34
+ name (str): The name of the handler instance.
35
+ connection_data (Dict): The connection data required to connect to the Confluence API.
36
+ kwargs: Arbitrary keyword arguments.
37
+ """
38
+ super().__init__(name)
39
39
  self.connection_data = connection_data
40
40
  self.kwargs = kwargs
41
+
41
42
  self.connection = None
42
43
  self.is_connected = False
44
+ self.thread_safe = True
43
45
 
44
- confluence_pages_data = ConfluencePagesTable(self)
45
- self._register_table("pages", confluence_pages_data)
46
+ self._register_table("spaces", ConfluenceSpacesTable(self))
47
+ self._register_table("pages", ConfluencePagesTable(self))
48
+ self._register_table("blogposts", ConfluenceBlogPostsTable(self))
49
+ self._register_table("whiteboards", ConfluenceWhiteboardsTable(self))
50
+ self._register_table("databases", ConfluenceDatabasesTable(self))
51
+ self._register_table("tasks", ConfluenceTasksTable(self))
46
52
 
47
- def connect(self):
48
- """Set up the connection required by the handler.
49
- Returns
50
- -------
51
- StatusResponse
52
- connection object
53
+ def connect(self) -> ConfluenceAPIClient:
54
+ """
55
+ Establishes a connection to the Confluence API.
56
+
57
+ Raises:
58
+ ValueError: If the required connection parameters are not provided.
59
+
60
+ Returns:
61
+ atlassian.confluence.Confluence: A connection object to the Confluence API.
53
62
  """
54
63
  if self.is_connected is True:
55
64
  return self.connection
56
-
57
- if not all(key in self.connection_data and self.connection_data.get(key) for key in ['url', 'username', 'password']):
58
- raise ValueError('Required parameters (url, username, password) must be provided and should not be empty.')
59
-
60
- conf = Confluence(
61
- url=self.connection_data.get('url'),
65
+
66
+ if not all(key in self.connection_data and self.connection_data.get(key) for key in ['api_base', 'username', 'password']):
67
+ raise ValueError('Required parameters (api_base, username, password) must be provided and should not be empty.')
68
+
69
+ self.connection = ConfluenceAPIClient(
70
+ url=self.connection_data.get('api_base'),
62
71
  username=self.connection_data.get('username'),
63
72
  password=self.connection_data.get('password'),
64
73
  )
65
- self.connection = conf
74
+
66
75
  self.is_connected = True
67
76
  return self.connection
68
77
 
69
78
  def check_connection(self) -> StatusResponse:
70
- """Check connection to the handler.
71
- Returns
72
- -------
73
- StatusResponse
74
- Status confirmation
79
+ """
80
+ Checks the status of the connection to the Confluence API.
81
+
82
+ Returns:
83
+ StatusResponse: An object containing the success status and an error message if an error occurs.
75
84
  """
76
85
  response = StatusResponse(False)
77
- need_to_close = self.is_connected is False
78
86
 
79
87
  try:
80
- self.connect()
88
+ connection = self.connect()
89
+ connection.get_spaces(limit=1)
81
90
  response.success = True
82
91
  except Exception as e:
83
92
  logger.error(f"Error connecting to Confluence API: {e}!")
@@ -86,17 +95,3 @@ class ConfluenceHandler(APIHandler):
86
95
  self.is_connected = response.success
87
96
 
88
97
  return response
89
-
90
- def native_query(self, query: str) -> StatusResponse:
91
- """Receive and process a raw query.
92
- Parameters
93
- ----------
94
- query : str
95
- query in a native format
96
- Returns
97
- -------
98
- StatusResponse
99
- Request status
100
- """
101
- ast = parse_sql(query)
102
- return self.query(ast)