Skip to content

Commit a3a68cf

Browse files
George-iamgeobon
andauthored
feat: add users profile and nick helpers to Python SDK (#12)
Expose users contract family methods with tests and quickstart examples so GA parity covers nick lifecycle and profile operations. Made-with: Cursor Co-authored-by: George-iam <georgeb@gmail.com>
1 parent 97f5229 commit a3a68cf

3 files changed

Lines changed: 215 additions & 0 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,25 @@ with AxmeClient(config) as client:
9292
print(schema["schema"]["schema_hash"])
9393
schema_get = client.get_schema("axme.calendar.schedule.v1")
9494
print(schema_get["schema"]["semantic_type"])
95+
registered = client.register_nick(
96+
{"nick": "@partner.user", "display_name": "Partner User"},
97+
idempotency_key="nick-register-001",
98+
)
99+
print(registered["owner_agent"])
100+
nick_check = client.check_nick("@partner.user")
101+
print(nick_check["available"])
102+
renamed = client.rename_nick(
103+
{"owner_agent": registered["owner_agent"], "nick": "@partner.new"},
104+
idempotency_key="nick-rename-001",
105+
)
106+
print(renamed["public_address"])
107+
profile = client.get_user_profile(registered["owner_agent"])
108+
print(profile["updated_at"])
109+
profile_updated = client.update_user_profile(
110+
{"owner_agent": registered["owner_agent"], "display_name": "Partner User Updated"},
111+
idempotency_key="profile-update-001",
112+
)
113+
print(profile_updated["display_name"])
95114
subscription = client.upsert_webhook_subscription(
96115
{
97116
"callback_url": "https://integrator.example/webhooks/axme",

axme_sdk/client.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,66 @@ def upsert_schema(
252252
def get_schema(self, semantic_type: str, *, trace_id: str | None = None) -> dict[str, Any]:
253253
return self._request_json("GET", f"/v1/schemas/{semantic_type}", trace_id=trace_id, retryable=True)
254254

255+
def register_nick(
256+
self,
257+
payload: dict[str, Any],
258+
*,
259+
idempotency_key: str | None = None,
260+
trace_id: str | None = None,
261+
) -> dict[str, Any]:
262+
return self._request_json(
263+
"POST",
264+
"/v1/users/register-nick",
265+
json_body=payload,
266+
idempotency_key=idempotency_key,
267+
trace_id=trace_id,
268+
retryable=idempotency_key is not None,
269+
)
270+
271+
def check_nick(self, nick: str, *, trace_id: str | None = None) -> dict[str, Any]:
272+
return self._request_json("GET", "/v1/users/check-nick", params={"nick": nick}, trace_id=trace_id, retryable=True)
273+
274+
def rename_nick(
275+
self,
276+
payload: dict[str, Any],
277+
*,
278+
idempotency_key: str | None = None,
279+
trace_id: str | None = None,
280+
) -> dict[str, Any]:
281+
return self._request_json(
282+
"POST",
283+
"/v1/users/rename-nick",
284+
json_body=payload,
285+
idempotency_key=idempotency_key,
286+
trace_id=trace_id,
287+
retryable=idempotency_key is not None,
288+
)
289+
290+
def get_user_profile(self, owner_agent: str, *, trace_id: str | None = None) -> dict[str, Any]:
291+
return self._request_json(
292+
"GET",
293+
"/v1/users/profile",
294+
params={"owner_agent": owner_agent},
295+
trace_id=trace_id,
296+
retryable=True,
297+
)
298+
299+
def update_user_profile(
300+
self,
301+
payload: dict[str, Any],
302+
*,
303+
idempotency_key: str | None = None,
304+
trace_id: str | None = None,
305+
) -> dict[str, Any]:
306+
return self._request_json(
307+
"POST",
308+
"/v1/users/profile/update",
309+
json_body=payload,
310+
idempotency_key=idempotency_key,
311+
trace_id=trace_id,
312+
retryable=idempotency_key is not None,
313+
)
314+
255315
def upsert_webhook_subscription(
256316
self,
257317
payload: dict[str, Any],

tests/test_client.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,142 @@ def handler(request: httpx.Request) -> httpx.Response:
526526
assert client.get_schema(semantic_type)["schema"]["semantic_type"] == semantic_type
527527

528528

529+
def test_register_nick_success() -> None:
530+
payload = {"nick": "@partner.user", "display_name": "Partner User"}
531+
user_id = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
532+
owner_agent = f"agent://user/{user_id}"
533+
534+
def handler(request: httpx.Request) -> httpx.Response:
535+
assert request.method == "POST"
536+
assert request.url.path == "/v1/users/register-nick"
537+
assert request.headers["idempotency-key"] == "nick-register-1"
538+
assert json.loads(request.read().decode("utf-8")) == payload
539+
return httpx.Response(
540+
200,
541+
json={
542+
"ok": True,
543+
"user_id": user_id,
544+
"owner_agent": owner_agent,
545+
"nick": "@partner.user",
546+
"public_address": "partner.user@ax",
547+
"display_name": "Partner User",
548+
"phone": None,
549+
"email": None,
550+
"created_at": "2026-02-28T00:00:00Z",
551+
},
552+
)
553+
554+
client = _client(handler)
555+
assert client.register_nick(payload, idempotency_key="nick-register-1")["owner_agent"] == owner_agent
556+
557+
558+
def test_check_nick_success() -> None:
559+
nick = "@partner.user"
560+
561+
def handler(request: httpx.Request) -> httpx.Response:
562+
assert request.method == "GET"
563+
assert request.url.path == "/v1/users/check-nick"
564+
assert request.url.params.get("nick") == nick
565+
return httpx.Response(
566+
200,
567+
json={
568+
"ok": True,
569+
"nick": nick,
570+
"normalized_nick": "partner.user",
571+
"public_address": "partner.user@ax",
572+
"available": True,
573+
},
574+
)
575+
576+
client = _client(handler)
577+
assert client.check_nick(nick)["available"] is True
578+
579+
580+
def test_rename_nick_success() -> None:
581+
payload = {"owner_agent": "agent://user/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "nick": "@partner.new"}
582+
583+
def handler(request: httpx.Request) -> httpx.Response:
584+
assert request.method == "POST"
585+
assert request.url.path == "/v1/users/rename-nick"
586+
assert request.headers["idempotency-key"] == "nick-rename-1"
587+
assert json.loads(request.read().decode("utf-8")) == payload
588+
return httpx.Response(
589+
200,
590+
json={
591+
"ok": True,
592+
"user_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
593+
"owner_agent": payload["owner_agent"],
594+
"nick": "@partner.new",
595+
"public_address": "partner.new@ax",
596+
"display_name": "Partner User",
597+
"phone": None,
598+
"email": None,
599+
"renamed_at": "2026-02-28T00:00:01Z",
600+
},
601+
)
602+
603+
client = _client(handler)
604+
assert client.rename_nick(payload, idempotency_key="nick-rename-1")["nick"] == "@partner.new"
605+
606+
607+
def test_get_user_profile_success() -> None:
608+
owner_agent = "agent://user/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
609+
610+
def handler(request: httpx.Request) -> httpx.Response:
611+
assert request.method == "GET"
612+
assert request.url.path == "/v1/users/profile"
613+
assert request.url.params.get("owner_agent") == owner_agent
614+
return httpx.Response(
615+
200,
616+
json={
617+
"ok": True,
618+
"user_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
619+
"owner_agent": owner_agent,
620+
"nick": "@partner.new",
621+
"normalized_nick": "partner.new",
622+
"public_address": "partner.new@ax",
623+
"display_name": "Partner User",
624+
"phone": None,
625+
"email": None,
626+
"updated_at": "2026-02-28T00:00:02Z",
627+
},
628+
)
629+
630+
client = _client(handler)
631+
assert client.get_user_profile(owner_agent)["nick"] == "@partner.new"
632+
633+
634+
def test_update_user_profile_success() -> None:
635+
payload = {
636+
"owner_agent": "agent://user/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
637+
"display_name": "Partner Updated",
638+
}
639+
640+
def handler(request: httpx.Request) -> httpx.Response:
641+
assert request.method == "POST"
642+
assert request.url.path == "/v1/users/profile/update"
643+
assert request.headers["idempotency-key"] == "profile-update-1"
644+
assert json.loads(request.read().decode("utf-8")) == payload
645+
return httpx.Response(
646+
200,
647+
json={
648+
"ok": True,
649+
"user_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
650+
"owner_agent": payload["owner_agent"],
651+
"nick": "@partner.new",
652+
"normalized_nick": "partner.new",
653+
"public_address": "partner.new@ax",
654+
"display_name": "Partner Updated",
655+
"phone": None,
656+
"email": None,
657+
"updated_at": "2026-02-28T00:00:03Z",
658+
},
659+
)
660+
661+
client = _client(handler)
662+
assert client.update_user_profile(payload, idempotency_key="profile-update-1")["display_name"] == "Partner Updated"
663+
664+
529665
@pytest.mark.parametrize(
530666
("status_code", "expected_exception"),
531667
[

0 commit comments

Comments
 (0)