Coverage for mindsdb / integrations / handlers / serpstack_handler / serpstack_handler.py: 0%
64 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
1import requests
3from mindsdb.utilities import log
4from mindsdb_sql_parser import parse_sql
6from mindsdb.integrations.libs.api_handler import APIHandler
8from mindsdb.integrations.libs.response import (
9 HandlerStatusResponse as StatusResponse,
10 HandlerResponse as Response
11)
13from .serpstack_tables import (
14 OrganicResultsTable, ImageResultsTable,
15 VideoResultsTable, NewsResultsTable, ShoppingResultsTable
16)
19logger = log.getLogger(__name__)
22class SerpstackHandler(APIHandler):
23 """A class for handling connections and interactions with the Serpstack API.
24 Attributes:
25 api_key (str): API access key for the Serpstack API.
26 is_connected (bool): Whether or not the API client is connected to Serpstack.
27 """
29 name = 'serpstack'
31 def __init__(self, name: str = None, **kwargs):
32 super().__init__(name)
34 connection_data = kwargs.get('connection_data', {})
35 self.connection_data = connection_data
37 self.access_key = None
38 self.base_url = None
39 self.is_connected = False
41 if 'access_key' in self.connection_data:
42 self.access_key = self.connection_data['access_key']
44 # register tables
45 organic_results = OrganicResultsTable(self)
46 self._register_table('organic_results', organic_results)
48 image_results = ImageResultsTable(self)
49 self._register_table('image_results', image_results)
51 video_results = VideoResultsTable(self)
52 self._register_table('video_results', video_results)
54 news_results = NewsResultsTable(self)
55 self._register_table('news_results', news_results)
57 shopping_results = ShoppingResultsTable(self)
58 self._register_table('shopping_results', shopping_results)
60 def connect(self):
61 """ Sets up connection and returns the base URL to be used"""
63 if self.is_connected:
64 return self.base_url
66 if not self.access_key:
67 logger.error("No access key provided for Serpstack API")
68 return None
70 try:
71 url = f"https://api.serpstack.com/search?access_key={self.access_key}"
72 api_request = requests.get(url)
73 api_response = api_request.json()
74 # error 105 means that user is on a free plan
75 if api_response['error']['code'] == 105:
76 self.base_url = "http://api.serpstack.com/search"
77 # error 310 means that missing search query, which means that user can use https
78 elif api_response['error']['code'] == 310:
79 self.base_url = "https://api.serpstack.com/search"
80 # any other error suggests issues with the account
81 else:
82 logger.error(f"Failed to connect to Serpstack API: {api_response['error']['info']}")
83 return None
85 self.is_connected = True
86 return self.base_url
88 except Exception as e:
89 logger.error(f"Failed to connect to Serpstack API: {str(e)}")
90 return None
92 def check_connection(self) -> StatusResponse:
93 """ Checks connection to Serpstack API"""
94 response = StatusResponse(False)
96 try:
97 self.connect()
98 response.success = True
99 response.copy_storage = True
100 except Exception as e:
101 response.error_message = (
102 f"Failed to connect to Serpstack API: {str(e)}"
103 )
104 logger.error(response.error_message)
105 response.success = False
107 self.is_connected = response.success
109 return response
111 def native_query(self, query: str = None) -> Response:
112 """Receive and process a raw query.
113 Parameters
114 ----------
115 query : str
116 query in a native format
117 Returns
118 -------
119 StatusResponse
120 Request status
121 """
122 ast = parse_sql(query)
123 return self.query(ast)