Issues (5)

src/Web/Hook.php (2 issues)

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
$config is never a sub-type of Joesama\Webhook\Connectors\ConnectorContract.
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
Bug Best Practice introduced by
The expression return Illuminate\Suppor...trtolower($type), null) could return the type array<string,string> which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
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