1 | <?php |
||
2 | |||
3 | namespace Joesama\Webhook\Web; |
||
4 | |||
5 | use GuzzleHttp\Exception\TransferException; |
||
6 | use GuzzleHttp\RequestOptions; |
||
7 | use Illuminate\Support\Arr; |
||
8 | use Illuminate\Support\Facades\Log; |
||
9 | use Illuminate\Support\Str; |
||
10 | use Joesama\Webhook\Connectors\ConnectorContract; |
||
11 | use Joesama\Webhook\Exceptions\WebHookException; |
||
12 | use Psr\Http\Message\RequestInterface; |
||
13 | use Psr\Http\Message\ResponseInterface; |
||
14 | |||
15 | class Hook extends Ping |
||
16 | { |
||
17 | /** |
||
18 | * WebHook request configuration. |
||
19 | * |
||
20 | * @var array |
||
21 | */ |
||
22 | private array $hooks; |
||
23 | |||
24 | /** |
||
25 | * Implementation of ConnectorContract. |
||
26 | * |
||
27 | * @var ConnectorContract|null |
||
28 | */ |
||
29 | private ?ConnectorContract $connector = null; |
||
30 | |||
31 | /** |
||
32 | * Connector id definition. |
||
33 | * |
||
34 | * @var string|null |
||
35 | */ |
||
36 | private ?string $connectorId = null; |
||
37 | |||
38 | /** |
||
39 | * WebHook constructor. |
||
40 | * |
||
41 | * @param ConnectorContract|array|string $config |string $config |
||
42 | */ |
||
43 | 11 | public function __construct(ConnectorContract|array|string $config = []) |
|
44 | { |
||
45 | 11 | $this->configs = ($config instanceof ConnectorContract) ? |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
46 | 1 | $this->connectorConfigurable($config) : |
|
47 | $this->webHookConfigurable($config); |
||
48 | 10 | } |
|
49 | |||
50 | 11 | /** |
|
51 | * Set the request body parameter. |
||
52 | */ |
||
53 | public function setRequestBody(array $request, string $type = 'json'): self |
||
54 | { |
||
55 | $this->options[$type] = $request; |
||
56 | |||
57 | if ($contentType = $this->mapContentType($type)) { |
||
58 | $this->setRequestHeader(['Content-type' => $contentType]); |
||
59 | 5 | } |
|
60 | |||
61 | 5 | return $this; |
|
62 | } |
||
63 | 5 | ||
64 | /** |
||
65 | 5 | * Set request header parameter. |
|
66 | */ |
||
67 | public function setRequestHeader(array $headers): self |
||
68 | { |
||
69 | $this->configs[RequestOptions::HEADERS] = array_merge( |
||
70 | Arr::get($this->configs, RequestOptions::HEADERS, []), |
||
71 | $headers |
||
72 | ); |
||
73 | |||
74 | 1 | return $this; |
|
75 | } |
||
76 | 1 | ||
77 | 1 | /** |
|
78 | 1 | * Attached configuration parameters. |
|
79 | * |
||
80 | * @param array|string $config |
||
81 | 1 | * @return Hook |
|
82 | */ |
||
83 | public function configurable(array|string $config): self |
||
84 | { |
||
85 | $this->configs = $this->webHookConfigurable($config); |
||
86 | |||
87 | return $this; |
||
88 | } |
||
89 | |||
90 | 1 | /** |
|
91 | * Get response from end point. |
||
92 | 1 | * |
|
93 | * @param string|null $url Endpoint URL |
||
94 | 1 | * @param string $method Request method |
|
95 | * @return mixed |
||
96 | */ |
||
97 | public function getResponse(string $url = null, string $method = 'POST') |
||
98 | { |
||
99 | $this->setUrlRequest($method, $url); |
||
100 | |||
101 | return $this->responseHandler($this->dispatch(), $this->getPsr7RequestHeader()); |
||
102 | } |
||
103 | |||
104 | 1 | /** |
|
105 | * Prepare configuration parameter. |
||
106 | 1 | * |
|
107 | * @param array|string $config |
||
108 | 1 | * @return array |
|
109 | */ |
||
110 | private function webHookConfigurable(array|string $config): array |
||
111 | { |
||
112 | $hookConfig = new Config($config); |
||
113 | |||
114 | $this->hooks = $hookConfig->hooks; |
||
115 | |||
116 | return array_merge($hookConfig->configs, $this->configs); |
||
117 | 11 | } |
|
118 | |||
119 | 11 | /** |
|
120 | * Implement WebHookConnector definition. |
||
121 | * |
||
122 | * @param ConnectorContract $connector |
||
123 | * @return void |
||
124 | */ |
||
125 | private function implementWebHookConnector(ConnectorContract $connector): void |
||
126 | { |
||
127 | 11 | $this->connector = $connector; |
|
128 | |||
129 | $this->connectorId = $connector->getConnectorId(); |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Map request content with header content type. |
||
134 | * |
||
135 | 1 | * @param string $type |
|
136 | * @return string|null |
||
137 | 1 | */ |
|
138 | private function mapContentType(string $type): ?string |
||
139 | 1 | { |
|
140 | $default = [ |
||
141 | 1 | RequestOptions::JSON => 'application/json', |
|
142 | RequestOptions::MULTIPART => 'multipart/form-data', |
||
143 | 1 | RequestOptions::FORM_PARAMS => 'application/x-www-form-urlencoded', |
|
144 | ]; |
||
145 | 1 | ||
146 | return Arr::get($default, strtolower($type), null); |
||
0 ignored issues
–
show
|
|||
147 | 1 | } |
|
148 | 1 | ||
149 | 1 | /** |
|
150 | * Set url & request method from webhook configurable. |
||
151 | * |
||
152 | 1 | * @param string $method |
|
153 | 1 | * @param string|null $url |
|
154 | * @return void |
||
155 | */ |
||
156 | private function setUrlRequest(string $method, ?string $url): void |
||
157 | { |
||
158 | $this->method = $method; |
||
159 | |||
160 | 6 | $this->pathUri = Str::contains($url, '/') ? $url : Arr::get($this->hooks, $url. '.' .$method, $url); |
|
161 | } |
||
162 | |||
163 | 6 | /** |
|
164 | 6 | * WebHook HTTP response handler. |
|
165 | 6 | * |
|
166 | * @param ResponseInterface $response |
||
167 | * @param RequestInterface $request |
||
168 | 6 | * @return mixed |
|
169 | 5 | */ |
|
170 | private function responseHandler(ResponseInterface $response, RequestInterface $request): mixed |
||
171 | 6 | { |
|
172 | if ($this->connector) { |
||
173 | return $this->connector->webHookResponse($response, $request); |
||
174 | } |
||
175 | |||
176 | return $response; |
||
177 | } |
||
178 | |||
179 | 1 | /** |
|
180 | * WebHook HTTP request exception handler. |
||
181 | 1 | * |
|
182 | * |
||
183 | 1 | * @param TransferException $exception |
|
184 | 1 | * @return mixed |
|
185 | * |
||
186 | * @throws WebHookException |
||
187 | */ |
||
188 | protected function exceptionHandlers(TransferException $exception): mixed |
||
189 | { |
||
190 | $this->logExceptionError($exception); |
||
191 | |||
192 | if ($this->connector) { |
||
193 | return $this->connector->webHookException($exception, $this->getPsr7RequestHeader()); |
||
194 | } |
||
195 | |||
196 | throw new WebHookException($exception->getMessage(), $exception->getCode(), $exception); |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Log error exception produce. |
||
201 | */ |
||
202 | private function logExceptionError(TransferException $exception): void |
||
203 | { |
||
204 | $logData = $this->logDataFormat(); |
||
205 | |||
206 | $logData['response'] = $exception->getMessage(); |
||
207 | |||
208 | if ($this->connector) { |
||
209 | //Need to notify system log error has occurs as the exception are handles. |
||
210 | 1 | $this->notifyCriticalLog($exception, $logData); |
|
211 | } |
||
212 | 1 | ||
213 | $this->saveLogToDatabase($logData); |
||
214 | 1 | } |
|
215 | |||
216 | /** |
||
217 | * Sent critical notification to application log. |
||
218 | 1 | */ |
|
219 | private function notifyCriticalLog(TransferException $exception, array $logData): void |
||
220 | { |
||
221 | Log::critical( |
||
222 | $this->connectorId, |
||
223 | [ |
||
224 | 'line' => $exception->getLine(), |
||
225 | 'file' => $exception->getFile(), |
||
226 | 1 | 'code' => $exception->getCode(), |
|
227 | 'url' => $logData['endpoint'], |
||
228 | 1 | 'method' => $this->method, |
|
229 | ] |
||
230 | 1 | ); |
|
231 | } |
||
232 | 1 | ||
233 | /** |
||
234 | * Save the log data to data base. |
||
235 | */ |
||
236 | private function saveLogToDatabase(array $logData) |
||
237 | 1 | { |
|
238 | 1 | if ($this->connector) { |
|
239 | //Saving log to data storage or etc... |
||
240 | $this->connector->webHookSavingData($logData); |
||
241 | } |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Format the log data. |
||
246 | */ |
||
247 | private function logDataFormat(): array |
||
248 | { |
||
249 | $request = $this->getPsr7RequestHeader(); |
||
250 | |||
251 | return [ |
||
252 | 'method' => $this->method, |
||
253 | 'endpoint' => Arr::first($request->getHeader('base_uri')) . $request->getUri()->getPath(), |
||
254 | 'request' => array_merge($this->configs, $this->options), |
||
255 | 'response' => null, |
||
256 | ]; |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * @param ConnectorContract $connector |
||
261 | * @return array |
||
262 | */ |
||
263 | protected function connectorConfigurable(ConnectorContract $connector):array |
||
264 | { |
||
265 | 1 | $this->implementWebHookConnector($connector); |
|
266 | |||
267 | 1 | $configs = $this->webHookConfigurable($connector->webHookConfiguration()); |
|
268 | |||
269 | $type = $connector->webHookContentType(); |
||
270 | $contentType = $this->mapContentType($type); |
||
271 | 1 | ||
272 | $configs[RequestOptions::HEADERS] = array_merge(array_merge( |
||
273 | $connector->webHookHeader(), |
||
274 | $contentType ? ['Content-type' => $contentType] : [] |
||
275 | ) |
||
276 | ); |
||
277 | |||
278 | 1 | $this->options[$type] = $connector->webHookContent(); |
|
279 | |||
280 | 1 | return $configs; |
|
281 | } |
||
282 | } |
||
283 |