From 8c39f3fe5fbc7cda446ba831514459700acfe2ed Mon Sep 17 00:00:00 2001 From: brandon Date: Wed, 12 Mar 2025 17:39:39 -0700 Subject: [PATCH 1/5] Two ways to make generic requests using the api client --- src/groundlight/experimental_api.py | 66 ++++++++++++++++++++++++++ test/unit/test_customizable_request.py | 10 ++++ 2 files changed, 76 insertions(+) create mode 100644 test/unit/test_customizable_request.py diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index c464e299..b0053c30 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -11,8 +11,10 @@ from io import BufferedReader, BytesIO from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union +from urllib3.response import HTTPResponse import requests +from groundlight.internalapi import _generate_request_id from groundlight_openapi_client.api.actions_api import ActionsApi from groundlight_openapi_client.api.detector_groups_api import DetectorGroupsApi from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi @@ -1046,3 +1048,67 @@ def get_detector_metrics(self, detector: Union[str, Detector]) -> dict: detector = detector.id obj = self.detectors_api.get_detector_metrics(detector) return obj.to_dict() + + def get_raw_headers(self) -> dict: + """ + Get the raw headers for the current API client + + :return: a dictionary containing the raw headers + """ + headers = {} + # see generated/groundlight_openapi_client/api_client.py update_params_for_auth + headers["x-api-token"] = self.api_client.configuration.api_key["ApiToken"] + # We generate a unique request ID client-side for each request + headers["X-Request-Id"] = _generate_request_id() + headers["User-Agent"] = self.api_client.default_headers["User-Agent"] + headers['Accept'] = 'application/json' + return headers + + def make_raw_rest_request(self, method: str, endpoint: str, body: Union[dict, None] = None) -> dict: + """ + Make a raw REST request to the specified URL + + :param method: the HTTP method to use + :param endpoint: the endpoint to send the request to - the url path appended after the + endpoint including a / at the beginging + :param body: the request body + + :return: a dictionary containing the raw response + """ + headers = self.get_raw_headers() + url = f"{self.api_client.configuration.host}{endpoint}" + response = requests.request(method, url, headers=headers, json=body) + return response.json() + + def make_generic_api_request( + self, + endpoint: str, + method: str, + headers: dict = None, + body: Union[dict, None] = None, + files = None, + ) -> HTTPResponse: + """ + Make a generic API request to the specified endpoint, utilizing many of the provided tools from the generated api client + + :param endpoint: the endpoint to send the request to - the url path appended after the + endpoint including a / at the beginging + :param method: the HTTP method to use + :param body: the request body + + :return: a dictionary containing the response + """ + # HEADERS MUST BE THE 4TH ARGUMENT, 0 INDEXED + if not headers: + headers = self.get_raw_headers() + return self.api_client.call_api( + endpoint, + method, + None, # Path Params + None, # Query params + headers, # header params + body = body, # body + files = files, # files + auth_settings = ['ApiToken'], + _preload_content = False, # This returns the urllib3 response rather than trying any type of processing + ) diff --git a/test/unit/test_customizable_request.py b/test/unit/test_customizable_request.py new file mode 100644 index 00000000..2088a32e --- /dev/null +++ b/test/unit/test_customizable_request.py @@ -0,0 +1,10 @@ +from groundlight import ExperimentalApi +from datetime import datetime + +gl = ExperimentalApi(endpoint="https://api.groundlight.ai/") + +def test_invalid_endpoint_config(): + print(gl.make_generic_api_request("/v1/me", "GET")) + print(gl.make_generic_api_request("/v1/detectors", "GET")) + name = f"Test {datetime.utcnow()}" + print(gl.make_generic_api_request("/v1/detector-groups", "POST", body={"name": name})) \ No newline at end of file From cea8dc3e52f917f05dc195a2f0a82e2ba7c16ad8 Mon Sep 17 00:00:00 2001 From: Auto-format Bot Date: Thu, 13 Mar 2025 00:41:51 +0000 Subject: [PATCH 2/5] Automatically reformatting code --- src/groundlight/experimental_api.py | 34 +++++++++++++------------- test/unit/test_customizable_request.py | 6 +++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index b0053c30..3fc93214 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -11,10 +11,8 @@ from io import BufferedReader, BytesIO from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union -from urllib3.response import HTTPResponse import requests -from groundlight.internalapi import _generate_request_id from groundlight_openapi_client.api.actions_api import ActionsApi from groundlight_openapi_client.api.detector_groups_api import DetectorGroupsApi from groundlight_openapi_client.api.detector_reset_api import DetectorResetApi @@ -49,8 +47,10 @@ Rule, WebhookAction, ) +from urllib3.response import HTTPResponse from groundlight.images import parse_supported_image_types +from groundlight.internalapi import _generate_request_id from groundlight.optional_imports import Image, np from .client import DEFAULT_REQUEST_TIMEOUT, Groundlight, GroundlightClientError, logger @@ -1061,7 +1061,7 @@ def get_raw_headers(self) -> dict: # We generate a unique request ID client-side for each request headers["X-Request-Id"] = _generate_request_id() headers["User-Agent"] = self.api_client.default_headers["User-Agent"] - headers['Accept'] = 'application/json' + headers["Accept"] = "application/json" return headers def make_raw_rest_request(self, method: str, endpoint: str, body: Union[dict, None] = None) -> dict: @@ -1081,13 +1081,13 @@ def make_raw_rest_request(self, method: str, endpoint: str, body: Union[dict, No return response.json() def make_generic_api_request( - self, - endpoint: str, - method: str, - headers: dict = None, - body: Union[dict, None] = None, - files = None, - ) -> HTTPResponse: + self, + endpoint: str, + method: str, + headers: dict = None, + body: Union[dict, None] = None, + files=None, + ) -> HTTPResponse: """ Make a generic API request to the specified endpoint, utilizing many of the provided tools from the generated api client @@ -1104,11 +1104,11 @@ def make_generic_api_request( return self.api_client.call_api( endpoint, method, - None, # Path Params - None, # Query params - headers, # header params - body = body, # body - files = files, # files - auth_settings = ['ApiToken'], - _preload_content = False, # This returns the urllib3 response rather than trying any type of processing + None, # Path Params + None, # Query params + headers, # header params + body=body, # body + files=files, # files + auth_settings=["ApiToken"], + _preload_content=False, # This returns the urllib3 response rather than trying any type of processing ) diff --git a/test/unit/test_customizable_request.py b/test/unit/test_customizable_request.py index 2088a32e..5dcc5059 100644 --- a/test/unit/test_customizable_request.py +++ b/test/unit/test_customizable_request.py @@ -1,10 +1,12 @@ -from groundlight import ExperimentalApi from datetime import datetime +from groundlight import ExperimentalApi + gl = ExperimentalApi(endpoint="https://api.groundlight.ai/") + def test_invalid_endpoint_config(): print(gl.make_generic_api_request("/v1/me", "GET")) print(gl.make_generic_api_request("/v1/detectors", "GET")) name = f"Test {datetime.utcnow()}" - print(gl.make_generic_api_request("/v1/detector-groups", "POST", body={"name": name})) \ No newline at end of file + print(gl.make_generic_api_request("/v1/detector-groups", "POST", body={"name": name})) From bc538462f3b0d7f52b93b56fa4915c2ec11e3c0f Mon Sep 17 00:00:00 2001 From: brandon Date: Wed, 12 Mar 2025 17:44:44 -0700 Subject: [PATCH 3/5] point to the right test server --- test/unit/test_customizable_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/test_customizable_request.py b/test/unit/test_customizable_request.py index 2088a32e..55dde0b2 100644 --- a/test/unit/test_customizable_request.py +++ b/test/unit/test_customizable_request.py @@ -1,7 +1,7 @@ from groundlight import ExperimentalApi from datetime import datetime -gl = ExperimentalApi(endpoint="https://api.groundlight.ai/") +gl = ExperimentalApi() def test_invalid_endpoint_config(): print(gl.make_generic_api_request("/v1/me", "GET")) From 4a2e0fbc43bb23a1d6c61e51dc506ee571d65dce Mon Sep 17 00:00:00 2001 From: brandon Date: Wed, 12 Mar 2025 17:48:33 -0700 Subject: [PATCH 4/5] linting --- src/groundlight/experimental_api.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 3fc93214..397ac301 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -1080,16 +1080,17 @@ def make_raw_rest_request(self, method: str, endpoint: str, body: Union[dict, No response = requests.request(method, url, headers=headers, json=body) return response.json() - def make_generic_api_request( + def make_generic_api_request( # noqa: PLR0913 # pylint: disable=too-many-arguments self, endpoint: str, method: str, - headers: dict = None, + headers: Union[dict, None] = None, body: Union[dict, None] = None, files=None, ) -> HTTPResponse: """ - Make a generic API request to the specified endpoint, utilizing many of the provided tools from the generated api client + Make a generic API request to the specified endpoint, utilizing many of the provided tools + from the generated api client :param endpoint: the endpoint to send the request to - the url path appended after the endpoint including a / at the beginging From 0296c2b40346b6291462368bc16c31582e264135 Mon Sep 17 00:00:00 2001 From: brandon Date: Thu, 13 Mar 2025 14:08:19 -0700 Subject: [PATCH 5/5] good suggestions --- src/groundlight/experimental_api.py | 17 +---------------- test/unit/test_customizable_request.py | 6 +++--- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 397ac301..73511dcc 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -1064,24 +1064,9 @@ def get_raw_headers(self) -> dict: headers["Accept"] = "application/json" return headers - def make_raw_rest_request(self, method: str, endpoint: str, body: Union[dict, None] = None) -> dict: - """ - Make a raw REST request to the specified URL - - :param method: the HTTP method to use - :param endpoint: the endpoint to send the request to - the url path appended after the - endpoint including a / at the beginging - :param body: the request body - - :return: a dictionary containing the raw response - """ - headers = self.get_raw_headers() - url = f"{self.api_client.configuration.host}{endpoint}" - response = requests.request(method, url, headers=headers, json=body) - return response.json() - def make_generic_api_request( # noqa: PLR0913 # pylint: disable=too-many-arguments self, + *, endpoint: str, method: str, headers: Union[dict, None] = None, diff --git a/test/unit/test_customizable_request.py b/test/unit/test_customizable_request.py index f7ac420d..9f62d73c 100644 --- a/test/unit/test_customizable_request.py +++ b/test/unit/test_customizable_request.py @@ -6,7 +6,7 @@ def test_invalid_endpoint_config(): - print(gl.make_generic_api_request("/v1/me", "GET")) - print(gl.make_generic_api_request("/v1/detectors", "GET")) + print(gl.make_generic_api_request(endpoint="/v1/me", method="GET")) + print(gl.make_generic_api_request(endpoint="/v1/detectors", method="GET")) name = f"Test {datetime.utcnow()}" - print(gl.make_generic_api_request("/v1/detector-groups", "POST", body={"name": name})) + print(gl.make_generic_api_request(endpoint="/v1/detector-groups", method="POST", body={"name": name}))