|
4 | 4 |
|
5 | 5 | namespace SharpAPI\SharpApiService;
|
6 | 6 |
|
7 |
| -use GuzzleHttp\Client; |
8 |
| -use GuzzleHttp\Exception\ClientException; |
9 | 7 | use GuzzleHttp\Exception\GuzzleException;
|
10 |
| -use Illuminate\Support\Carbon; |
11 | 8 | use InvalidArgumentException;
|
12 |
| -use Psr\Http\Message\ResponseInterface; |
| 9 | +use SharpAPI\Core\Client\SharpApiClient; |
13 | 10 | use SharpAPI\SharpApiService\Dto\JobDescriptionParameters;
|
14 |
| -use SharpAPI\SharpApiService\Dto\SharpApiJob; |
15 |
| -use SharpAPI\SharpApiService\Dto\SharpApiSubscriptionInfo; |
16 |
| -use SharpAPI\SharpApiService\Enums\SharpApiJobStatusEnum; |
17 | 11 | use SharpAPI\SharpApiService\Enums\SharpApiJobTypeEnum;
|
18 |
| -use Spatie\Url\Url; |
19 | 12 |
|
20 |
| -class SharpApiService |
| 13 | +class SharpApiService extends SharpApiClient |
21 | 14 | {
|
22 |
| - protected string $apiBaseUrl; |
23 |
| - |
24 |
| - protected string $apiKey; |
25 |
| - |
26 |
| - protected int $apiJobStatusPollingInterval = 5; |
27 |
| - |
28 |
| - protected int $apiJobStatusPollingWait = 180; |
29 |
| - |
30 |
| - protected string $userAgent; |
31 |
| - |
32 | 15 | /**
|
33 | 16 | * Initializes a new instance of the class.
|
34 | 17 | *
|
35 | 18 | * @throws InvalidArgumentException if the API key is empty.
|
36 | 19 | */
|
37 | 20 | public function __construct()
|
38 | 21 | {
|
39 |
| - $this->setApiKey(config('sharpapi-client.api_key')); |
40 |
| - if (empty($this->apiKey)) { |
41 |
| - throw new InvalidArgumentException('API key is required.'); |
42 |
| - } |
| 22 | + parent::__construct(config('sharpapi-client.api_key')); |
43 | 23 | $this->setApiBaseUrl(config('sharpapi-client.base_url', 'https://sharpapi.com/api/v1'));
|
44 | 24 | $this->setApiJobStatusPollingInterval((int) config('sharpapi-client.api_job_status_polling_interval', 5));
|
45 | 25 | $this->setApiJobStatusPollingWait((int) config('sharpapi-client.api_job_status_polling_wait', 180));
|
46 |
| - $this->setUserAgent('SharpAPILaravelAgent/1.2.0'); |
47 |
| - } |
48 |
| - |
49 |
| - /** |
50 |
| - * Fetch the main API URL |
51 |
| - */ |
52 |
| - public function getApiBaseUrl(): string |
53 |
| - { |
54 |
| - return $this->apiBaseUrl; |
55 |
| - } |
56 |
| - |
57 |
| - /** |
58 |
| - * Might come in handy if case some API mocking is needed |
59 |
| - */ |
60 |
| - public function setApiBaseUrl(string $apiBaseUrl): void |
61 |
| - { |
62 |
| - $this->apiBaseUrl = $apiBaseUrl; |
63 |
| - } |
64 |
| - |
65 |
| - public function getApiKey(): string |
66 |
| - { |
67 |
| - return $this->apiKey; |
68 |
| - } |
69 |
| - |
70 |
| - public function setApiKey(string $apiKey): void |
71 |
| - { |
72 |
| - $this->apiKey = $apiKey; |
73 |
| - } |
| 26 | + $this->setUserAgent('SharpAPILaravelAgent/1.2.2'); |
74 | 27 |
|
75 |
| - public function getUserAgent(): string |
76 |
| - { |
77 |
| - return $this->userAgent; |
78 |
| - } |
79 |
| - |
80 |
| - /** |
81 |
| - * Handy method to set custom User-Agent header for Affiliate Program members. |
82 |
| - * |
83 |
| - * More at: https://sharpapi.com/affiliate_program |
84 |
| - */ |
85 |
| - public function setUserAgent(string $userAgent): void |
86 |
| - { |
87 |
| - $this->userAgent = $userAgent; |
88 |
| - } |
89 |
| - |
90 |
| - public function getApiJobStatusPollingInterval(): int |
91 |
| - { |
92 |
| - return $this->apiJobStatusPollingInterval; |
93 |
| - } |
94 |
| - |
95 |
| - public function setApiJobStatusPollingInterval(int $apiJobStatusPollingInterval): void |
96 |
| - { |
97 |
| - $this->apiJobStatusPollingInterval = $apiJobStatusPollingInterval; |
98 |
| - } |
99 |
| - |
100 |
| - public function getApiJobStatusPollingWait(): int |
101 |
| - { |
102 |
| - return $this->apiJobStatusPollingWait; |
103 |
| - } |
104 |
| - |
105 |
| - public function setApiJobStatusPollingWait(int $apiJobStatusPollingWait): void |
106 |
| - { |
107 |
| - $this->apiJobStatusPollingWait = $apiJobStatusPollingWait; |
108 |
| - } |
109 |
| - |
110 |
| - /** |
111 |
| - * Generic request method to run Guzzle client |
112 |
| - * |
113 |
| - * @throws GuzzleException |
114 |
| - */ |
115 |
| - private function makeRequest( |
116 |
| - string $method, |
117 |
| - string $url, |
118 |
| - array $data = [], |
119 |
| - ?string $filePath = null |
120 |
| - ): ResponseInterface { |
121 |
| - $client = new Client(); |
122 |
| - $options = [ |
123 |
| - 'headers' => $this->getHeaders(), |
124 |
| - ]; |
125 |
| - if ($method === 'POST') { |
126 |
| - if (is_string($filePath) && strlen($filePath)) { |
127 |
| - $options['multipart'][] = |
128 |
| - [ |
129 |
| - 'name' => 'file', |
130 |
| - 'contents' => file_get_contents($filePath), |
131 |
| - 'filename' => basename($filePath), |
132 |
| - ]; |
133 |
| - } else { |
134 |
| - $options['json'] = $data; |
135 |
| - } |
136 |
| - } |
137 |
| - |
138 |
| - return $client->request($method, $this->getApiBaseUrl().$url, $options); |
139 |
| - } |
140 |
| - |
141 |
| - private function parseStatusUrl(ResponseInterface $response) |
142 |
| - { |
143 |
| - return json_decode($response->getBody()->__toString(), true)['status_url']; |
144 |
| - } |
145 |
| - |
146 |
| - /** |
147 |
| - * Generic method to check job status in polling mode and then fetch results of the dispatched job |
148 |
| - * |
149 |
| - * @throws ClientException|GuzzleException |
150 |
| - * |
151 |
| - * @api |
152 |
| - */ |
153 |
| - public function fetchResults(string $statusUrl): SharpApiJob |
154 |
| - { |
155 |
| - $client = new Client(); |
156 |
| - $waitingTime = 0; |
157 |
| - |
158 |
| - do { |
159 |
| - $response = $client->request( |
160 |
| - 'GET', |
161 |
| - $statusUrl, |
162 |
| - ['headers' => $this->getHeaders()] |
163 |
| - ); |
164 |
| - $jobStatus = json_decode($response->getBody()->__toString(), true)['data']['attributes']; |
165 |
| - |
166 |
| - if ( |
167 |
| - $jobStatus['status'] === SharpApiJobStatusEnum::SUCCESS->value |
168 |
| - || |
169 |
| - $jobStatus['status'] === SharpApiJobStatusEnum::FAILED->value |
170 |
| - ) { |
171 |
| - break; |
172 |
| - } // it's still `pending` status, let's wait a bit more |
173 |
| - $retryAfter = isset($response->getHeader('Retry-After')[0]) |
174 |
| - ? (int) $response->getHeader('Retry-After')[0] |
175 |
| - : $this->getApiJobStatusPollingInterval(); // fallback if no Retry-After header |
176 |
| - |
177 |
| - if (config('sharpapi-client.api_job_status_use_polling_interval')) { |
178 |
| - // let's force to use the value from config |
179 |
| - $retryAfter = $this->getApiJobStatusPollingInterval(); |
180 |
| - } |
181 |
| - $waitingTime = $waitingTime + $retryAfter; |
182 |
| - if ($waitingTime >= $this->getApiJobStatusPollingWait()) { |
183 |
| - break; |
184 |
| - } // otherwise wait a bit more and try again |
185 |
| - sleep($retryAfter); |
186 |
| - } while (true); |
187 |
| - |
188 |
| - $data = json_decode($response->getBody()->__toString(), true)['data']; |
189 |
| - |
190 |
| - $url = Url::fromString($statusUrl); |
191 |
| - if(count($url->getSegments()) == 5) { // shared job result URL |
192 |
| - $result = (object) json_decode($data['attributes']['result']); |
193 |
| - } else { // 7 segments, 1-to-1 job to result url |
194 |
| - $result = (object)$data['attributes']['result']; |
195 |
| - } |
196 |
| - |
197 |
| - return new SharpApiJob( |
198 |
| - id: $data['id'], |
199 |
| - type: $data['attributes']['type'], |
200 |
| - status: $data['attributes']['status'], |
201 |
| - result: $result ?? null |
202 |
| - ); |
203 |
| - } |
204 |
| - |
205 |
| - /** |
206 |
| - * Prepare shared headers |
207 |
| - * |
208 |
| - * @return string[] |
209 |
| - */ |
210 |
| - private function getHeaders(): array |
211 |
| - { |
212 |
| - return [ |
213 |
| - 'Authorization' => 'Bearer '.$this->getApiKey(), |
214 |
| - 'Accept' => 'application/json', |
215 |
| - 'User-Agent' => $this->getUserAgent(), |
216 |
| - ]; |
217 |
| - } |
218 |
| - |
219 |
| - /** |
220 |
| - * Simple PING endpoint to check the availability of the API and its internal time zone (timestamp). |
221 |
| - * { |
222 |
| - * "ping": "pong", |
223 |
| - * "timestamp": "2024-03-12T08:50:11.188308Z" |
224 |
| - * } |
225 |
| - * |
226 |
| - * @throws GuzzleException |
227 |
| - * |
228 |
| - * @api |
229 |
| - */ |
230 |
| - public function ping(): ?array |
231 |
| - { |
232 |
| - $response = $this->makeRequest('GET', '/ping'); |
233 |
| - |
234 |
| - return json_decode($response->getBody()->__toString(), true); |
235 |
| - } |
236 |
| - |
237 |
| - /** |
238 |
| - * Endpoint to check details regarding the subscription's current period |
239 |
| - * |
240 |
| - * "subscription_words_used_percentage" is a percentage of current monthly quota usage |
241 |
| - * and might serve as an alert to the user of the depleted credits. |
242 |
| - * With a value above 80%, it's advised to subscribe to more credits |
243 |
| - * at https://sharpapi.com/dashboard/credits to avoid service disruption. |
244 |
| - * |
245 |
| - * These values are also available in the Dashboard at https://sharpapi.com/dashboard |
246 |
| - * |
247 |
| - * @throws GuzzleException |
248 |
| - * |
249 |
| - * @api |
250 |
| - */ |
251 |
| - public function quota(): ?SharpApiSubscriptionInfo |
252 |
| - { |
253 |
| - $response = $this->makeRequest('GET', '/quota'); |
254 |
| - $info = json_decode($response->getBody()->__toString(), true); |
255 |
| - if (! array_key_exists('timestamp', $info)) { |
256 |
| - return null; |
257 |
| - } |
258 |
| - |
259 |
| - return new SharpApiSubscriptionInfo( |
260 |
| - timestamp: new Carbon($info['timestamp']), |
261 |
| - on_trial: $info['on_trial'], |
262 |
| - trial_ends: new Carbon($info['trial_ends']), |
263 |
| - subscribed: $info['subscribed'], |
264 |
| - current_subscription_start: new Carbon($info['current_subscription_start']), |
265 |
| - current_subscription_end: new Carbon($info['current_subscription_end']), |
266 |
| - subscription_words_quota: $info['subscription_words_quota'], |
267 |
| - subscription_words_used: $info['subscription_words_used'], |
268 |
| - subscription_words_used_percentage: $info['subscription_words_used_percentage'] |
269 |
| - ); |
270 | 28 | }
|
271 | 29 |
|
272 | 30 | /**
|
|
0 commit comments