Skip to content

Commit 40a2b52

Browse files
authored
refactor: pass URL params using params parameter instead of hand-rolling URLs (#37)
* refactor: pass URL params via params parameter instead of URL string * refactor: pass URL params via params parameter instead of URL string * refactor: update test to match new params approach * refactor: update test to match new params approach * refactor: pass URL params via params parameter instead of URL string * fix: tests
1 parent 92078b3 commit 40a2b52

9 files changed

Lines changed: 35 additions & 30 deletions

File tree

src/deepset_mcp/api/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ async def request(
5757
method: str = "GET",
5858
data: dict[str, Any] | None = None,
5959
headers: dict[str, str] | None = None,
60+
**kwargs: Any,
6061
) -> TransportResponse[T]: ...
6162

6263
@overload
@@ -68,6 +69,7 @@ async def request(
6869
method: str = "GET",
6970
data: dict[str, Any] | None = None,
7071
headers: dict[str, str] | None = None,
72+
**kwargs: Any,
7173
) -> TransportResponse[Any]: ...
7274

7375
async def request(
@@ -78,6 +80,7 @@ async def request(
7880
data: dict[str, Any] | None = None,
7981
headers: dict[str, str] | None = None,
8082
response_type: type[T] | None = None,
83+
**kwargs: Any,
8184
) -> TransportResponse[Any]:
8285
"""Make a request to the deepset API."""
8386
if not endpoint.startswith("/"):
@@ -102,6 +105,7 @@ async def request(
102105
json=data,
103106
headers=request_headers,
104107
response_type=response_type,
108+
**kwargs,
105109
)
106110

107111
async def close(self) -> None:

src/deepset_mcp/api/haystack_service/resource.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ async def get_component_input_output(self, component_name: str) -> dict[str, Any
3939
The component input/output schema as a dictionary
4040
"""
4141
resp = await self._client.request(
42-
endpoint=f"v1/haystack/components/input-output?domain=deepset-cloud&names={component_name}",
42+
endpoint="v1/haystack/components/input-output",
4343
method="GET",
4444
headers={"accept": "application/json"},
45+
params={"domain": "deepset-cloud", "names": component_name},
4546
response_type=list[dict[str, Any]],
4647
)
4748

src/deepset_mcp/api/pipeline/resource.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ async def list(
7777
"limit": limit,
7878
}
7979

80-
query = "?" + "&".join(f"{k}={v}" for k, v in params.items()) if params else ""
8180
resp = await self._client.request(
82-
endpoint=f"v1/workspaces/{self._workspace}/pipelines{query}",
81+
endpoint=f"v1/workspaces/{self._workspace}/pipelines",
8382
method="GET",
83+
params=params,
8484
)
8585

8686
raise_for_status(resp)

src/deepset_mcp/api/pipeline_template/resource.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ async def list_templates(self, limit: int = 100) -> list[PipelineTemplate]:
5555
List of pipeline templates
5656
"""
5757
response = await self._client.request(
58-
f"/v1/workspaces/{self._workspace}/pipeline_templates?limit={limit}&page_number=1&field=created_at&order=DESC",
58+
f"/v1/workspaces/{self._workspace}/pipeline_templates",
5959
method="GET",
60+
params={"limit": limit, "page_number": 1, "field": "created_at", "order": "DESC"},
6061
)
6162

6263
raise_for_status(response)

src/deepset_mcp/api/protocols.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ async def request(
3333
method: str = "GET",
3434
data: dict[str, Any] | None = None,
3535
headers: dict[str, str] | None = None,
36+
**kwargs: Any,
3637
) -> TransportResponse[T]: ...
3738

3839
@overload
@@ -44,6 +45,7 @@ async def request(
4445
method: str = "GET",
4546
data: dict[str, Any] | None = None,
4647
headers: dict[str, str] | None = None,
48+
**kwargs: Any,
4749
) -> TransportResponse[Any]: ...
4850

4951
async def request(
@@ -54,6 +56,7 @@ async def request(
5456
method: str = "GET",
5557
data: dict[str, Any] | None = None,
5658
headers: dict[str, str] | None = None,
59+
**kwargs: Any,
5760
) -> TransportResponse[Any]:
5861
"""Make a request to the API."""
5962
...

test/unit/api/haystack_service/test_haystack_service_resource.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def mock_schema_error_response(mock_client: BaseFakeClient) -> None:
4040

4141
@pytest.fixture
4242
def mock_successful_io_response(mock_client: BaseFakeClient) -> None:
43-
mock_client.responses["v1/haystack/components/input-output?domain=deepset-cloud&names=Agent"] = TransportResponse(
43+
mock_client.responses["v1/haystack/components/input-output"] = TransportResponse(
4444
status_code=200,
4545
json=[{"name": "Agent", "input": "Mock input", "output": "Mock output"}],
4646
text=json.dumps([{"name": "Agent", "input": "Mock input", "output": "Mock output"}]),
@@ -49,7 +49,7 @@ def mock_successful_io_response(mock_client: BaseFakeClient) -> None:
4949

5050
@pytest.fixture
5151
def mock_io_error_response(mock_client: BaseFakeClient) -> None:
52-
mock_client.responses["v1/haystack/components/input-output?domain=deepset-cloud&names=Agent"] = TransportResponse(
52+
mock_client.responses["v1/haystack/components/input-output"] = TransportResponse(
5353
status_code=500,
5454
json={"message": "Internal server error"},
5555
text="Internal server error",

test/unit/api/pipeline/test_pipeline_resource.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async def test_list_pipelines_default_params(self) -> None:
6868
# Create client with predefined response
6969
client = DummyClient(
7070
responses={
71-
"test-workspace/pipelines?page_number=1&limit=10": {
71+
"test-workspace/pipelines": {
7272
"data": sample_pipelines,
7373
"has_more": False,
7474
"total": 2,
@@ -88,8 +88,9 @@ async def test_list_pipelines_default_params(self) -> None:
8888

8989
# Verify request
9090
assert len(client.requests) == 1
91-
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines?page_number=1&limit=10"
91+
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines"
9292
assert client.requests[0]["method"] == "GET"
93+
assert client.requests[0]["params"] == {"page_number": 1, "limit": 10}
9394

9495
@pytest.mark.asyncio
9596
async def test_list_pipelines_with_pagination(self) -> None:
@@ -103,7 +104,7 @@ async def test_list_pipelines_with_pagination(self) -> None:
103104
# Create client with predefined response
104105
client = DummyClient(
105106
responses={
106-
"test-workspace/pipelines?page_number=2&limit=5": {
107+
"test-workspace/pipelines": {
107108
"data": sample_pipelines,
108109
"has_more": False,
109110
"total": 10,
@@ -121,15 +122,14 @@ async def test_list_pipelines_with_pagination(self) -> None:
121122
assert result[1].id == "4"
122123

123124
# Verify request
124-
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines?page_number=2&limit=5"
125+
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines"
126+
assert client.requests[0]["params"] == {"page_number": 2, "limit": 5}
125127

126128
@pytest.mark.asyncio
127129
async def test_list_pipelines_empty_result(self) -> None:
128130
"""Test listing pipelines when there are no pipelines."""
129131
# Create client with empty response
130-
client = DummyClient(
131-
responses={"test-workspace/pipelines?page_number=1&limit=10": {"data": [], "has_more": False, "total": 0}}
132-
)
132+
client = DummyClient(responses={"test-workspace/pipelines": {"data": [], "has_more": False, "total": 0}})
133133

134134
# Create resource and call list method
135135
resource = PipelineResource(client=client, workspace="test-workspace")
@@ -142,7 +142,7 @@ async def test_list_pipelines_empty_result(self) -> None:
142142
async def test_list_pipelines_error(self) -> None:
143143
"""Test handling of errors when listing pipelines."""
144144
# Create client that raises an exception
145-
client = DummyClient(responses={"test-workspace/pipelines?page_number=1&limit=10": ValueError("API Error")})
145+
client = DummyClient(responses={"test-workspace/pipelines": ValueError("API Error")})
146146

147147
# Create resource
148148
resource = PipelineResource(client=client, workspace="test-workspace")
@@ -155,9 +155,7 @@ async def test_list_pipelines_error(self) -> None:
155155
async def test_list_pipelines_with_zero_limit(self) -> None:
156156
"""Test listing pipelines with a limit of zero (edge case)."""
157157
# Create client
158-
client = DummyClient(
159-
responses={"test-workspace/pipelines?page_number=1&limit=0": {"data": [], "has_more": False, "total": 10}}
160-
)
158+
client = DummyClient(responses={"test-workspace/pipelines": {"data": [], "has_more": False, "total": 10}})
161159

162160
# Create resource and call list method with limit=0
163161
resource = PipelineResource(client=client, workspace="test-workspace")
@@ -167,7 +165,8 @@ async def test_list_pipelines_with_zero_limit(self) -> None:
167165
assert len(result) == 0
168166

169167
# Verify request
170-
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines?page_number=1&limit=0"
168+
assert client.requests[0]["endpoint"] == "v1/workspaces/test-workspace/pipelines"
169+
assert client.requests[0]["params"] == {"page_number": 1, "limit": 0}
171170

172171
@pytest.mark.asyncio
173172
async def test_get_pipeline_with_yaml(self) -> None:

test/unit/api/pipeline_template/test_pipeline_template_resource.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ async def test_list_templates_default_params(self) -> None:
105105
# Create client with predefined response
106106
client = DummyClient(
107107
responses={
108-
"test-workspace/pipeline_templates?limit=100&page_number=1&field=created_at&order=DESC": {
108+
"test-workspace/pipeline_templates": {
109109
"data": sample_templates,
110110
"has_more": False,
111111
"total": 2,
@@ -125,10 +125,7 @@ async def test_list_templates_default_params(self) -> None:
125125

126126
# Verify request
127127
assert len(client.requests) == 1
128-
assert (
129-
client.requests[0]["endpoint"]
130-
== "/v1/workspaces/test-workspace/pipeline_templates?limit=100&page_number=1&field=created_at&order=DESC"
131-
)
128+
assert client.requests[0]["endpoint"] == "/v1/workspaces/test-workspace/pipeline_templates"
132129
assert client.requests[0]["method"] == "GET"
133130

134131
@pytest.mark.asyncio
@@ -142,7 +139,7 @@ async def test_list_templates_custom_limit(self) -> None:
142139
# Create client with predefined response
143140
client = DummyClient(
144141
responses={
145-
"test-workspace/pipeline_templates?limit=1&page_number=1&field=created_at&order=DESC": {
142+
"test-workspace/pipeline_templates": {
146143
"data": sample_templates,
147144
"has_more": True,
148145
"total": 2,
@@ -160,18 +157,15 @@ async def test_list_templates_custom_limit(self) -> None:
160157
assert result[0].template_name == "Template 1"
161158

162159
# Verify request
163-
assert (
164-
client.requests[0]["endpoint"]
165-
== "/v1/workspaces/test-workspace/pipeline_templates?limit=1&page_number=1&field=created_at&order=DESC"
166-
)
160+
assert client.requests[0]["endpoint"] == "/v1/workspaces/test-workspace/pipeline_templates"
167161

168162
@pytest.mark.asyncio
169163
async def test_list_templates_empty_result(self) -> None:
170164
"""Test listing templates when there are no templates."""
171165
# Create client with empty response
172166
client = DummyClient(
173167
responses={
174-
"test-workspace/pipeline_templates?limit=100&page_number=1&field=created_at&order=DESC": {
168+
"test-workspace/pipeline_templates": {
175169
"data": [],
176170
"has_more": False,
177171
"total": 0,

test/unit/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ async def request(
3838
method: str = "GET",
3939
data: dict[str, Any] | None = None,
4040
headers: dict[str, str] | None = None,
41+
**kwargs: Any,
4142
) -> TransportResponse[T]: ...
4243

4344
@overload
@@ -49,6 +50,7 @@ async def request(
4950
method: str = "GET",
5051
data: dict[str, Any] | None = None,
5152
headers: dict[str, str] | None = None,
53+
**kwargs: Any,
5254
) -> TransportResponse[Any]: ...
5355

5456
async def request(
@@ -59,6 +61,7 @@ async def request(
5961
method: str = "GET",
6062
data: dict[str, Any] | None = None,
6163
headers: dict[str, str] | None = None,
64+
**kwargs: Any,
6265
) -> TransportResponse[Any]:
6366
"""
6467
Record the request and return a predefined response.
@@ -84,7 +87,7 @@ async def request(
8487
ValueError
8588
If no response is predefined for the endpoint.
8689
"""
87-
self.requests.append({"endpoint": endpoint, "method": method, "data": data, "headers": headers})
90+
self.requests.append({"endpoint": endpoint, "method": method, "data": data, "headers": headers, **kwargs})
8891

8992
# Find the appropriate response
9093
for resp_key, resp_data in self.responses.items():

0 commit comments

Comments
 (0)