Laravel AT Protocol Client (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpClient\Client;
4
5use BackedEnum;
6use Illuminate\Support\Facades\Http;
7use SocialDept\AtpClient\AtpClient;
8use SocialDept\AtpClient\Exceptions\AtpResponseException;
9use SocialDept\AtpClient\Http\DPoPClient;
10use SocialDept\AtpClient\Http\HasHttp;
11use SocialDept\AtpClient\Http\Response;
12use SocialDept\AtpClient\Session\Session;
13use SocialDept\AtpClient\Session\SessionManager;
14
15class Client
16{
17 use HasHttp {
18 call as authenticatedCall;
19 postBlob as authenticatedPostBlob;
20 }
21
22 /**
23 * The parent AtpClient instance we belong to
24 */
25 protected AtpClient $atp;
26
27 /**
28 * Service URL for public mode
29 */
30 protected ?string $serviceUrl;
31
32 public function __construct(
33 AtpClient $parent,
34 ?SessionManager $sessions = null,
35 ?string $did = null,
36 ?string $serviceUrl = null,
37 ) {
38 $this->atp = $parent;
39 $this->sessions = $sessions;
40 $this->did = $did;
41 $this->serviceUrl = $serviceUrl;
42
43 if (! $this->isPublicMode()) {
44 $this->dpopClient = app(DPoPClient::class);
45 }
46 }
47
48 /**
49 * Check if client is in public mode (no authentication).
50 */
51 public function isPublicMode(): bool
52 {
53 return $this->sessions === null || $this->did === null;
54 }
55
56 /**
57 * Get the current session.
58 */
59 public function session(): Session
60 {
61 return $this->sessions->session($this->did);
62 }
63
64 /**
65 * Get the service URL.
66 */
67 public function serviceUrl(): string
68 {
69 return $this->serviceUrl;
70 }
71
72 /**
73 * Make XRPC call - routes to public or authenticated based on mode.
74 */
75 protected function call(
76 string|BackedEnum $endpoint,
77 string $method,
78 ?array $params = null,
79 ?array $body = null
80 ): Response {
81 if ($this->isPublicMode()) {
82 return $this->publicCall($endpoint, $method, $params, $body);
83 }
84
85 return $this->authenticatedCall($endpoint, $method, $params, $body);
86 }
87
88 /**
89 * Make public XRPC call (no authentication).
90 */
91 protected function publicCall(
92 string|BackedEnum $endpoint,
93 string $method,
94 ?array $params = null,
95 ?array $body = null
96 ): Response {
97 $endpoint = $endpoint instanceof BackedEnum ? $endpoint->value : $endpoint;
98 $url = rtrim($this->serviceUrl, '/') . '/xrpc/' . $endpoint;
99 $params = array_filter($params ?? [], fn ($v) => ! is_null($v));
100
101 $response = match ($method) {
102 'GET' => Http::get($url, $params),
103 'POST' => Http::post($url, $body ?? $params),
104 'DELETE' => Http::delete($url, $params),
105 default => throw new \InvalidArgumentException("Unsupported method: {$method}"),
106 };
107
108 if ($response->failed() || isset($response->json()['error'])) {
109 throw AtpResponseException::fromResponse($response, $endpoint);
110 }
111
112 return new Response($response);
113 }
114
115 /**
116 * Make POST request with raw binary body (for blob uploads).
117 * Only works in authenticated mode.
118 */
119 public function postBlob(string|BackedEnum $endpoint, string $data, string $mimeType): Response
120 {
121 if ($this->isPublicMode()) {
122 throw new \RuntimeException('Blob uploads require authentication.');
123 }
124
125 return $this->authenticatedPostBlob($endpoint, $data, $mimeType);
126 }
127}