-
Notifications
You must be signed in to change notification settings - Fork 9
Added Abstract Endpoint #8
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
Changes from 3 commits
d13d52e
f9b5080
bcac434
ab1f369
b1a5808
8ac4189
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| """Tests for the endpoint module.""" | ||
|
|
||
| import pytest | ||
| from requests import Session | ||
| from requests.models import Response as RequestResponse | ||
|
|
||
| from tfe.endpoint import Endpoint | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_session(mocker): | ||
| """Create a mock session for testing.""" | ||
| return mocker.Mock(spec=Session) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def endpoint(mock_session): | ||
| """Create an Endpoint instance for testing.""" | ||
| return Endpoint(client=mock_session) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_response(mocker): | ||
| """Create a mock HTTP response.""" | ||
| response = mocker.Mock(spec=RequestResponse) | ||
| response.status_code = 200 | ||
| response.json.return_value = {"data": "test"} | ||
| return response | ||
|
|
||
|
|
||
| class TestEndpoint: | ||
| """Test cases for the Endpoint class.""" | ||
|
|
||
| @pytest.mark.parametrize( | ||
| "method,expected_method,json_data,expected_call", | ||
| [ | ||
| ("GET", "get", None, ("get", "/test/path")), | ||
| ("POST", "post", {"key": "value"}, ("post", "/test/path")), | ||
| ("PUT", "put", {"key": "value"}, ("put", "/test/path")), | ||
| ("PATCH", "patch", {"key": "value"}, ("patch", "/test/path")), | ||
| ("DELETE", "delete", None, ("delete", "/test/path")), | ||
| ("get", "get", None, ("get", "/test/path")), # Test case insensitive | ||
| ( | ||
| "post", | ||
| "post", | ||
| {"data": "test"}, | ||
| ("post", "/test/path"), | ||
| ), # Test case insensitive | ||
| ], | ||
| ) | ||
| def test_make_request_all_methods( | ||
| self, endpoint, mock_response, method, expected_method, json_data, expected_call | ||
| ): | ||
| """Test _make_request with all supported HTTP methods.""" | ||
| # Setup the mock method to return our mock response | ||
| getattr(endpoint._http_client, expected_method).return_value = mock_response | ||
|
|
||
| # Make the request | ||
| if json_data: | ||
| response = endpoint._make_request(method, "/test/path", json=json_data) | ||
| # Verify the correct method was called with the correct arguments | ||
| getattr(endpoint._http_client, expected_method).assert_called_once_with( | ||
| "/test/path", json=json_data | ||
| ) | ||
| else: | ||
| response = endpoint._make_request(method, "/test/path") | ||
| # Verify the correct method was called with the correct arguments | ||
| getattr(endpoint._http_client, expected_method).assert_called_once_with( | ||
| "/test/path" | ||
| ) | ||
|
|
||
| # Verify the response is returned correctly | ||
| assert response == mock_response | ||
|
|
||
| def test_make_request_unsupported_method(self, endpoint): | ||
| """Test that unsupported HTTP methods raise ValueError.""" | ||
| with pytest.raises(ValueError, match="Unsupported HTTP method: INVALID"): | ||
| endpoint._make_request("INVALID", "/test/path") | ||
|
|
||
| def test_get_method(self, endpoint, mock_response): | ||
| """Test the _get convenience method.""" | ||
| endpoint._http_client.get.return_value = mock_response | ||
|
|
||
| response = endpoint._get("/test/path") | ||
|
|
||
| endpoint._http_client.get.assert_called_once_with("/test/path") | ||
| assert response == mock_response | ||
|
|
||
| def test_post_method(self, endpoint, mock_response): | ||
| """Test the _post convenience method.""" | ||
| endpoint._http_client.post.return_value = mock_response | ||
| test_data = {"key": "value"} | ||
|
|
||
| response = endpoint._post("/test/path", test_data) | ||
|
|
||
| endpoint._http_client.post.assert_called_once_with("/test/path", json=test_data) | ||
| assert response == mock_response | ||
|
|
||
| def test_put_method(self, endpoint, mock_response): | ||
| """Test the _put convenience method.""" | ||
| endpoint._http_client.put.return_value = mock_response | ||
| test_data = {"key": "value"} | ||
|
|
||
| response = endpoint._put("/test/path", test_data) | ||
|
|
||
| endpoint._http_client.put.assert_called_once_with("/test/path", json=test_data) | ||
| assert response == mock_response | ||
|
|
||
| def test_patch_method(self, endpoint, mock_response): | ||
| """Test the _patch convenience method.""" | ||
| endpoint._http_client.patch.return_value = mock_response | ||
| test_data = {"key": "value"} | ||
|
|
||
| response = endpoint._patch("/test/path", test_data) | ||
|
|
||
| endpoint._http_client.patch.assert_called_once_with( | ||
| "/test/path", json=test_data | ||
| ) | ||
| assert response == mock_response | ||
|
|
||
| def test_delete_method(self, endpoint, mock_response): | ||
| """Test the _delete convenience method.""" | ||
| endpoint._http_client.delete.return_value = mock_response | ||
|
|
||
| response = endpoint._delete("/test/path") | ||
|
|
||
| endpoint._http_client.delete.assert_called_once_with("/test/path") | ||
| assert response == mock_response | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +0,0 @@ | ||
| """ | ||
| Python client library for Terraform Enterprise/Cloud API. | ||
|
|
||
| This package provides a Python interface to the Terraform Enterprise | ||
| and Terraform Cloud APIs, allowing you to programmatically manage | ||
| workspaces, runs, state files, and other TFE/TFC resources. | ||
| """ | ||
|
|
||
| from tfe.client import Client, TFEClientError | ||
| from tfe.config import Config | ||
|
|
||
| __all__ = ["Client", "TFEClientError", "Config"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| """ | ||
| Base service class for Terraform Enterprise/Cloud API services. | ||
|
|
||
| This module provides an abstract base class that all TFE API services | ||
| should inherit from. It provides common functionality for HTTP requests | ||
| and defines the interface that all service implementations must follow. | ||
| """ | ||
|
|
||
| import logging | ||
| from abc import ABC, abstractmethod | ||
| from typing import Any, TypeVar | ||
|
|
||
| from requests import Session | ||
| from requests.models import Response as RequestResponse | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| T = TypeVar("T", bound="Response") | ||
|
|
||
|
|
||
| class Response(ABC): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we going to fully implement this? Currently there is no implementation or any sort of relation with endpoint class. |
||
| @classmethod | ||
| @abstractmethod | ||
| def from_http_response(cls: type[T], response: RequestResponse) -> T: | ||
| """Create an instance of the endpoint specific response class from an HTTP response.""" | ||
|
taru-garg-2000 marked this conversation as resolved.
Outdated
|
||
| pass | ||
|
|
||
|
|
||
| class Endpoint: | ||
| """Base class for all TFE API services.""" | ||
|
|
||
| def __init__(self, client: Session) -> None: | ||
| self._http_client = client | ||
|
|
||
| def _make_request( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why aren't we checking for response codes / raising any errors for those HTTP errors? |
||
| self, method: str, path: str, json: dict[str, Any] | None = None | ||
| ) -> RequestResponse: | ||
| """ | ||
| Make an HTTP request using the client's HTTP client. | ||
|
|
||
| Args: | ||
| method: HTTP method (GET, POST, PUT, PATCH, DELETE) | ||
| path: API path | ||
| json: JSON payload for POST, PUT, PATCH requests | ||
| Returns: | ||
| The HTTP response from requests library | ||
| """ | ||
| method = method.upper() | ||
|
|
||
| # Make the request | ||
| match method: | ||
| case "GET": | ||
| return self._http_client.get(path) | ||
| case "POST": | ||
| return self._http_client.post(path, json=json) | ||
| case "PUT": | ||
| return self._http_client.put(path, json=json) | ||
| case "PATCH": | ||
| return self._http_client.patch(path, json=json) | ||
| case "DELETE": | ||
| return self._http_client.delete(path) | ||
| case _: | ||
| raise ValueError(f"Unsupported HTTP method: {method}") | ||
|
|
||
| def _get(self, path: str) -> RequestResponse: | ||
| return self._make_request("GET", path) | ||
|
|
||
| def _post(self, path: str, data: dict) -> RequestResponse: | ||
| return self._make_request("POST", path, json=data) | ||
|
|
||
| def _put(self, path: str, data: dict) -> RequestResponse: | ||
| return self._make_request("PUT", path, json=data) | ||
|
|
||
| def _patch(self, path: str, data: dict) -> RequestResponse: | ||
| return self._make_request("PATCH", path, json=data) | ||
|
|
||
| def _delete(self, path: str) -> RequestResponse: | ||
| return self._make_request("DELETE", path) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again no test cases for error codes