MindsDB 25.7.4.0__py3-none-any.whl → 25.8.2.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 MindsDB might be problematic. Click here for more details.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +11 -1
- mindsdb/api/executor/command_executor.py +9 -15
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +21 -24
- mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +9 -3
- mindsdb/api/executor/sql_query/steps/subselect_step.py +11 -8
- mindsdb/api/executor/utilities/mysql_to_duckdb_functions.py +264 -0
- mindsdb/api/executor/utilities/sql.py +30 -0
- mindsdb/api/http/initialize.py +2 -1
- mindsdb/api/http/namespaces/views.py +56 -72
- mindsdb/integrations/handlers/db2_handler/db2_handler.py +19 -23
- mindsdb/integrations/handlers/gong_handler/__about__.py +2 -0
- mindsdb/integrations/handlers/gong_handler/__init__.py +30 -0
- mindsdb/integrations/handlers/gong_handler/connection_args.py +37 -0
- mindsdb/integrations/handlers/gong_handler/gong_handler.py +164 -0
- mindsdb/integrations/handlers/gong_handler/gong_tables.py +508 -0
- mindsdb/integrations/handlers/gong_handler/icon.svg +25 -0
- mindsdb/integrations/handlers/gong_handler/test_gong_handler.py +125 -0
- mindsdb/integrations/handlers/huggingface_handler/__init__.py +8 -12
- mindsdb/integrations/handlers/huggingface_handler/finetune.py +203 -223
- mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +360 -383
- mindsdb/integrations/handlers/huggingface_handler/requirements.txt +7 -7
- mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +7 -7
- mindsdb/integrations/handlers/huggingface_handler/settings.py +25 -25
- mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +1 -2
- mindsdb/integrations/handlers/openai_handler/constants.py +11 -30
- mindsdb/integrations/handlers/openai_handler/helpers.py +27 -34
- mindsdb/integrations/handlers/openai_handler/openai_handler.py +14 -12
- mindsdb/integrations/handlers/salesforce_handler/constants.py +9 -2
- mindsdb/integrations/libs/llm/config.py +0 -14
- mindsdb/integrations/libs/llm/utils.py +0 -15
- mindsdb/integrations/utilities/files/file_reader.py +5 -19
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +1 -1
- mindsdb/interfaces/agents/agents_controller.py +83 -45
- mindsdb/interfaces/agents/constants.py +0 -1
- mindsdb/interfaces/agents/langchain_agent.py +1 -3
- mindsdb/interfaces/database/projects.py +111 -7
- mindsdb/interfaces/knowledge_base/controller.py +7 -1
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +6 -10
- mindsdb/interfaces/knowledge_base/preprocessing/text_splitter.py +73 -0
- mindsdb/interfaces/query_context/context_controller.py +14 -15
- mindsdb/utilities/config.py +2 -0
- mindsdb/utilities/fs.py +54 -17
- {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.2.0.dist-info}/METADATA +278 -263
- {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.2.0.dist-info}/RECORD +49 -48
- mindsdb/integrations/handlers/anyscale_endpoints_handler/__about__.py +0 -9
- mindsdb/integrations/handlers/anyscale_endpoints_handler/__init__.py +0 -20
- mindsdb/integrations/handlers/anyscale_endpoints_handler/anyscale_endpoints_handler.py +0 -290
- mindsdb/integrations/handlers/anyscale_endpoints_handler/creation_args.py +0 -14
- mindsdb/integrations/handlers/anyscale_endpoints_handler/icon.svg +0 -4
- mindsdb/integrations/handlers/anyscale_endpoints_handler/requirements.txt +0 -2
- mindsdb/integrations/handlers/anyscale_endpoints_handler/settings.py +0 -51
- mindsdb/integrations/handlers/anyscale_endpoints_handler/tests/test_anyscale_endpoints_handler.py +0 -212
- /mindsdb/integrations/handlers/{anyscale_endpoints_handler/tests/__init__.py → gong_handler/requirements.txt} +0 -0
- {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.2.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -10,143 +10,127 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
|
|
|
10
10
|
from mindsdb.utilities.exception import EntityNotExistsError
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@ns_conf.route(
|
|
13
|
+
@ns_conf.route("/<project_name>/views")
|
|
14
14
|
class ViewsList(Resource):
|
|
15
|
-
@ns_conf.doc(
|
|
16
|
-
@api_endpoint_metrics(
|
|
15
|
+
@ns_conf.doc("list_views")
|
|
16
|
+
@api_endpoint_metrics("GET", "/views")
|
|
17
17
|
def get(self, project_name):
|
|
18
|
-
|
|
18
|
+
"""List all views"""
|
|
19
19
|
session = SessionController()
|
|
20
20
|
try:
|
|
21
21
|
project = session.database_controller.get_project(project_name)
|
|
22
22
|
except EntityNotExistsError:
|
|
23
|
-
return http_error(
|
|
24
|
-
HTTPStatus.NOT_FOUND,
|
|
25
|
-
'Project not found',
|
|
26
|
-
f'Project name {project_name} does not exist'
|
|
27
|
-
)
|
|
23
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
28
24
|
|
|
29
25
|
all_views = project.get_views()
|
|
30
26
|
all_view_objs = []
|
|
31
27
|
# Only want to return relevant fields to the user.
|
|
32
28
|
for view in all_views:
|
|
33
|
-
all_view_objs.append({
|
|
34
|
-
'id': view['metadata']['id'],
|
|
35
|
-
'name': view['name'],
|
|
36
|
-
'query': view['query']
|
|
37
|
-
})
|
|
29
|
+
all_view_objs.append({"id": view["metadata"]["id"], "name": view["name"], "query": view["query"]})
|
|
38
30
|
return all_view_objs
|
|
39
31
|
|
|
40
|
-
@ns_conf.doc(
|
|
41
|
-
@api_endpoint_metrics(
|
|
32
|
+
@ns_conf.doc("create_view")
|
|
33
|
+
@api_endpoint_metrics("POST", "/views")
|
|
42
34
|
def post(self, project_name):
|
|
43
|
-
|
|
44
|
-
if
|
|
45
|
-
return http_error(HTTPStatus.BAD_REQUEST,
|
|
35
|
+
"""Create a new view"""
|
|
36
|
+
if "view" not in request.json:
|
|
37
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "view" parameter in POST body')
|
|
46
38
|
session = SessionController()
|
|
47
|
-
view_obj = request.json[
|
|
48
|
-
if
|
|
49
|
-
return http_error(HTTPStatus.BAD_REQUEST,
|
|
50
|
-
if
|
|
51
|
-
return http_error(HTTPStatus.BAD_REQUEST,
|
|
52
|
-
name = view_obj[
|
|
53
|
-
query = view_obj[
|
|
39
|
+
view_obj = request.json["view"]
|
|
40
|
+
if "name" not in view_obj:
|
|
41
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "name" field for view')
|
|
42
|
+
if "query" not in view_obj:
|
|
43
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "query" field for view')
|
|
44
|
+
name = view_obj["name"]
|
|
45
|
+
query = view_obj["query"]
|
|
54
46
|
|
|
55
47
|
try:
|
|
56
48
|
project = session.database_controller.get_project(project_name)
|
|
57
49
|
except EntityNotExistsError:
|
|
58
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
50
|
+
return http_error(HTTPStatus.NOT_FOUND, "Not found", f"Project name {project_name} does not exist")
|
|
59
51
|
|
|
60
52
|
if project.get_view(name) is not None:
|
|
61
|
-
return http_error(HTTPStatus.CONFLICT,
|
|
53
|
+
return http_error(HTTPStatus.CONFLICT, "Name conflict", f"View with name {name} already exists.")
|
|
62
54
|
|
|
63
|
-
project.create_view(name, query)
|
|
55
|
+
project.create_view(name, query, session)
|
|
64
56
|
created_view = project.get_view(name)
|
|
65
57
|
# Only want to return relevant fields to the user.
|
|
66
58
|
return {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
"id": created_view["metadata"]["id"],
|
|
60
|
+
"name": created_view["name"],
|
|
61
|
+
"query": created_view["query"],
|
|
70
62
|
}, HTTPStatus.CREATED
|
|
71
63
|
|
|
72
64
|
|
|
73
|
-
@ns_conf.route(
|
|
74
|
-
@ns_conf.param(
|
|
75
|
-
@ns_conf.param(
|
|
65
|
+
@ns_conf.route("/<project_name>/views/<view_name>")
|
|
66
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
67
|
+
@ns_conf.param("view_name", "Name of the view")
|
|
76
68
|
class ViewResource(Resource):
|
|
77
|
-
@ns_conf.doc(
|
|
78
|
-
@api_endpoint_metrics(
|
|
69
|
+
@ns_conf.doc("get_view")
|
|
70
|
+
@api_endpoint_metrics("GET", "/views/view")
|
|
79
71
|
def get(self, project_name, view_name):
|
|
80
|
-
|
|
72
|
+
"""Get a view by name"""
|
|
81
73
|
session = SessionController()
|
|
82
74
|
try:
|
|
83
75
|
project = session.database_controller.get_project(project_name)
|
|
84
76
|
except EntityNotExistsError:
|
|
85
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
77
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
86
78
|
|
|
87
79
|
view = project.get_view(view_name)
|
|
88
80
|
if view is None:
|
|
89
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
81
|
+
return http_error(HTTPStatus.NOT_FOUND, "View not found", f"View with name {view_name} does not exist")
|
|
90
82
|
|
|
91
83
|
# Only want to return relevant fields to the user.
|
|
92
|
-
return {
|
|
93
|
-
'id': view['metadata']['id'],
|
|
94
|
-
'name': view['name'],
|
|
95
|
-
'query': view['query']
|
|
96
|
-
}
|
|
84
|
+
return {"id": view["metadata"]["id"], "name": view["name"], "query": view["query"]}
|
|
97
85
|
|
|
98
|
-
@ns_conf.doc(
|
|
99
|
-
@api_endpoint_metrics(
|
|
86
|
+
@ns_conf.doc("update_view")
|
|
87
|
+
@api_endpoint_metrics("PUT", "/views/view")
|
|
100
88
|
def put(self, project_name, view_name):
|
|
101
|
-
|
|
102
|
-
if
|
|
103
|
-
return http_error(HTTPStatus.BAD_REQUEST,
|
|
104
|
-
request_view = request.json[
|
|
89
|
+
"""Updates or creates a view"""
|
|
90
|
+
if "view" not in request.json:
|
|
91
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "view" parameter in PUT body')
|
|
92
|
+
request_view = request.json["view"]
|
|
105
93
|
session = SessionController()
|
|
106
94
|
try:
|
|
107
95
|
project = session.database_controller.get_project(project_name)
|
|
108
96
|
except EntityNotExistsError:
|
|
109
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
97
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
110
98
|
|
|
111
99
|
existing_view = project.get_view(view_name)
|
|
112
100
|
if existing_view is None:
|
|
113
101
|
# Create
|
|
114
|
-
if
|
|
115
|
-
return http_error(HTTPStatus.BAD_REQUEST,
|
|
116
|
-
project.create_view(view_name, request_view[
|
|
102
|
+
if "query" not in request_view:
|
|
103
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "query" field for new view')
|
|
104
|
+
project.create_view(view_name, request_view["query"], session)
|
|
117
105
|
created_view = project.get_view(view_name)
|
|
118
106
|
# Only want to return relevant fields to the user.
|
|
119
107
|
return {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
108
|
+
"id": created_view["metadata"]["id"],
|
|
109
|
+
"name": created_view["name"],
|
|
110
|
+
"query": created_view["query"],
|
|
123
111
|
}, HTTPStatus.CREATED
|
|
124
112
|
|
|
125
|
-
new_query = existing_view[
|
|
126
|
-
if
|
|
127
|
-
new_query = request_view[
|
|
113
|
+
new_query = existing_view["query"]
|
|
114
|
+
if "query" in request_view:
|
|
115
|
+
new_query = request_view["query"]
|
|
128
116
|
project.update_view(view_name, new_query)
|
|
129
117
|
|
|
130
118
|
existing_view = project.get_view(view_name)
|
|
131
119
|
# Only want to return relevant fields to the user.
|
|
132
|
-
return {
|
|
133
|
-
'id': existing_view['metadata']['id'],
|
|
134
|
-
'name': existing_view['name'],
|
|
135
|
-
'query': existing_view['query']
|
|
136
|
-
}
|
|
120
|
+
return {"id": existing_view["metadata"]["id"], "name": existing_view["name"], "query": existing_view["query"]}
|
|
137
121
|
|
|
138
|
-
@ns_conf.doc(
|
|
139
|
-
@api_endpoint_metrics(
|
|
122
|
+
@ns_conf.doc("delete_view")
|
|
123
|
+
@api_endpoint_metrics("DELETE", "/views/view")
|
|
140
124
|
def delete(self, project_name, view_name):
|
|
141
|
-
|
|
125
|
+
"""Deletes a view by name"""
|
|
142
126
|
session = SessionController()
|
|
143
127
|
try:
|
|
144
128
|
project = session.database_controller.get_project(project_name)
|
|
145
129
|
except EntityNotExistsError:
|
|
146
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
130
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
147
131
|
|
|
148
132
|
if project.get_view(view_name) is None:
|
|
149
|
-
return http_error(HTTPStatus.NOT_FOUND,
|
|
133
|
+
return http_error(HTTPStatus.NOT_FOUND, "View not found", f"View with name {view_name} does not exist")
|
|
150
134
|
|
|
151
135
|
project.delete_view(view_name)
|
|
152
|
-
return
|
|
136
|
+
return "", HTTPStatus.NO_CONTENT
|
|
@@ -59,16 +59,20 @@ class DB2Handler(DatabaseHandler):
|
|
|
59
59
|
return self.connection
|
|
60
60
|
|
|
61
61
|
# Mandatory connection parameters.
|
|
62
|
-
if not all(key in self.connection_data for key in [
|
|
63
|
-
raise ValueError(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
connection_string
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
if not all(key in self.connection_data for key in ["host", "user", "password", "database"]):
|
|
63
|
+
raise ValueError("Required parameters (host, user, password, database) must be provided.")
|
|
64
|
+
cloud = "databases.appdomain.cloud" in self.connection_data["host"]
|
|
65
|
+
if cloud:
|
|
66
|
+
connection_string = f"DATABASE={self.connection_data['database']};HOSTNAME={self.connection_data['host']};PORT={self.connection_data['port']};PROTOCOL=TCPIP;UID={self.connection_data['user']};PWD={self.connection_data['password']};SECURITY=SSL;"
|
|
67
|
+
connection_string += "SSLSERVERCERTIFICATE=;"
|
|
68
|
+
else:
|
|
69
|
+
connection_string = f"DRIVER={'IBM DB2 ODBC DRIVER'};DATABASE={self.connection_data['database']};HOST={self.connection_data['host']};PROTOCOL=TCPIP;UID={self.connection_data['user']};PWD={self.connection_data['password']};"
|
|
70
|
+
|
|
71
|
+
# Optional connection parameters.
|
|
72
|
+
if "port" in self.connection_data:
|
|
73
|
+
connection_string += f"PORT={self.connection_data['port']};"
|
|
74
|
+
|
|
75
|
+
if "schema" in self.connection_data:
|
|
72
76
|
connection_string += f"CURRENTSCHEMA={self.connection_data['schema']};"
|
|
73
77
|
|
|
74
78
|
try:
|
|
@@ -106,10 +110,10 @@ class DB2Handler(DatabaseHandler):
|
|
|
106
110
|
self.connect()
|
|
107
111
|
response.success = True
|
|
108
112
|
except (OperationalError, ValueError) as known_error:
|
|
109
|
-
logger.error(f
|
|
113
|
+
logger.error(f"Connection check to IBM Db2 failed, {known_error}!")
|
|
110
114
|
response.error_message = str(known_error)
|
|
111
115
|
except Exception as unknown_error:
|
|
112
|
-
logger.error(f
|
|
116
|
+
logger.error(f"Connection check to IBM Db2 failed due to an unknown error, {unknown_error}!")
|
|
113
117
|
response.error_message = str(unknown_error)
|
|
114
118
|
|
|
115
119
|
if response.success and need_to_close:
|
|
@@ -141,9 +145,7 @@ class DB2Handler(DatabaseHandler):
|
|
|
141
145
|
result = cur.fetchall()
|
|
142
146
|
response = Response(
|
|
143
147
|
RESPONSE_TYPE.TABLE,
|
|
144
|
-
data_frame=pd.DataFrame(
|
|
145
|
-
result, columns=[x[0] for x in cur.description]
|
|
146
|
-
),
|
|
148
|
+
data_frame=pd.DataFrame(result, columns=[x[0] for x in cur.description]),
|
|
147
149
|
)
|
|
148
150
|
else:
|
|
149
151
|
response = Response(RESPONSE_TYPE.OK)
|
|
@@ -198,10 +200,7 @@ class DB2Handler(DatabaseHandler):
|
|
|
198
200
|
}
|
|
199
201
|
)
|
|
200
202
|
|
|
201
|
-
response = Response(
|
|
202
|
-
RESPONSE_TYPE.TABLE,
|
|
203
|
-
data_frame=pd.DataFrame(tables)
|
|
204
|
-
)
|
|
203
|
+
response = Response(RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(tables))
|
|
205
204
|
|
|
206
205
|
return response
|
|
207
206
|
|
|
@@ -227,9 +226,6 @@ class DB2Handler(DatabaseHandler):
|
|
|
227
226
|
|
|
228
227
|
columns = [column["COLUMN_NAME"] for column in result]
|
|
229
228
|
|
|
230
|
-
response = Response(
|
|
231
|
-
RESPONSE_TYPE.TABLE,
|
|
232
|
-
data_frame=pd.DataFrame(columns, columns=["COLUMN_NAME"])
|
|
233
|
-
)
|
|
229
|
+
response = Response(RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(columns, columns=["COLUMN_NAME"]))
|
|
234
230
|
|
|
235
231
|
return response
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from mindsdb.integrations.libs.const import HANDLER_TYPE
|
|
2
|
+
|
|
3
|
+
from .__about__ import __version__ as version, __description__ as description
|
|
4
|
+
from .connection_args import connection_args, connection_args_example
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from .gong_handler import GongHandler as Handler
|
|
8
|
+
|
|
9
|
+
import_error = None
|
|
10
|
+
except Exception as e:
|
|
11
|
+
Handler = None
|
|
12
|
+
import_error = e
|
|
13
|
+
|
|
14
|
+
title = "Gong"
|
|
15
|
+
name = "gong"
|
|
16
|
+
type = HANDLER_TYPE.DATA
|
|
17
|
+
icon_path = "icon.svg"
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Handler",
|
|
21
|
+
"version",
|
|
22
|
+
"name",
|
|
23
|
+
"type",
|
|
24
|
+
"title",
|
|
25
|
+
"description",
|
|
26
|
+
"import_error",
|
|
27
|
+
"icon_path",
|
|
28
|
+
"connection_args_example",
|
|
29
|
+
"connection_args",
|
|
30
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
|
|
3
|
+
from mindsdb.integrations.libs.const import HANDLER_CONNECTION_ARG_TYPE as ARG_TYPE
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
connection_args = OrderedDict(
|
|
7
|
+
api_key={
|
|
8
|
+
"type": ARG_TYPE.PWD,
|
|
9
|
+
"description": "Gong API key for authentication.",
|
|
10
|
+
"secret": True,
|
|
11
|
+
"required": False,
|
|
12
|
+
"label": "API Key",
|
|
13
|
+
},
|
|
14
|
+
# Basic Authentication with Access Key + Secret Key (Option 2)
|
|
15
|
+
access_key={
|
|
16
|
+
"type": ARG_TYPE.STR,
|
|
17
|
+
"description": "Gong Access Key for basic authentication (if not using OAuth).",
|
|
18
|
+
"secret": True,
|
|
19
|
+
"required": False,
|
|
20
|
+
"label": "Access Key",
|
|
21
|
+
},
|
|
22
|
+
secret_key={
|
|
23
|
+
"type": ARG_TYPE.PWD,
|
|
24
|
+
"description": "Gong Secret Key for basic authentication (if not using OAuth).",
|
|
25
|
+
"secret": True,
|
|
26
|
+
"required": False,
|
|
27
|
+
"label": "Secret Key",
|
|
28
|
+
},
|
|
29
|
+
base_url={
|
|
30
|
+
"type": ARG_TYPE.STR,
|
|
31
|
+
"description": "Gong API base URL (optional, defaults to production).",
|
|
32
|
+
"required": False,
|
|
33
|
+
"label": "Base URL",
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
connection_args_example = OrderedDict(api_key="your_gong_api_key_here", base_url="https://api.gong.io")
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
from mindsdb_sql_parser import parse_sql
|
|
5
|
+
|
|
6
|
+
from mindsdb.integrations.handlers.gong_handler.gong_tables import (
|
|
7
|
+
GongCallsTable,
|
|
8
|
+
GongUsersTable,
|
|
9
|
+
GongAnalyticsTable,
|
|
10
|
+
GongTranscriptsTable,
|
|
11
|
+
)
|
|
12
|
+
from mindsdb.integrations.libs.api_handler import APIHandler
|
|
13
|
+
from mindsdb.integrations.libs.response import (
|
|
14
|
+
HandlerResponse as Response,
|
|
15
|
+
HandlerStatusResponse as StatusResponse,
|
|
16
|
+
RESPONSE_TYPE,
|
|
17
|
+
)
|
|
18
|
+
from mindsdb.utilities import log
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
logger = log.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GongHandler(APIHandler):
|
|
25
|
+
"""
|
|
26
|
+
This handler handles the connection and execution of SQL statements on Gong.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
name = "gong"
|
|
30
|
+
|
|
31
|
+
def __init__(self, name: str, connection_data: Dict, **kwargs: Any) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Initializes the handler.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
name (Text): The name of the handler instance.
|
|
37
|
+
connection_data (Dict): The connection data required to connect to the Gong API.
|
|
38
|
+
kwargs: Arbitrary keyword arguments.
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(name)
|
|
41
|
+
self.connection_data = connection_data
|
|
42
|
+
self.kwargs = kwargs
|
|
43
|
+
|
|
44
|
+
self.connection = None
|
|
45
|
+
self.is_connected = False
|
|
46
|
+
self.base_url = connection_data.get("base_url", "https://api.gong.io")
|
|
47
|
+
|
|
48
|
+
# Support both bearer token and access key + secret key
|
|
49
|
+
self.bearer_token = connection_data.get("api_key")
|
|
50
|
+
self.access_key = connection_data.get("access_key")
|
|
51
|
+
self.secret_key = connection_data.get("secret_key")
|
|
52
|
+
|
|
53
|
+
# Register core tables
|
|
54
|
+
self._register_table("calls", GongCallsTable(self))
|
|
55
|
+
self._register_table("users", GongUsersTable(self))
|
|
56
|
+
self._register_table("analytics", GongAnalyticsTable(self))
|
|
57
|
+
self._register_table("transcripts", GongTranscriptsTable(self))
|
|
58
|
+
|
|
59
|
+
def connect(self) -> requests.Session:
|
|
60
|
+
"""
|
|
61
|
+
Establishes a connection to the Gong API.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: If the required connection parameters are not provided.
|
|
65
|
+
Exception: If a connection error occurs.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
requests.Session: A session object for making API requests.
|
|
69
|
+
"""
|
|
70
|
+
if self.is_connected is True:
|
|
71
|
+
return self.connection
|
|
72
|
+
|
|
73
|
+
if self.access_key and self.secret_key:
|
|
74
|
+
auth_method = "basic"
|
|
75
|
+
elif self.bearer_token:
|
|
76
|
+
auth_method = "bearer"
|
|
77
|
+
else:
|
|
78
|
+
raise ValueError("Either bearer_token or (access_key + secret_key) is required to connect to Gong API.")
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
self.connection = requests.Session()
|
|
82
|
+
|
|
83
|
+
if auth_method == "basic":
|
|
84
|
+
# Basic authentication with access key + secret key
|
|
85
|
+
self.connection.auth = (self.access_key, self.secret_key)
|
|
86
|
+
self.connection.headers.update({"Content-Type": "application/json", "Accept": "application/json"})
|
|
87
|
+
else:
|
|
88
|
+
# Bearer token authentication
|
|
89
|
+
self.connection.headers.update(
|
|
90
|
+
{
|
|
91
|
+
"Authorization": f"Bearer {self.bearer_token}",
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
"Accept": "application/json",
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
test_response = self.connection.get(f"{self.base_url}/v2/users")
|
|
98
|
+
test_response.raise_for_status()
|
|
99
|
+
|
|
100
|
+
self.is_connected = True
|
|
101
|
+
return self.connection
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
self.is_connected = False
|
|
105
|
+
logger.error(f"Error connecting to Gong API: {e}")
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
def check_connection(self) -> StatusResponse:
|
|
109
|
+
"""
|
|
110
|
+
Checks the status of the connection to the Gong API.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
StatusResponse: An object containing the success status and an error message if an error occurs.
|
|
114
|
+
"""
|
|
115
|
+
response = StatusResponse(False)
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
self.connect()
|
|
119
|
+
# Test the connection by making a simple API call
|
|
120
|
+
test_response = self.connection.get(f"{self.base_url}/v2/users")
|
|
121
|
+
test_response.raise_for_status()
|
|
122
|
+
response.success = True
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.error(f"Connection check to Gong failed: {e}")
|
|
125
|
+
response.error_message = str(e)
|
|
126
|
+
|
|
127
|
+
self.is_connected = response.success
|
|
128
|
+
return response
|
|
129
|
+
|
|
130
|
+
def native_query(self, query: str) -> Response:
|
|
131
|
+
"""
|
|
132
|
+
Executes a native query on Gong and returns the result.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
query (Text): The SQL query to be executed.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Response: A response object containing the result of the query or an error message.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
ast = parse_sql(query)
|
|
142
|
+
return self.query(ast)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.error(f"Error running query: {query} on Gong: {e}")
|
|
145
|
+
return Response(RESPONSE_TYPE.ERROR, error_code=0, error_message=str(e))
|
|
146
|
+
|
|
147
|
+
def call_gong_api(self, endpoint: str, params: Dict = None) -> Dict:
|
|
148
|
+
"""
|
|
149
|
+
Makes a call to the Gong API.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
endpoint (str): The API endpoint to call.
|
|
153
|
+
params (Dict): Query parameters for the API call.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Dict: The API response.
|
|
157
|
+
"""
|
|
158
|
+
if not self.is_connected:
|
|
159
|
+
self.connect()
|
|
160
|
+
|
|
161
|
+
url = f"{self.base_url}{endpoint}"
|
|
162
|
+
response = self.connection.get(url, params=params)
|
|
163
|
+
response.raise_for_status()
|
|
164
|
+
return response.json()
|