|
8 | 8 | import urllib |
9 | 9 | from copy import deepcopy |
10 | 10 | from requests import Timeout |
| 11 | +from urllib.parse import urlparse |
| 12 | +from hyper import HTTPConnection |
| 13 | + |
11 | 14 |
|
12 | 15 | from ksql.builder import SQLBuilder |
13 | 16 | from ksql.errors import CreateError, InvalidQueryError, KSQLError |
@@ -65,6 +68,42 @@ def ksql(self, ksql_string, stream_properties=None): |
65 | 68 | res = json.loads(response) |
66 | 69 | return res |
67 | 70 |
|
| 71 | + def query2(self, query_string, encoding="utf-8", chunk_size=128, stream_properties=None, idle_timeout=None): |
| 72 | + """ |
| 73 | + Process streaming incoming data with HTTP/2. |
| 74 | +
|
| 75 | + """ |
| 76 | + parsed_uri = urlparse(self.url) |
| 77 | + |
| 78 | + logging.debug("KSQL generated: {}".format(query_string)) |
| 79 | + sql_string = self._validate_sql_string(query_string) |
| 80 | + body = {"sql": sql_string} |
| 81 | + if stream_properties: |
| 82 | + body["properties"] = stream_properties |
| 83 | + else: |
| 84 | + body["properties"] = {} |
| 85 | + |
| 86 | + with HTTPConnection(parsed_uri.netloc) as connection: |
| 87 | + streaming_response = self._request2( |
| 88 | + endpoint="query-stream", body=body, connection=connection |
| 89 | + ) |
| 90 | + start_idle = None |
| 91 | + |
| 92 | + if streaming_response.status == 200: |
| 93 | + for chunk in streaming_response.read_chunked(): |
| 94 | + if chunk != b"\n": |
| 95 | + start_idle = None |
| 96 | + yield chunk.decode(encoding) |
| 97 | + |
| 98 | + else: |
| 99 | + if not start_idle: |
| 100 | + start_idle = time.time() |
| 101 | + if idle_timeout and time.time() - start_idle > idle_timeout: |
| 102 | + print("Ending query because of time out! ({} seconds)".format(idle_timeout)) |
| 103 | + return |
| 104 | + else: |
| 105 | + raise ValueError("Return code is {}.".format(streaming_response.status)) |
| 106 | + |
68 | 107 | def query(self, query_string, encoding="utf-8", chunk_size=128, stream_properties=None, idle_timeout=None): |
69 | 108 | """ |
70 | 109 | Process streaming incoming data. |
@@ -93,6 +132,20 @@ def get_request(self, endpoint): |
93 | 132 | auth = (self.api_key, self.secret) if self.api_key or self.secret else None |
94 | 133 | return requests.get(endpoint, headers=self.headers, auth=auth) |
95 | 134 |
|
| 135 | + def _request2(self, endpoint, connection, body, method="POST", encoding="utf-8"): |
| 136 | + url = "{}/{}".format(self.url, endpoint) |
| 137 | + data = json.dumps(body).encode(encoding) |
| 138 | + |
| 139 | + headers = deepcopy(self.headers) |
| 140 | + if self.api_key and self.secret: |
| 141 | + base64string = base64.b64encode(bytes("{}:{}".format(self.api_key, self.secret), "utf-8")).decode("utf-8") |
| 142 | + headers["Authorization"] = "Basic %s" % base64string |
| 143 | + |
| 144 | + connection.request(method=method.upper(), url=url, headers=headers, body=data) |
| 145 | + resp = connection.get_response() |
| 146 | + |
| 147 | + return resp |
| 148 | + |
96 | 149 | def _request(self, endpoint, method="POST", sql_string="", stream_properties=None, encoding="utf-8"): |
97 | 150 | url = "{}/{}".format(self.url, endpoint) |
98 | 151 |
|
@@ -122,10 +175,51 @@ def _request(self, endpoint, method="POST", sql_string="", stream_properties=Non |
122 | 175 | raise http_error |
123 | 176 | else: |
124 | 177 | logging.debug("content: {}".format(content)) |
125 | | - raise KSQLError(e=content.get("message"), error_code=content.get("error_code")) |
| 178 | + raise KSQLError(content.get("message"), content.get("error_code"), content.get("stackTrace")) |
126 | 179 | else: |
127 | 180 | return r |
128 | 181 |
|
| 182 | + def close_query(self, query_id): |
| 183 | + body = {"queryId": query_id} |
| 184 | + data = json.dumps(body).encode("utf-8") |
| 185 | + url = "{}/{}".format(self.url, "close-query") |
| 186 | + |
| 187 | + response = requests.post(url=url, data=data) |
| 188 | + |
| 189 | + if response.status_code == 200: |
| 190 | + logging.debug("Successfully canceled Query ID: {}".format(query_id)) |
| 191 | + return True |
| 192 | + elif response.status_code == 400: |
| 193 | + message = json.loads(response.content)["message"] |
| 194 | + logging.debug("Failed canceling Query ID: {}: {}".format(query_id, message)) |
| 195 | + return False |
| 196 | + else: |
| 197 | + raise ValueError("Return code is {}.".format(response.status_code)) |
| 198 | + |
| 199 | + def inserts_stream(self, stream_name, rows): |
| 200 | + body = '{{"target":"{}"}}'.format(stream_name) |
| 201 | + for row in rows: |
| 202 | + body += '\n{}'.format(json.dumps(row)) |
| 203 | + |
| 204 | + parsed_uri = urlparse(self.url) |
| 205 | + url = "{}/{}".format(self.url, "inserts-stream") |
| 206 | + headers = deepcopy(self.headers) |
| 207 | + with HTTPConnection(parsed_uri.netloc) as connection: |
| 208 | + connection.request("POST", url, bytes(body, "utf-8"), headers) |
| 209 | + response = connection.get_response() |
| 210 | + result = response.read() |
| 211 | + |
| 212 | + result_str = result.decode("utf-8") |
| 213 | + result_chunks = result_str.split("\n") |
| 214 | + return_arr = [] |
| 215 | + for chunk in result_chunks: |
| 216 | + try: |
| 217 | + return_arr.append(json.loads(chunk)) |
| 218 | + except: |
| 219 | + pass |
| 220 | + |
| 221 | + return return_arr |
| 222 | + |
129 | 223 | @staticmethod |
130 | 224 | def retry(exceptions, delay=1, max_retries=5): |
131 | 225 | """ |
|
0 commit comments