fixed post request v2 command, added a http request base to inherit from, and mypy and ruff are now passing w/ burnettk
This commit is contained in:
parent
88229a02e3
commit
847aa7c266
|
@ -1,7 +1,8 @@
|
|||
import pytest
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sleepless(monkeypatch: Any) -> None:
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
|
||||
import requests
|
||||
from typing import Any
|
||||
|
||||
import requests # type: ignore
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDictV1
|
||||
from spiffworkflow_connector_command.command_interface import ConnectorCommand
|
||||
|
||||
|
||||
class GetRequest:
|
||||
class GetRequest(ConnectorCommand):
|
||||
def __init__(self,
|
||||
url: str,
|
||||
headers: dict[str, str] | None = None,
|
||||
|
@ -16,7 +20,7 @@ class GetRequest:
|
|||
self.basic_auth_username = basic_auth_username
|
||||
self.basic_auth_password = basic_auth_password
|
||||
|
||||
def execute(self, config, task_data):
|
||||
def execute(self, _config: Any, _task_data: Any) -> CommandResultDictV1:
|
||||
auth = None
|
||||
if self.basic_auth_username is not None and self.basic_auth_password is not None:
|
||||
auth = (self.basic_auth_username, self.basic_auth_password)
|
||||
|
|
|
@ -1,118 +1,20 @@
|
|||
import json
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import requests # type: ignore
|
||||
from spiffworkflow_connector_command.command_interface import CommandErrorDict
|
||||
from spiffworkflow_connector_command.command_interface import CommandResponseDict
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDict
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDictV2
|
||||
from spiffworkflow_connector_command.command_interface import ConnectorCommand
|
||||
|
||||
from connector_http.http_request_base import HttpRequestBase
|
||||
|
||||
class GetRequestV2(ConnectorCommand):
|
||||
|
||||
class GetRequestV2(ConnectorCommand, HttpRequestBase):
|
||||
def __init__(self,
|
||||
url: str,
|
||||
headers: dict[str, str] | None = None,
|
||||
params: dict[str, str] | None = None,
|
||||
basic_auth_username: str | None = None,
|
||||
basic_auth_password: str | None = None,
|
||||
attempts: int | None = None,
|
||||
attempts: int | None = None, **kwargs: Any
|
||||
):
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self.params = params or {}
|
||||
self.basic_auth_username = basic_auth_username
|
||||
self.basic_auth_password = basic_auth_password
|
||||
|
||||
HttpRequestBase.__init__(self, **kwargs)
|
||||
if not isinstance(attempts, int) or attempts < 1 or attempts > 10:
|
||||
attempts = 1
|
||||
|
||||
self.attempts = attempts
|
||||
|
||||
def execute(self, _config: Any, _task_data: dict) -> CommandResultDict:
|
||||
logs = []
|
||||
|
||||
def log(msg: str) -> None:
|
||||
print(f"LOG: {msg}")
|
||||
logs.append(f"[{time.time()}] {msg}")
|
||||
|
||||
log("Will execute")
|
||||
|
||||
auth = None
|
||||
if self.basic_auth_username is not None and self.basic_auth_password is not None:
|
||||
auth = (self.basic_auth_username, self.basic_auth_password)
|
||||
log("Set auth")
|
||||
|
||||
attempt = 1
|
||||
command_response: dict = {}
|
||||
error: CommandErrorDict | None = None
|
||||
status = 0
|
||||
mimetype = "application/json"
|
||||
http_response = None
|
||||
while attempt <= self.attempts:
|
||||
command_response = {}
|
||||
status = 0
|
||||
if attempt > 1:
|
||||
log("Sleeping before next attempt")
|
||||
time.sleep(1)
|
||||
|
||||
log(f"Will attempt {attempt} of {self.attempts}")
|
||||
http_response = None
|
||||
|
||||
try:
|
||||
log(f"Will call {self.url}")
|
||||
http_response = requests.get(self.url, self.params, headers=self.headers, auth=auth, timeout=300)
|
||||
log(f"Did call {self.url}")
|
||||
|
||||
log("Will parse http_response")
|
||||
status = http_response.status_code
|
||||
except Exception as e:
|
||||
log(f"Did catch exception: {e}")
|
||||
error = self.create_error_from_exception(exception=e, http_response=http_response)
|
||||
if status < 300:
|
||||
status = 500
|
||||
finally:
|
||||
log(f"Did attempt {attempt} of {self.attempts}")
|
||||
|
||||
# check for 500 level status
|
||||
if status // 100 != 5:
|
||||
break
|
||||
|
||||
attempt += 1
|
||||
|
||||
log("Did execute")
|
||||
|
||||
if http_response is not None:
|
||||
command_response = {"raw_response": http_response.text}
|
||||
# this string can include modifiers like UTF-8, which is why it's not using ==
|
||||
if 'application/json' in http_response.headers.get('Content-Type', ''):
|
||||
try:
|
||||
command_response = json.loads(http_response.text)
|
||||
except Exception as e:
|
||||
error = self.create_error_from_exception(exception=e, http_response=http_response)
|
||||
log("Did parse http_response")
|
||||
|
||||
if status >= 400 and error is None:
|
||||
error = self.create_error(error_name=f"HttpError{status}", http_response=http_response)
|
||||
|
||||
return_response: CommandResponseDict = {
|
||||
"command_response": command_response,
|
||||
"spiff__logs": logs,
|
||||
"error": error,
|
||||
}
|
||||
result: CommandResultDict = {
|
||||
"response": return_response,
|
||||
"status": status,
|
||||
"mimetype": mimetype,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def create_error_from_exception(self, exception: Exception, http_response: requests.Response | None) -> CommandErrorDict:
|
||||
return self.create_error(error_name=exception.__class__.__name__, http_response=http_response, additional_message=str(exception))
|
||||
|
||||
def create_error(self, error_name: str, http_response: requests.Response | None, additional_message: str = "") -> CommandErrorDict:
|
||||
raw_response = http_response.text if http_response is not None else None
|
||||
message = f"Received Error: {additional_message}. Raw http_response was: {raw_response}"
|
||||
error: CommandErrorDict = {"error_name": error_name, "message": message}
|
||||
return error
|
||||
def execute(self, _config: Any, _task_data: dict) -> CommandResultDictV2:
|
||||
return self.run_request(requests.get)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Any
|
||||
|
||||
import requests
|
||||
import requests # type: ignore
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDictV1
|
||||
from spiffworkflow_connector_command.command_interface import ConnectorCommand
|
||||
|
||||
|
||||
class PostRequest:
|
||||
class PostRequest(ConnectorCommand):
|
||||
def __init__(self,
|
||||
url: str,
|
||||
headers: dict[str, str] | None,
|
||||
|
@ -17,7 +19,7 @@ class PostRequest:
|
|||
self.basic_auth_password = basic_auth_password
|
||||
self.data = data
|
||||
|
||||
def execute(self, config, task_data):
|
||||
def execute(self, _config: Any, _task_data: Any) -> CommandResultDictV1:
|
||||
auth = None
|
||||
if self.basic_auth_username is not None and self.basic_auth_password is not None:
|
||||
auth = (self.basic_auth_username, self.basic_auth_password)
|
||||
|
|
|
@ -1,66 +1,12 @@
|
|||
import json
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
import requests # type: ignore
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDictV2
|
||||
from spiffworkflow_connector_command.command_interface import ConnectorCommand
|
||||
|
||||
from connector_http.http_request_base import HttpRequestBase
|
||||
|
||||
|
||||
class PostRequestV2:
|
||||
def __init__(self,
|
||||
url: str,
|
||||
headers: dict[str, str] | None = None,
|
||||
basic_auth_username: str | None = None,
|
||||
basic_auth_password: str | None = None,
|
||||
data: dict[str, Any] | None = None,
|
||||
):
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self.basic_auth_username = basic_auth_username
|
||||
self.basic_auth_password = basic_auth_password
|
||||
self.data = data
|
||||
|
||||
def execute(self, config, task_data):
|
||||
logs = []
|
||||
|
||||
def log(msg):
|
||||
logs.append(f"[{time.time()}] {msg}")
|
||||
|
||||
response = {}
|
||||
status = 0
|
||||
mimetype = "application/json"
|
||||
|
||||
log("Will execute")
|
||||
|
||||
auth = None
|
||||
if self.basic_auth_username is not None and self.basic_auth_password is not None:
|
||||
auth = (self.basic_auth_username, self.basic_auth_password)
|
||||
log("basic auth has been set")
|
||||
|
||||
try:
|
||||
log(f"Will call {self.url} with data {self.data}")
|
||||
api_response = requests.post(self.url, headers=self.headers, auth=auth, json=self.data, timeout=300)
|
||||
log(f"Did call {self.url}")
|
||||
|
||||
log(f"Will parse response with status code {api_response.status_code}")
|
||||
status = api_response.status_code
|
||||
response = json.loads(api_response.text) if api_response.text else {}
|
||||
log("Did parse response")
|
||||
except Exception as e:
|
||||
log(f"Did catch exception: {e}")
|
||||
if len(response) == 0:
|
||||
response = f'{"error": {e}, "raw_response": {api_response.text}}',
|
||||
if status == 0:
|
||||
status = 500
|
||||
finally:
|
||||
log("Did execute")
|
||||
|
||||
result = {
|
||||
"response": {
|
||||
"api_response": response,
|
||||
"spiff__logs": logs,
|
||||
},
|
||||
"status": status,
|
||||
"mimetype": mimetype,
|
||||
}
|
||||
|
||||
return result
|
||||
class PostRequestV2(ConnectorCommand, HttpRequestBase):
|
||||
def execute(self, _config: Any, _task_data: dict) -> CommandResultDictV2:
|
||||
return self.run_request(requests.post)
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import json
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
|
||||
import requests # type: ignore
|
||||
from spiffworkflow_connector_command.command_interface import CommandErrorDict
|
||||
from spiffworkflow_connector_command.command_interface import CommandResultDictV2
|
||||
from spiffworkflow_connector_command.command_interface import ConnectorProxyResponseDict
|
||||
|
||||
|
||||
class HttpRequestBase:
|
||||
def __init__(self,
|
||||
url: str,
|
||||
headers: dict[str, str] | None = None,
|
||||
params: dict[str, str] | None = None,
|
||||
basic_auth_username: str | None = None,
|
||||
basic_auth_password: str | None = None,
|
||||
):
|
||||
self.url = url
|
||||
self.headers = headers or {}
|
||||
self.params = params or {}
|
||||
self.basic_auth_username = basic_auth_username
|
||||
self.basic_auth_password = basic_auth_password
|
||||
self.attempts = 1
|
||||
|
||||
def _create_error_from_exception(self, exception: Exception, http_response: requests.Response | None) -> CommandErrorDict:
|
||||
return self._create_error(
|
||||
error_name=exception.__class__.__name__, http_response=http_response, additional_message=str(exception)
|
||||
)
|
||||
|
||||
def _create_error(
|
||||
self, error_name: str, http_response: requests.Response | None, additional_message: str = ""
|
||||
) -> CommandErrorDict:
|
||||
raw_response = http_response.text if http_response is not None else None
|
||||
message = f"Received Error: {additional_message}. Raw http_response was: {raw_response}"
|
||||
error: CommandErrorDict = {"error_name": error_name, "message": message}
|
||||
return error
|
||||
|
||||
def run_request(self, request_function: Callable) -> CommandResultDictV2:
|
||||
logs = []
|
||||
|
||||
def log(msg: str) -> None:
|
||||
print(f"LOG: {msg}")
|
||||
logs.append(f"[{time.time()}] {msg}")
|
||||
|
||||
log("Will execute")
|
||||
|
||||
auth = None
|
||||
if self.basic_auth_username is not None and self.basic_auth_password is not None:
|
||||
auth = (self.basic_auth_username, self.basic_auth_password)
|
||||
log("Set auth")
|
||||
|
||||
attempt = 1
|
||||
command_response: dict = {}
|
||||
error: CommandErrorDict | None = None
|
||||
status = 0
|
||||
mimetype = "application/json"
|
||||
http_response = None
|
||||
while attempt <= self.attempts:
|
||||
command_response = {}
|
||||
status = 0
|
||||
if attempt > 1:
|
||||
log("Sleeping before next attempt")
|
||||
time.sleep(1)
|
||||
|
||||
log(f"Will attempt {attempt} of {self.attempts}")
|
||||
http_response = None
|
||||
|
||||
try:
|
||||
log(f"Will call {self.url}")
|
||||
http_response = request_function(self.url, self.params, headers=self.headers, auth=auth, timeout=300)
|
||||
log(f"Did call {self.url}")
|
||||
|
||||
log("Will parse http_response")
|
||||
status = http_response.status_code
|
||||
except Exception as e:
|
||||
log(f"Did catch exception: {e}")
|
||||
error = self._create_error_from_exception(exception=e, http_response=http_response)
|
||||
if status < 300:
|
||||
status = 500
|
||||
finally:
|
||||
log(f"Did attempt {attempt} of {self.attempts}")
|
||||
|
||||
# check for 500 level status
|
||||
if status // 100 != 5:
|
||||
break
|
||||
|
||||
attempt += 1
|
||||
|
||||
log("Did execute")
|
||||
|
||||
if http_response is not None:
|
||||
command_response = {"raw_response": http_response.text}
|
||||
# this string can include modifiers like UTF-8, which is why it's not using ==
|
||||
if 'application/json' in http_response.headers.get('Content-Type', ''):
|
||||
try:
|
||||
command_response = json.loads(http_response.text)
|
||||
except Exception as e:
|
||||
error = self._create_error_from_exception(exception=e, http_response=http_response)
|
||||
log("Did parse http_response")
|
||||
|
||||
if status >= 400 and error is None:
|
||||
error = self._create_error(error_name=f"HttpError{status}", http_response=http_response)
|
||||
|
||||
return_response: ConnectorProxyResponseDict = {
|
||||
"command_response": command_response,
|
||||
"spiff__logs": logs,
|
||||
"error": error,
|
||||
}
|
||||
result: CommandResultDictV2 = {
|
||||
"response": return_response,
|
||||
"status": status,
|
||||
"mimetype": mimetype,
|
||||
}
|
||||
|
||||
return result
|
|
@ -1,20 +1,20 @@
|
|||
from unittest.mock import patch
|
||||
from typing import Any
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from connector_http.commands.get_request_v2 import GetRequestV2
|
||||
|
||||
|
||||
class TestGetRequestV2:
|
||||
def test_html_from_url(self) -> None:
|
||||
get_requestor = GetRequestV2(url="http://example.com")
|
||||
request = GetRequestV2(url="http://example.com")
|
||||
result = None
|
||||
return_html = "<html>Hey</html>"
|
||||
with patch("requests.get") as mock_request:
|
||||
mock_request.return_value.status_code = 200
|
||||
mock_request.return_value.ok = True
|
||||
mock_request.return_value.text = return_html
|
||||
result = get_requestor.execute(None, {})
|
||||
result = request.execute(None, {})
|
||||
assert result is not None
|
||||
assert result["status"] == 200
|
||||
assert result["mimetype"] == "application/json"
|
||||
|
@ -27,7 +27,7 @@ class TestGetRequestV2:
|
|||
assert len(response["spiff__logs"]) > 0
|
||||
|
||||
def test_json_from_url(self) -> None:
|
||||
get_requestor = GetRequestV2(url="http://example.com")
|
||||
request = GetRequestV2(url="http://example.com")
|
||||
result = None
|
||||
return_json = {"hey": "we_return"}
|
||||
with patch("requests.get") as mock_request:
|
||||
|
@ -35,7 +35,7 @@ class TestGetRequestV2:
|
|||
mock_request.return_value.ok = True
|
||||
mock_request.return_value.headers = {"Content-Type": "application/json"}
|
||||
mock_request.return_value.text = json.dumps(return_json)
|
||||
result = get_requestor.execute(None, {})
|
||||
result = request.execute(None, {})
|
||||
assert result is not None
|
||||
assert result["status"] == 200
|
||||
assert result["mimetype"] == "application/json"
|
||||
|
@ -48,14 +48,14 @@ class TestGetRequestV2:
|
|||
assert len(response["spiff__logs"]) > 0
|
||||
|
||||
def test_can_handle_500(self, sleepless: Any) -> None:
|
||||
get_requestor = GetRequestV2(url="http://example.com", attempts=3)
|
||||
request = GetRequestV2(url="http://example.com", attempts=3)
|
||||
result = None
|
||||
return_json = {"error": "we_did_error"}
|
||||
with patch("requests.get") as mock_request:
|
||||
mock_request.return_value.status_code = 500
|
||||
mock_request.return_value.headers = {"Content-Type": "application/json"}
|
||||
mock_request.return_value.text = json.dumps(return_json)
|
||||
result = get_requestor.execute(None, {})
|
||||
result = request.execute(None, {})
|
||||
assert mock_request.call_count == 3
|
||||
assert result is not None
|
||||
assert result["status"] == 500
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from connector_http.commands.post_request_v2 import PostRequestV2
|
||||
|
||||
|
||||
class TestPostRequestV2:
|
||||
def test_html_from_url(self) -> None:
|
||||
request = PostRequestV2(url="http://example.com")
|
||||
result = None
|
||||
return_html = "<html>Hey</html>"
|
||||
with patch("requests.post") as mock_request:
|
||||
mock_request.return_value.status_code = 200
|
||||
mock_request.return_value.ok = True
|
||||
mock_request.return_value.text = return_html
|
||||
result = request.execute(None, {})
|
||||
assert mock_request.call_count == 1
|
||||
assert result is not None
|
||||
assert result["status"] == 200
|
||||
assert result["mimetype"] == "application/json"
|
||||
|
||||
response = result["response"]
|
||||
assert response is not None
|
||||
assert response["command_response"] == {"raw_response": return_html}
|
||||
assert response["error"] is None
|
||||
assert response["spiff__logs"] is not None
|
||||
assert len(response["spiff__logs"]) > 0
|
||||
|
||||
def test_json_from_url(self) -> None:
|
||||
request = PostRequestV2(url="http://example.com")
|
||||
result = None
|
||||
return_json = {"hey": "we_return"}
|
||||
with patch("requests.post") as mock_request:
|
||||
mock_request.return_value.status_code = 200
|
||||
mock_request.return_value.ok = True
|
||||
mock_request.return_value.headers = {"Content-Type": "application/json"}
|
||||
mock_request.return_value.text = json.dumps(return_json)
|
||||
result = request.execute(None, {})
|
||||
assert mock_request.call_count == 1
|
||||
assert result is not None
|
||||
assert result["status"] == 200
|
||||
assert result["mimetype"] == "application/json"
|
||||
|
||||
response = result["response"]
|
||||
assert response is not None
|
||||
assert response["command_response"] == return_json
|
||||
assert response["error"] is None
|
||||
assert response["spiff__logs"] is not None
|
||||
assert len(response["spiff__logs"]) > 0
|
||||
|
||||
def test_can_handle_500(self, sleepless: Any) -> None:
|
||||
request = PostRequestV2(url="http://example.com")
|
||||
result = None
|
||||
return_json = {"error": "we_did_error"}
|
||||
with patch("requests.post") as mock_request:
|
||||
mock_request.return_value.status_code = 500
|
||||
mock_request.return_value.headers = {"Content-Type": "application/json"}
|
||||
mock_request.return_value.text = json.dumps(return_json)
|
||||
result = request.execute(None, {})
|
||||
assert mock_request.call_count == 1
|
||||
assert result is not None
|
||||
assert result["status"] == 500
|
||||
assert result["mimetype"] == "application/json"
|
||||
|
||||
response = result["response"]
|
||||
assert response is not None
|
||||
assert response["command_response"] == return_json
|
||||
assert response["error"] is not None
|
||||
assert response["spiff__logs"] is not None
|
||||
assert len(response["spiff__logs"]) > 0
|
Loading…
Reference in New Issue