Skip to content

Commit

Permalink
Merge branch 'mainline' into honglich/win_backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Honglichenn authored Nov 27, 2023
2 parents 150270a + 7177744 commit ce19774
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ __pycache__/
_version.py
.vscode
.coverage
.idea/
.idea/
120 changes: 120 additions & 0 deletions src/openjd/adaptor_runtime/application_ipc/_adaptor_server_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

from __future__ import annotations

import json
import sys
from http import HTTPStatus
from time import sleep
from typing import TYPE_CHECKING, Callable, Dict, Any, Optional
from .._http import HTTPResponse

if TYPE_CHECKING: # pragma: no cover because pytest will think we should test for this.
from openjd.adaptor_runtime_client import Action
from ._adaptor_server import AdaptorServer


class AdaptorServerResponseGenerator:
"""
This class is used for generating responses for all requests to the Adaptor server.
Response methods follow format: `generate_{request_path}_{method}_response`
"""

def __init__(
self,
server: AdaptorServer,
response_fn: Callable,
query_string_params: Dict[str, Any],
) -> None:
"""
Response generator
Args:
server: The server used for communication. For Linux, this will
be a AdaptorServer instance.
response_fn: The function used to return the result to the client.
For Linux, this will be an HTTPResponse instance.
query_string_params: The request parameters sent by the client.
For Linux, these will be extracted from the URL.
"""
self.server = server
self.response_method = response_fn
self.query_string_params = query_string_params

def generate_path_mapping_get_response(self) -> HTTPResponse:
"""
Handle GET request to /path_mapping path.
Returns:
HTTPResponse: A body and response code to send to the DCC Client
"""

if "path" in self.query_string_params:
return self.response_method(
HTTPStatus.OK,
json.dumps(
{"path": self.server.adaptor.map_path(self.query_string_params["path"][0])}
),
)
else:
return self.response_method(HTTPStatus.BAD_REQUEST, "Missing path in query string.")

def generate_path_mapping_rules_get_response(self) -> HTTPResponse:
"""
Handle GET request to /path_mapping_rules path.
Returns:
HTTPResponse: A body and response code to send to the DCC Client
"""
return self.response_method(
HTTPStatus.OK,
json.dumps(
{
"path_mapping_rules": [
rule.to_dict() for rule in self.server.adaptor.path_mapping_rules
]
}
),
)

def generate_action_get_response(self) -> HTTPResponse:
"""
Handle GET request to /action path.
Returns:
HTTPResponse: A body and response code to send to the DCC Client
"""
action = self._dequeue_action()

# We are going to wait until we have an action in the queue. This
# could happen between tasks.
while action is None:
sleep(0.01)
action = self._dequeue_action()

return self.response_method(HTTPStatus.OK, str(action))

def _dequeue_action(self) -> Optional[Action]:
"""This function will dequeue the first action in the queue.
Returns:
Action: A tuple containing the next action structured:
("action_name", { "args1": "val1", "args2": "val2" })
None: If the Actions Queue is empty.
Raises:
TypeError: If the server isn't an AdaptorServer.
"""
# This condition shouldn't matter, because we have typehinted the server above.
# This is only here for type hinting (as is the return None below).
if hasattr(self, "server") and hasattr(self.server, "actions_queue"):
return self.server.actions_queue.dequeue_action()

print(
"ERROR: Could not retrieve the next action because the server or actions queue "
"wasn't set.",
file=sys.stderr,
flush=True,
)
return None
48 changes: 18 additions & 30 deletions src/openjd/adaptor_runtime/application_ipc/_http_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

from __future__ import annotations

import json
import sys
from http import HTTPStatus
from time import sleep
from typing import TYPE_CHECKING
from typing import Optional

from ._adaptor_server_response import AdaptorServerResponseGenerator
from .._http import HTTPResponse, RequestHandler, ResourceRequestHandler

if TYPE_CHECKING: # pragma: no cover because pytest will think we should test for this.
Expand Down Expand Up @@ -42,6 +41,20 @@ class AdaptorResourceRequestHandler(ResourceRequestHandler):

server: AdaptorServer # This is just for type hinting

@property
def server_response(self):
"""
This is required because the socketserver.BaseRequestHandler.__init__ method actually
handles the request. This means the and self.query_string_params variable
are not set until that init method is called, so we need to do this type check outside
the init chain.
"""
if not hasattr(self, "_server_response"):
self._server_response = AdaptorServerResponseGenerator(
self.server, HTTPResponse, self.query_string_params
)
return self._server_response


class PathMappingEndpoint(AdaptorResourceRequestHandler):
path = "/path_mapping"
Expand All @@ -54,15 +67,7 @@ def get(self) -> HTTPResponse:
HTTPResponse: A body and response code to send to the DCC Client
"""
try:
if "path" in self.query_string_params:
return HTTPResponse(
HTTPStatus.OK,
json.dumps(
{"path": self.server.adaptor.map_path(self.query_string_params["path"][0])}
),
)
else:
return HTTPResponse(HTTPStatus.BAD_REQUEST, "Missing path in query string.")
return self.server_response.generate_path_mapping_get_response()
except Exception as e:
return HTTPResponse(HTTPStatus.INTERNAL_SERVER_ERROR, body=str(e))

Expand All @@ -77,16 +82,7 @@ def get(self) -> HTTPResponse:
Returns:
HTTPResponse: A body and response code to send to the DCC Client
"""
return HTTPResponse(
HTTPStatus.OK,
json.dumps(
{
"path_mapping_rules": [
rule.to_dict() for rule in self.server.adaptor.path_mapping_rules
]
}
),
)
return self.server_response.generate_path_mapping_rules_get_response()


class ActionEndpoint(AdaptorResourceRequestHandler):
Expand All @@ -100,15 +96,7 @@ def get(self) -> HTTPResponse:
Returns:
HTTPResponse: A body and response code to send to the DCC Client
"""
action = self._dequeue_action()

# We are going to wait until we have an action in the queue. This
# could happen between tasks.
while action is None:
sleep(0.01)
action = self._dequeue_action()

return HTTPResponse(HTTPStatus.OK, str(action))
return self.server_response.generate_action_get_response()

def _dequeue_action(self) -> Optional[Action]:
"""This function will dequeue the first action in the queue.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ def test_get_returns_rules(self):


class TestActionEndpoint:
def test_get_returns_action(self):
@patch.object(
ActionEndpoint, "query_string_params", new_callable=PropertyMock, return_value="{}"
)
def test_get_returns_action(self, mock_qsp):
# GIVEN
mock_request_handler = MagicMock()
mock_server = MagicMock(spec=_AdaptorServer)
Expand Down

0 comments on commit ce19774

Please sign in to comment.