Skip to content

Commit

Permalink
Added client-level REST helpers. (#544)
Browse files Browse the repository at this point in the history
* Added client-level REST helpers.

Signed-off-by: dblock <[email protected]>

* Move functions into an .http namespace.

Signed-off-by: dblock <[email protected]>

* Poetry update in samples.

Signed-off-by: dblock <[email protected]>

* Fix: typo.

Signed-off-by: dblock <[email protected]>

* Clarified what to use in which older versions.

Signed-off-by: dblock <[email protected]>

---------

Signed-off-by: dblock <[email protected]>
  • Loading branch information
dblock authored Nov 13, 2023
1 parent da436cb commit e68b9e7
Show file tree
Hide file tree
Showing 16 changed files with 1,102 additions and 159 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Added `samples`, `benchmarks` and `docs` to `nox -rs format` ([#556](https://github.com/opensearch-project/opensearch-py/pull/556))
- Added guide on the document lifecycle API(s) ([#559](https://github.com/opensearch-project/opensearch-py/pull/559))
- Added Windows CI ([#569](https://github.com/opensearch-project/opensearch-py/pull/569))
- Added `client.http` JSON REST request API helpers ([#544](https://github.com/opensearch-project/opensearch-py/pull/544))
### Changed
- Generate `tasks` client from API specs ([#508](https://github.com/opensearch-project/opensearch-py/pull/508))
- Generate `ingest` client from API specs ([#513](https://github.com/opensearch-project/opensearch-py/pull/513))
Expand Down
1 change: 1 addition & 0 deletions docs/source/api-ref/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ titlesonly:
maxdepth: 1
---
clients/http_client
clients/cat_client
clients/cluster_client
clients/dangling_indices_client
Expand Down
5 changes: 5 additions & 0 deletions docs/source/api-ref/clients/http_client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Http Client

```{eval-rst}
.. autoclass:: opensearchpy.client.http.HttpClient
```
46 changes: 30 additions & 16 deletions guides/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,75 @@

# Making Raw JSON REST Requests

The OpenSearch client implements many high-level REST DSLs that invoke OpenSearch APIs. However you may find yourself in a situation that requires you to invoke an API that is not supported by the client. Use `client.transport.perform_request` to do so. See [samples/json](../samples/json) for a complete working sample.
The OpenSearch client implements many high-level REST DSLs that invoke OpenSearch APIs. However you may find yourself in a situation that requires you to invoke an API that is not supported by the client. Use `client.http.get`, `head` , `put`, `post`, and `delete` to do so. See [samples/json](../samples/json) for a complete working sample.

## GET

The following example returns the server version information via `GET /`.

```python
info = client.transport.perform_request('GET', '/')
print(f"Welcome to {info['version']['distribution']} {info['version']['number']}!")
info = client.get("/")
print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!")
```

Note that the client will parse the response as JSON when appropriate.

These methods are also available in the asynchronous client.

```python
info = await client.http.get("/")
print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!")
```

Use `perform_request` in older versions (<= 2.3.x), and `client.http.get` and others in newer ones.

```python
info = client.transport.perform_request("GET", "/")
print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!")
```

## PUT

The following example creates an index.

```python
index_body = {
'settings': {
'index': {
'number_of_shards': 4
"settings": {
"index": {
"number_of_shards": 4
}
}
}

client.transport.perform_request("PUT", "/movies", body=index_body)
client.http.put("/movies", body=index_body)
```

Note that the client will raise errors automatically. For example, if the index already exists, an `opensearchpy.exceptions.RequestError: RequestError(400, 'resource_already_exists_exception',` will be thrown.
Note that the client will raise errors automatically. For example, if the index already exists, an `opensearchpy.exceptions.RequestError: RequestError(400, "resource_already_exists_exception",` will be thrown.

## POST

The following example searches for a document.

```python
q = 'miller'
q = "miller"

query = {
'size': 5,
'query': {
'multi_match': {
'query': q,
'fields': ['title^2', 'director']
"size": 5,
"query": {
"multi_match": {
"query": q,
"fields": ["title^2", "director"]
}
}
}

client.transport.perform_request("POST", "/movies/_search", body = query)
client.http.post("/movies/_search", body = query)
```

## DELETE

The following example deletes an index.

```python
client.transport.perform_request("DELETE", "/movies")
client.http.delete("/movies")
```
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test(session: Any) -> None:
session.run("python", "setup.py", "test")


@nox.session() # type: ignore
@nox.session(python=["3.7"]) # type: ignore
def format(session: Any) -> None:
session.install("black", "isort")

Expand Down
3 changes: 2 additions & 1 deletion opensearchpy/_async/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from .cluster import ClusterClient
from .dangling_indices import DanglingIndicesClient
from .features import FeaturesClient
from .http import HttpClient
from .indices import IndicesClient
from .ingest import IngestClient
from .nodes import NodesClient
Expand Down Expand Up @@ -229,8 +230,8 @@ class as kwargs, or a string in the format of ``host[:port]`` which will be
self.remote_store = RemoteStoreClient(self)

self.features = FeaturesClient(self)

self.plugins = PluginsClient(self)
self.http = HttpClient(self)

def __repr__(self) -> Any:
try:
Expand Down
129 changes: 129 additions & 0 deletions opensearchpy/_async/client/http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: Apache-2.0
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
#
# Modifications Copyright OpenSearch Contributors. See
# GitHub history for details.

from typing import Any, Mapping, Optional

from .client import Client
from .utils import NamespacedClient


class HttpClient(NamespacedClient):
def __init__(self, client: Client) -> None:
super(HttpClient, self).__init__(client)

async def get(
self,
url: str,
headers: Optional[Mapping[str, Any]] = None,
params: Optional[Mapping[str, Any]] = None,
body: Any = None,
) -> Any:
"""
Perform a GET request and return the data.
:arg url: absolute url (without host) to target
:arg headers: dictionary of headers, will be handed over to the
underlying :class:`~opensearchpy.Connection` class
:arg params: dictionary of query parameters, will be handed over to the
underlying :class:`~opensearchpy.Connection` class for serialization
:arg body: body of the request, will be serialized using serializer and
passed to the connection
"""
return await self.transport.perform_request(
"GET", url=url, headers=headers, params=params, body=body
)

async def head(
self,
url: str,
headers: Optional[Mapping[str, Any]] = None,
params: Optional[Mapping[str, Any]] = None,
body: Any = None,
) -> Any:
"""
Perform a HEAD request and return the data.
:arg url: absolute url (without host) to target
:arg headers: dictionary of headers, will be handed over to the
underlying :class:`~opensearchpy.Connection` class
:arg params: dictionary of query parameters, will be handed over to the
underlying :class:`~opensearchpy.Connection` class for serialization
:arg body: body of the request, will be serialized using serializer and
passed to the connection
"""
return await self.transport.perform_request(
"HEAD", url=url, headers=headers, params=params, body=body
)

async def post(
self,
url: str,
headers: Optional[Mapping[str, Any]] = None,
params: Optional[Mapping[str, Any]] = None,
body: Any = None,
) -> Any:
"""
Perform a POST request and return the data.
:arg url: absolute url (without host) to target
:arg headers: dictionary of headers, will be handed over to the
underlying :class:`~opensearchpy.Connection` class
:arg params: dictionary of query parameters, will be handed over to the
underlying :class:`~opensearchpy.Connection` class for serialization
:arg body: body of the request, will be serialized using serializer and
passed to the connection
"""
return await self.transport.perform_request(
"POST", url=url, headers=headers, params=params, body=body
)

async def delete(
self,
url: str,
headers: Optional[Mapping[str, Any]] = None,
params: Optional[Mapping[str, Any]] = None,
body: Any = None,
) -> Any:
"""
Perform a DELETE request and return the data.
:arg url: absolute url (without host) to target
:arg headers: dictionary of headers, will be handed over to the
underlying :class:`~opensearchpy.Connection` class
:arg params: dictionary of query parameters, will be handed over to the
underlying :class:`~opensearchpy.Connection` class for serialization
:arg body: body of the request, will be serialized using serializer and
passed to the connection
"""
return await self.transport.perform_request(
"DELETE", url=url, headers=headers, params=params, body=body
)

async def put(
self,
url: str,
headers: Optional[Mapping[str, Any]] = None,
params: Optional[Mapping[str, Any]] = None,
body: Any = None,
) -> Any:
"""
Perform a PUT request and return the data.
:arg url: absolute url (without host) to target
:arg headers: dictionary of headers, will be handed over to the
underlying :class:`~opensearchpy.Connection` class
:arg params: dictionary of query parameters, will be handed over to the
underlying :class:`~opensearchpy.Connection` class for serialization
:arg body: body of the request, will be serialized using serializer and
passed to the connection
"""
return await self.transport.perform_request(
"PUT", url=url, headers=headers, params=params, body=body
)
3 changes: 2 additions & 1 deletion opensearchpy/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from .cluster import ClusterClient
from .dangling_indices import DanglingIndicesClient
from .features import FeaturesClient
from .http import HttpClient
from .indices import IndicesClient
from .ingest import IngestClient
from .nodes import NodesClient
Expand Down Expand Up @@ -229,8 +230,8 @@ class as kwargs, or a string in the format of ``host[:port]`` which will be
self.remote_store = RemoteStoreClient(self)

self.features = FeaturesClient(self)

self.plugins = PluginsClient(self)
self.http = HttpClient(self)

def __repr__(self) -> Any:
try:
Expand Down
Loading

0 comments on commit e68b9e7

Please sign in to comment.