Test Failed
Pull Request — master (#5)
by AHMED JOHARI
03:13
created

Hook::connectorConfigurable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.008

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 15
ccs 4
cts 5
cp 0.8
rs 10
cc 1
nc 1
nop 1
crap 1.008
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
35
     */
36
    private string $connectorId;
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
$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->mapContentType($type);
56
57
        $this->options[$type] = $request;
58
59 5
        return $this;
60
    }
61 5
62
    /**
63 5
     * Set request header parameter.
64
     */
65 5
    public function setRequestHeader(array $headers): self
66
    {
67
        $this->configs[RequestOptions::HEADERS] = array_merge(
68
            Arr::get($this->configs, RequestOptions::HEADERS, []),
69
            $headers
70
        );
71
72
        return $this;
73
    }
74 1
75
    /**
76 1
     * Attached configuration parameters.
77 1
     *
78 1
     * @param array|string $config
79
     * @return Hook
80
     */
81 1
    public function configurable(array|string $config): self
82
    {
83
        $this->configs = $this->webHookConfigurable($config);
84
85
        return $this;
86
    }
87
88
    /**
89
     * Get response from end point.
90 1
     *
91
     * @param string|null $url Endpoint URL
92 1
     * @param string $method Request method
93
     * @return mixed
94 1
     */
95
    public function getResponse(string $url = null, string $method = 'POST')
96
    {
97
        $this->setUrlRequest($method, $url);
98
99
        return $this->responseHandler($this->dispatch(), $this->getPsr7RequestHeader());
100
    }
101
102
    /**
103
     * Prepare configuration parameter.
104 1
     *
105
     * @param array|string $config
106 1
     * @return array
107
     */
108 1
    private function webHookConfigurable(array|string $config): array
109
    {
110
        if (is_string($config)) {
0 ignored issues
show
introduced by
The condition is_string($config) is always false.
Loading history...
111
            $hookConfig = new Config($config);
112
113
            $config = $hookConfig->configs;
114
115
            $this->hooks = $hookConfig->hooks;
116
        }
117 11
118
        return array_merge($config, $this->configs);
119 11
    }
120
121
    /**
122
     *  Implement WebHookConnector definition.
123
     *
124
     * @param ConnectorContract $connector
125
     * @return void
126
     */
127 11
    private function implementWebHookConnector(ConnectorContract $connector): void
128
    {
129
        $this->connector = $connector;
130
131
        $this->connectorId = $connector->getConnectorId();
132
    }
133
134
    /**
135 1
     * Map request content with header content type.
136
     *
137 1
     * @param string $type
138
     * @return string|null
139 1
     */
140
    private function mapContentType(string $type): ?string
141 1
    {
142
        $default = [
143 1
            RequestOptions::JSON => 'application/json',
144
            RequestOptions::MULTIPART => 'multipart/form-data',
145 1
            RequestOptions::FORM_PARAMS => 'application/x-www-form-urlencoded',
146
        ];
147 1
148 1
        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...
149 1
    }
150
151
    /**
152 1
     * Set url & request method from webhook configurable.
153 1
     *
154
     * @param string $method
155
     * @param string|null $url
156
     * @return void
157
     */
158
    private function setUrlRequest(string $method, ?string $url): void
159
    {
160 6
        $this->method = $method;
161
162
        $this->pathUri = Str::contains($url, '/') ? $url : Arr::get($this->hooks, $url. '.' .$method, $url);
163 6
    }
164 6
165 6
    /**
166
     * WebHook HTTP response handler.
167
     *
168 6
     * @param ResponseInterface $response
169 5
     * @param RequestInterface $request
170
     * @return mixed
171 6
     */
172
    private function responseHandler(ResponseInterface $response, RequestInterface $request): mixed
173
    {
174
        if ($this->connector instanceof ConnectorContract) {
175
            return $this->connector->webHookResponse($response, $request);
176
        }
177
178
        return $response;
179 1
    }
180
181 1
    /**
182
     * WebHook HTTP request exception handler.
183 1
     *
184 1
     *
185
     * @param TransferException $exception
186
     * @return mixed
187
     *
188
     * @throws WebHookException
189
     */
190
    protected function exceptionHandlers(TransferException $exception): mixed
191
    {
192
        $this->logExceptionError($exception);
193
194
        if ($this->connector) {
195
            return $this->connector->webHookException($exception, $this->getPsr7RequestHeader());
196
        }
197
198
        throw new WebHookException($exception->getMessage(), $exception->getCode(), $exception);
199
    }
200
201
    /**
202
     * Log error exception produce.
203
     */
204
    private function logExceptionError(TransferException $exception): void
205
    {
206
        $logData = $this->logDataFormat();
207
208
        $logData['response'] = $exception->getMessage();
209
210 1
        if ($this->connector instanceof ConnectorContract) {
211
            //Need to notify system log error has occurs as the exception are handles.
212 1
            $this->notifyCriticalLog($exception, $logData);
213
        }
214 1
215
        $this->saveLogToDatabase($logData);
216
    }
217
218 1
    /**
219
     * Sent critical notification to application log.
220
     */
221
    private function notifyCriticalLog(TransferException $exception, array $logData): void
222
    {
223
        Log::critical(
224
            $this->connectorId,
225
            [
226 1
                'line' => $exception->getLine(),
227
                'file' => $exception->getFile(),
228 1
                'code' => $exception->getCode(),
229
                'url' => $logData['endpoint'],
230 1
                'method' => $this->method,
231
            ]
232 1
        );
233
    }
234
235
    /**
236
     * Save the log data to data base.
237 1
     */
238 1
    private function saveLogToDatabase(array $logData)
239
    {
240
        if ($this->connector instanceof ConnectorContract) {
241
            //Saving log to data storage or etc...
242
            $this->connector->webHookSavingData($logData);
243
        }
244
    }
245
246
    /**
247
     * Format the log data.
248
     */
249
    private function logDataFormat(): array
250
    {
251
        $request = $this->getPsr7RequestHeader();
252
253
        return [
254
            'method' => $this->method,
255
            'endpoint' => Arr::first($request->getHeader('base_uri')) . $request->getUri()->getPath(),
256
            'request' => array_merge($this->configs, $this->options),
257
            'response' => null,
258
        ];
259
    }
260
261
    /**
262
     * @param ConnectorContract $connector
263
     * @return array
264
     */
265 1
    protected function connectorConfigurable(ConnectorContract $connector):array
266
    {
267 1
        $this->implementWebHookConnector($connector);
268
269
        $configs = $this->webHookConfigurable($connector->webHookConfiguration());
270
271 1
        $configs[RequestOptions::HEADERS] = array_merge(
272
            Arr::get($this->configs, RequestOptions::HEADERS, []),
273
            $connector->webHookHeader(),
274
            ['Content-type' => $this->mapContentType($connector->webHookContentType())]
275
        );
276
277
        $this->options[$connector->webHookContentType()] = $connector->webHookContent();
278 1
279
        return $configs;
280 1
    }
281
}
282