joesama /
webhook
| 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
Loading history...
|
|||
| 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 |