Skip to content

Commit ae3c5cd

Browse files
Use account-scoped URL prefix for agent API requests
Agent endpoints now use /v1/Account/{auth_id}/Agent and /v1/Account/{auth_id}/AgentCall paths instead of /v1/Agent and /v1/AgentCall. Add agents_base_url property to HttpTransport.
1 parent c4cc7b3 commit ae3c5cd

File tree

3 files changed

+66
-57
lines changed

3 files changed

+66
-57
lines changed

src/plivo_agent/_http.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def __init__(
6767
def auth_id(self) -> str:
6868
return self._auth_id
6969

70+
@property
71+
def agents_base_url(self) -> str:
72+
return f"{self._base_url}/v1/Account/{self._auth_id}"
73+
7074
async def request(
7175
self,
7276
method: str,

src/plivo_agent/agent/client.py

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,59 +12,61 @@
1212

1313

1414
class AgentResource:
15-
"""Agent CRUD -- POST/GET/PATCH/DELETE /v1/Agent
15+
"""Agent CRUD -- POST/GET/PATCH/DELETE /Agent
1616
1717
Agent IDs are UUIDs (e.g. "550e8400-e29b-41d4-a716-446655440000").
1818
Creating an agent requires ``agent_name``.
1919
"""
2020

21-
def __init__(self, http: HttpTransport) -> None:
21+
def __init__(self, http: HttpTransport, prefix: str) -> None:
2222
self._http = http
23+
self._prefix = prefix
2324

2425
async def create(self, **kwargs: Any) -> dict:
25-
"""POST /v1/Agent
26+
"""POST /Agent
2627
2728
Required fields: ``agent_name``, ``websocket_url``.
2829
Returns the created agent with ``agent_uuid`` as the identifier.
2930
"""
30-
return await self._http.request("POST", "/v1/Agent", json=kwargs)
31+
return await self._http.request("POST", f"{self._prefix}/Agent", json=kwargs)
3132

3233
async def get(self, agent_uuid: str) -> dict:
33-
"""GET /v1/Agent/{agent_uuid}"""
34-
return await self._http.request("GET", f"/v1/Agent/{agent_uuid}")
34+
"""GET /Agent/{agent_uuid}"""
35+
return await self._http.request("GET", f"{self._prefix}/Agent/{agent_uuid}")
3536

3637
async def list(self, **params: Any) -> dict:
37-
"""GET /v1/Agent -- paginated list.
38+
"""GET /Agent -- paginated list.
3839
3940
Optional query params: page, per_page, sort_by, sort_order,
4041
agent_mode, participant_mode.
4142
4243
Returns ``{"data": [...], "meta": {"page", "per_page", "total", "total_pages"}}``.
4344
"""
44-
return await self._http.request("GET", "/v1/Agent", params=params)
45+
return await self._http.request("GET", f"{self._prefix}/Agent", params=params)
4546

4647
async def update(self, agent_uuid: str, **kwargs: Any) -> dict:
47-
"""PATCH /v1/Agent/{agent_uuid}"""
48+
"""PATCH /Agent/{agent_uuid}"""
4849
return await self._http.request(
49-
"PATCH", f"/v1/Agent/{agent_uuid}", json=kwargs
50+
"PATCH", f"{self._prefix}/Agent/{agent_uuid}", json=kwargs
5051
)
5152

5253
async def delete(self, agent_uuid: str) -> None:
53-
"""DELETE /v1/Agent/{agent_uuid}"""
54-
await self._http.request("DELETE", f"/v1/Agent/{agent_uuid}")
54+
"""DELETE /Agent/{agent_uuid}"""
55+
await self._http.request("DELETE", f"{self._prefix}/Agent/{agent_uuid}")
5556

5657

5758
class CallResource:
5859
"""Call management -- connect, initiate, dial."""
5960

60-
def __init__(self, http: HttpTransport) -> None:
61+
def __init__(self, http: HttpTransport, prefix: str) -> None:
6162
self._http = http
63+
self._prefix = prefix
6264

6365
async def connect(self, call_uuid: str, agent_uuid: str) -> dict:
64-
"""POST /v1/AgentCall/{call_uuid}/connect -- connect an active call to an agent."""
66+
"""POST /AgentCall/{call_uuid}/connect -- connect an active call to an agent."""
6567
return await self._http.request(
6668
"POST",
67-
f"/v1/AgentCall/{call_uuid}/connect",
69+
f"{self._prefix}/AgentCall/{call_uuid}/connect",
6870
json={"agent_id": agent_uuid},
6971
)
7072

@@ -77,7 +79,7 @@ async def initiate(
7779
voicemail_detect: bool = False,
7880
**kwargs: Any,
7981
) -> dict:
80-
"""POST /v1/AgentCall -- initiate an outbound call.
82+
"""POST /AgentCall -- initiate an outbound call.
8183
8284
Args:
8385
agent_uuid: Agent UUID to handle the call.
@@ -93,15 +95,15 @@ async def initiate(
9395
if voicemail_detect:
9496
body["voicemail_detect"] = True
9597
body.update(kwargs)
96-
return await self._http.request("POST", "/v1/AgentCall", json=body)
98+
return await self._http.request("POST", f"{self._prefix}/AgentCall", json=body)
9799

98100
async def dial(
99101
self,
100102
call_uuid: str,
101103
targets: list[dict],
102104
**kwargs: Any,
103105
) -> dict:
104-
"""POST /v1/AgentCall/{call_uuid}/dial -- dial out to one or more targets.
106+
"""POST /AgentCall/{call_uuid}/dial -- dial out to one or more targets.
105107
106108
Args:
107109
call_uuid: Active call UUID.
@@ -110,7 +112,7 @@ async def dial(
110112
"""
111113
body: dict[str, Any] = {"targets": targets, **kwargs}
112114
return await self._http.request(
113-
"POST", f"/v1/AgentCall/{call_uuid}/dial", json=body
115+
"POST", f"{self._prefix}/AgentCall/{call_uuid}/dial", json=body
114116
)
115117

116118

@@ -120,49 +122,51 @@ class NumberResource:
120122
Numbers are in E.164 format (e.g. "+14155551234").
121123
"""
122124

123-
def __init__(self, http: HttpTransport) -> None:
125+
def __init__(self, http: HttpTransport, prefix: str) -> None:
124126
self._http = http
127+
self._prefix = prefix
125128

126129
async def assign(self, agent_uuid: str, number: str) -> dict:
127-
"""POST /v1/Agent/{agent_uuid}/Number -- assign a number to an agent."""
130+
"""POST /Agent/{agent_uuid}/Number -- assign a number to an agent."""
128131
return await self._http.request(
129132
"POST",
130-
f"/v1/Agent/{agent_uuid}/Number",
133+
f"{self._prefix}/Agent/{agent_uuid}/Number",
131134
json={"number": number},
132135
)
133136

134137
async def list(self, agent_uuid: str) -> dict:
135-
"""GET /v1/Agent/{agent_uuid}/Number -- list numbers for an agent."""
138+
"""GET /Agent/{agent_uuid}/Number -- list numbers for an agent."""
136139
return await self._http.request(
137-
"GET", f"/v1/Agent/{agent_uuid}/Number"
140+
"GET", f"{self._prefix}/Agent/{agent_uuid}/Number"
138141
)
139142

140143
async def unassign(self, agent_uuid: str, number: str) -> None:
141-
"""DELETE /v1/Agent/{agent_uuid}/Number/{number} -- unassign a number."""
144+
"""DELETE /Agent/{agent_uuid}/Number/{number} -- unassign a number."""
142145
await self._http.request(
143-
"DELETE", f"/v1/Agent/{agent_uuid}/Number/{number}"
146+
"DELETE", f"{self._prefix}/Agent/{agent_uuid}/Number/{number}"
144147
)
145148

146149

147150
class SessionResource:
148151
"""Session history -- list and get agent sessions."""
149152

150-
def __init__(self, http: HttpTransport) -> None:
153+
def __init__(self, http: HttpTransport, prefix: str) -> None:
151154
self._http = http
155+
self._prefix = prefix
152156

153157
async def list(self, agent_uuid: str, **params: Any) -> dict:
154-
"""GET /v1/Agent/{agent_uuid}/Session -- list sessions.
158+
"""GET /Agent/{agent_uuid}/Session -- list sessions.
155159
156160
Optional query params: page, per_page, sort_by, sort_order, agent_mode.
157161
"""
158162
return await self._http.request(
159-
"GET", f"/v1/Agent/{agent_uuid}/Session", params=params
163+
"GET", f"{self._prefix}/Agent/{agent_uuid}/Session", params=params
160164
)
161165

162166
async def get(self, agent_uuid: str, session_id: str) -> dict:
163-
"""GET /v1/Agent/{agent_uuid}/Session/{session_id} -- get session details."""
167+
"""GET /Agent/{agent_uuid}/Session/{session_id} -- get session details."""
164168
return await self._http.request(
165-
"GET", f"/v1/Agent/{agent_uuid}/Session/{session_id}"
169+
"GET", f"{self._prefix}/Agent/{agent_uuid}/Session/{session_id}"
166170
)
167171

168172

@@ -178,7 +182,8 @@ class AgentClient:
178182

179183
def __init__(self, http: HttpTransport) -> None:
180184
self._http = http
181-
self.agents = AgentResource(http)
182-
self.calls = CallResource(http)
183-
self.numbers = NumberResource(http)
184-
self.sessions = SessionResource(http)
185+
prefix = f"/v1/Account/{http.auth_id}"
186+
self.agents = AgentResource(http, prefix)
187+
self.calls = CallResource(http, prefix)
188+
self.numbers = NumberResource(http, prefix)
189+
self.sessions = SessionResource(http, prefix)

tests/test_agent/test_client.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212

1313
async def test_create_agent(mock_api, http_transport):
14-
"""POST /v1/Agent creates an agent."""
15-
mock_api.post("/v1/Agent").mock(
14+
"""POST /v1/Account/TESTAUTH123/Agent creates an agent."""
15+
mock_api.post("/v1/Account/TESTAUTH123/Agent").mock(
1616
return_value=httpx.Response(
1717
200,
1818
json={"agent_uuid": AGENT_UUID, "agent_name": "My Agent"},
@@ -25,8 +25,8 @@ async def test_create_agent(mock_api, http_transport):
2525

2626

2727
async def test_get_agent(mock_api, http_transport):
28-
"""GET /v1/Agent/{uuid} retrieves an agent."""
29-
mock_api.get(f"/v1/Agent/{AGENT_UUID}").mock(
28+
"""GET /v1/Account/TESTAUTH123/Agent/{uuid} retrieves an agent."""
29+
mock_api.get(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}").mock(
3030
return_value=httpx.Response(
3131
200,
3232
json={"agent_uuid": AGENT_UUID, "agent_name": "My Agent"},
@@ -38,8 +38,8 @@ async def test_get_agent(mock_api, http_transport):
3838

3939

4040
async def test_list_agents(mock_api, http_transport):
41-
"""GET /v1/Agent lists agents with query params."""
42-
mock_api.get("/v1/Agent").mock(
41+
"""GET /v1/Account/TESTAUTH123/Agent lists agents with query params."""
42+
mock_api.get("/v1/Account/TESTAUTH123/Agent").mock(
4343
return_value=httpx.Response(
4444
200,
4545
json={"data": [{"agent_uuid": AGENT_UUID}], "meta": {"total": 1}},
@@ -52,8 +52,8 @@ async def test_list_agents(mock_api, http_transport):
5252

5353

5454
async def test_update_agent(mock_api, http_transport):
55-
"""PATCH /v1/Agent/{uuid} updates an agent."""
56-
mock_api.patch(f"/v1/Agent/{AGENT_UUID}").mock(
55+
"""PATCH /v1/Account/TESTAUTH123/Agent/{uuid} updates an agent."""
56+
mock_api.patch(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}").mock(
5757
return_value=httpx.Response(
5858
200,
5959
json={"agent_uuid": AGENT_UUID, "agent_name": "Updated Agent"},
@@ -65,8 +65,8 @@ async def test_update_agent(mock_api, http_transport):
6565

6666

6767
async def test_delete_agent(mock_api, http_transport):
68-
"""DELETE /v1/Agent/{uuid} deletes an agent (204)."""
69-
mock_api.delete(f"/v1/Agent/{AGENT_UUID}").mock(
68+
"""DELETE /v1/Account/TESTAUTH123/Agent/{uuid} deletes an agent (204)."""
69+
mock_api.delete(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}").mock(
7070
return_value=httpx.Response(204)
7171
)
7272
client = AgentClient(http_transport)
@@ -75,8 +75,8 @@ async def test_delete_agent(mock_api, http_transport):
7575

7676

7777
async def test_call_initiate(mock_api, http_transport):
78-
"""POST /v1/AgentCall initiates an outbound call."""
79-
mock_api.post("/v1/AgentCall").mock(
78+
"""POST /v1/Account/TESTAUTH123/AgentCall initiates an outbound call."""
79+
mock_api.post("/v1/Account/TESTAUTH123/AgentCall").mock(
8080
return_value=httpx.Response(
8181
200,
8282
json={"call_uuid": CALL_UUID, "status": "initiated"},
@@ -93,8 +93,8 @@ async def test_call_initiate(mock_api, http_transport):
9393

9494

9595
async def test_call_connect(mock_api, http_transport):
96-
"""POST /v1/AgentCall/{uuid}/connect connects a call to an agent."""
97-
mock_api.post(f"/v1/AgentCall/{CALL_UUID}/connect").mock(
96+
"""POST /v1/Account/TESTAUTH123/AgentCall/{uuid}/connect connects a call to an agent."""
97+
mock_api.post(f"/v1/Account/TESTAUTH123/AgentCall/{CALL_UUID}/connect").mock(
9898
return_value=httpx.Response(
9999
200,
100100
json={"status": "connected"},
@@ -106,8 +106,8 @@ async def test_call_connect(mock_api, http_transport):
106106

107107

108108
async def test_number_assign(mock_api, http_transport):
109-
"""POST /v1/Agent/{uuid}/Number assigns a number to an agent."""
110-
mock_api.post(f"/v1/Agent/{AGENT_UUID}/Number").mock(
109+
"""POST /v1/Account/TESTAUTH123/Agent/{uuid}/Number assigns a number to an agent."""
110+
mock_api.post(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Number").mock(
111111
return_value=httpx.Response(
112112
200,
113113
json={"status": "assigned", "number": "+14155551234"},
@@ -120,9 +120,9 @@ async def test_number_assign(mock_api, http_transport):
120120

121121

122122
async def test_number_unassign(mock_api, http_transport):
123-
"""DELETE /v1/Agent/{uuid}/Number/{num} unassigns a number."""
123+
"""DELETE /v1/Account/TESTAUTH123/Agent/{uuid}/Number/{num} unassigns a number."""
124124
number = "+14155551234"
125-
mock_api.delete(f"/v1/Agent/{AGENT_UUID}/Number/{number}").mock(
125+
mock_api.delete(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Number/{number}").mock(
126126
return_value=httpx.Response(204)
127127
)
128128
client = AgentClient(http_transport)
@@ -134,8 +134,8 @@ async def test_number_unassign(mock_api, http_transport):
134134

135135

136136
async def test_session_list(mock_api, http_transport):
137-
"""GET /v1/Agent/{uuid}/Session lists sessions."""
138-
mock_api.get(f"/v1/Agent/{AGENT_UUID}/Session").mock(
137+
"""GET /v1/Account/TESTAUTH123/Agent/{uuid}/Session lists sessions."""
138+
mock_api.get(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Session").mock(
139139
return_value=httpx.Response(
140140
200,
141141
json={
@@ -153,8 +153,8 @@ async def test_session_list(mock_api, http_transport):
153153

154154

155155
async def test_session_get(mock_api, http_transport):
156-
"""GET /v1/Agent/{uuid}/Session/{session_id} gets session details."""
157-
mock_api.get(f"/v1/Agent/{AGENT_UUID}/Session/{SESSION_ID}").mock(
156+
"""GET /v1/Account/TESTAUTH123/Agent/{uuid}/Session/{session_id} gets session details."""
157+
mock_api.get(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Session/{SESSION_ID}").mock(
158158
return_value=httpx.Response(
159159
200,
160160
json={

0 commit comments

Comments
 (0)