Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use properties. #29

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion samples/hello/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
- [Hello World](#hello-world)
- [Start OpenSearch](#start-opensearch)
- [Start the Extension](#start-the-extension)
- [Register the Extension](#register-the-extension)
- [Call an Extension API](#call-an-extension-api)

# Hello World

This sample implements an extension that exposes a REST API.

### Start OpenSearch

Enable the `opensearch.experimental.feature.extensions.enabled` experimental feature in either way described in [the developer guide](https://github.com/opensearch-project/opensearch-sdk-java/blob/main/DEVELOPER_GUIDE.md#enable-the-extensions-feature-flag).
Expand Down Expand Up @@ -80,4 +88,18 @@ INFO:root:< prefix=b'ES', version=2.10.0.99, type=['request'], message=50 byte(s
INFO:root:> prefix=b'ES', version=2.10.0.99, type=['response'], message=179 byte(s), id=9, ctx=req={}, res={}, id=hello-world, version=3.0.0.99, name=hello-world, host=127.0.0.1, addr=127.0.0.1, attr={}, roles={('data', 'd', True), ('ingest', 'i', False), ('remote_cluster_client', 'r', False), ('cluster_manager', 'm', False)}, cluster name=, version=3.0.0.99, features=[], size=185 byte(s)
INFO:root:< prefix=b'ES', version=2.10.0.99, type=['request'], message=469 byte(s), id=10, ctx=req={'_system_index_access_allowed': 'false'}, res={}, None, features=[], action=internal:discovery/extensions
INFO:root:> prefix=b'ES', version=2.10.0.99, type=['response'], message=167 byte(s), id=6, ctx=req={'_system_index_access_allowed': 'false', 'extension_unique_id': 'hello-world'}, res={}, node=, id=None, features=[], action=internal:discovery/registerrestactions, size=173 byte(s)
```
```

### Call an Extension API

```bash
curl -XGET "localhost:9200/_extensions/_hello-world/hello"
Hello from Python! 👋
```

OpenSearch invokes an extension endpoint, and you should see some output there.

```
INFO:root:< prefix=b'ES', version=2.10.0.99, type=['request'], message=161 byte(s), id=15, ctx=req={'_system_index_access_allowed': 'false'}, res={}, None, features=[], action=internal:extensions/restexecuteonextensiontaction
INFO:root:> prefix=b'ES', version=2.10.0.99, type=['response'], message=108 byte(s), id=15, ctx=req={'_system_index_access_allowed': 'false'}, res={}, <opensearch_sdk_py.rest.rest_execute_on_extension_response.RestExecuteOnExtensionResponse object at 0x1048cb450>, features=[], size=114 byte(s)
```
4 changes: 2 additions & 2 deletions samples/hello/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ async def run_server() -> None:
# TODO Move this code to SDK "runner" class
# Map interface name to instance
interfaces = dict()
for interface in extension.get_implemented_interfaces():
for interface in extension.implemented_interfaces:
interfaces[interface[0]] = interface[1]
logging.info(f"Registering {interface[0]} to point to {interface[1]}")
# If it's an ActionExtension it has this extension point
# TODO This could perhaps be better with isinstance()
if "ActionExtension" in interfaces.keys():
for handler in getattr(interfaces["ActionExtension"], "get_extension_rest_handlers")():
for handler in getattr(interfaces["ActionExtension"], "extension_rest_handlers"):
ExtensionRestHandlers().register(handler)
logging.info(f"Registering {handler}")

Expand Down
6 changes: 4 additions & 2 deletions samples/hello/hello_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@


class HelloExtension(Extension, ActionExtension):
def get_implemented_interfaces(self) -> list[tuple]:
@property
def implemented_interfaces(self) -> list[tuple]:
# TODO: This is lazy and temporary.
# Really we should be using this class to call some SDK class run(),
# passing an instance of ourself to the SDK and letting it parse out
# the superclass names with class.__mro__ and calling the appropriate
# implemented functions from the interfaces.
return [("Extension", self), ("ActionExtension", self)]

def get_extension_rest_handlers(self) -> list[ExtensionRestHandler]:
@property
def extension_rest_handlers(self) -> list[ExtensionRestHandler]:
return [HelloRestHandler()]
1 change: 1 addition & 0 deletions samples/hello/hello_rest_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ def handle_request(self, rest_request: ExtensionRestRequest) -> ExtensionRestRes
response_bytes = bytes("Hello from Python!", "utf-8") + b"\x20\xf0\x9f\x91\x8b"
return ExtensionRestResponse(RestStatus.OK, response_bytes, ExtensionRestResponse.TEXT_CONTENT_TYPE)

@property
def routes(self) -> list[NamedRoute]:
return [NamedRoute(method=RestMethod.GET, path="/hello", unique_name="greeting")]
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def handle(self, request: OutboundMessageRequest, input: StreamInput) -> StreamO
OutboundMessageRequest(
thread_context=request.thread_context_struct,
features=request.features,
message=RegisterRestActionsRequest("hello-world", ExtensionRestHandlers().named_routes()),
message=RegisterRestActionsRequest("hello-world", ExtensionRestHandlers().named_routes),
version=request.version,
action="internal:discovery/registerrestactions",
is_handshake=False,
Expand Down
3 changes: 2 additions & 1 deletion src/opensearch_sdk_py/api/action_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@


class ActionExtension(ABC):
@property
@abstractmethod
def get_extension_rest_handlers(self) -> list[ExtensionRestHandler]:
def extension_rest_handlers(self) -> list[ExtensionRestHandler]:
"""
Implementer should return a list of classes implementing ExtensionRestHandler
"""
3 changes: 2 additions & 1 deletion src/opensearch_sdk_py/api/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@


class Extension(ABC):
@property
@abstractmethod
def get_implemented_interfaces(self) -> list[tuple]:
def implemented_interfaces(self) -> list[tuple]:
"""
Implementer should return a list of tuples containing the implemented interface (such as
Extension, ActionExtension, etc.) and the implementing class.
Expand Down
1 change: 1 addition & 0 deletions src/opensearch_sdk_py/rest/extension_rest_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
class ExtensionRestHandler(ABC):
@abstractmethod
def handle_request(rest_request: ExtensionRestRequest) -> ExtensionRestResponse:
pass

Check warning on line 22 in src/opensearch_sdk_py/rest/extension_rest_handler.py

View check run for this annotation

Codecov / codecov/patch

src/opensearch_sdk_py/rest/extension_rest_handler.py#L22

Added line #L22 was not covered by tests

@property
def routes(self) -> list[NamedRoute]:
return []

Check warning on line 26 in src/opensearch_sdk_py/rest/extension_rest_handler.py

View check run for this annotation

Codecov / codecov/patch

src/opensearch_sdk_py/rest/extension_rest_handler.py#L26

Added line #L26 was not covered by tests
3 changes: 2 additions & 1 deletion src/opensearch_sdk_py/rest/extension_rest_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ def __new__(cls): # type:ignore
return cls._singleton

def register(self, klass: ExtensionRestHandler) -> None:
for route in getattr(klass, "routes")():
for route in klass.routes:
# for matching the handler on the extension side only method and path matter
self[route.key] = klass
# but we have to send the full named route to OpenSearch
self._named_routes.append(str(route))

@property
def named_routes(self) -> list[str]:
return self._named_routes

Expand Down
3 changes: 2 additions & 1 deletion tests/rest/test_extension_rest_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_registers_handler(self) -> None:
self.assertEqual(len(ExtensionRestHandlers()), 2)
self.assertIsInstance(ExtensionRestHandlers()["GET /foo"], FakeRestHandler)
self.assertIsInstance(ExtensionRestHandlers()["GET /bar"], FakeRestHandler)
self.assertListEqual(ExtensionRestHandlers().named_routes(), ["GET /foo get_foo", "GET /bar get_bar"])
self.assertListEqual(ExtensionRestHandlers().named_routes, ["GET /foo get_foo", "GET /bar get_bar"])

response = handlers.handle("GET /foo", ExtensionRestRequest())
self.assertEqual(response.status, RestStatus.NOT_IMPLEMENTED)
Expand All @@ -38,5 +38,6 @@ def __init__(self) -> None:
def handle_request(self, rest_request: ExtensionRestRequest) -> ExtensionRestResponse:
return ExtensionRestResponse(status=RestStatus.NOT_IMPLEMENTED)

@property
def routes(self) -> list[NamedRoute]:
return [NamedRoute(RestMethod.GET, "/foo", "get_foo"), NamedRoute(RestMethod.GET, "/bar", "get_bar")]
Loading