Passed
Push — master ( 879438...c3e810 )
by Jasper
02:13
created

HttpClient   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Test Coverage

Coverage 82.93%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 17
eloc 54
c 2
b 0
f 0
dl 0
loc 169
ccs 34
cts 41
cp 0.8293
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setTokenUrl() 0 5 1
A authenticate() 0 7 1
A request() 0 6 1
A fetchToken() 0 19 2
A __construct() 0 12 4
A sendRequest() 0 15 3
A getToken() 0 7 2
A setBaseUrl() 0 5 1
A createRequest() 0 10 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Swis\Melvin;
6
7
use Http\Discovery\Psr17FactoryDiscovery;
8
use Http\Discovery\Psr18ClientDiscovery;
9
use Psr\Http\Client\ClientExceptionInterface;
10
use Psr\Http\Client\ClientInterface;
11
use Psr\Http\Message\RequestFactoryInterface;
12
use Psr\Http\Message\RequestInterface;
13
use Psr\Http\Message\StreamFactoryInterface;
14
use Swis\Melvin\Exceptions\AuthenticationException;
15
use Swis\Melvin\Exceptions\RequestException;
16
17
class HttpClient
18
{
19
    private string $baseUrl = 'https://melvin.ndw.nu/melvinservice/rest/';
20
21
    private string $tokenUrl = 'https://keycloak.ndwcloud.nu/auth/realms/ndw/protocol/openid-connect/token';
22
23
    private string $username;
24
25
    private string $password;
26
27
    private string $clientId;
28
29
    private string $token;
30
31
    protected ClientInterface $httpClient;
32
33 4
    protected RequestFactoryInterface $requestFactory;
34
35
    protected StreamFactoryInterface $streamFactory;
36
37
    public function __construct(
38
        string $username,
39
        string $password,
40 4
        string $clientId = 'melvin-frontend-test',
41 4
        ?ClientInterface $httpClient = null,
42 4
        ?RequestFactoryInterface $requestFactory = null,
43 4
        ?StreamFactoryInterface $streamFactory = null
44
    ) {
45
        $this->authenticate($username, $password, $clientId);
46
        $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find();
47
        $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory();
48
        $this->streamFactory = $streamFactory ?: Psr17FactoryDiscovery::findStreamFactory();
49
    }
50
51
    /**
52
     * @param mixed|null $body
53
     *
54
     * @throws \JsonException
55 4
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
56
     * @throws \Swis\Melvin\Exceptions\RequestException
57 4
     *
58 4
     * @return mixed
59
     */
60 4
    public function request(string $method, string $uri, $body = null)
61
    {
62
        $request = $this->createRequest($method, $uri, $body)
63
            ->withHeader('Authorization', sprintf('Bearer %s', $this->getToken()));
64
65
        return $this->sendRequest($request);
66
    }
67
68
    /**
69 4
     * @param string $username
70
     * @param string $password
71 4
     * @param string $clientId
72 4
     *
73
     * @return $this
74 4
     */
75
    public function authenticate(string $username, string $password, string $clientId = 'melvin-frontend-test'): self
76
    {
77
        $this->username = $username;
78
        $this->password = $password;
79
        $this->clientId = $clientId;
80
81
        return $this;
82
    }
83
84
    /**
85
     * @param string $baseUrl
86
     *
87
     * @return $this
88
     */
89
    public function setBaseUrl(string $baseUrl): self
90
    {
91
        $this->baseUrl = $baseUrl;
92
93
        return $this;
94 4
    }
95
96 4
    /**
97
     * @param string $tokenUrl
98 4
     *
99 4
     * @return $this
100 4
     */
101
    public function setTokenUrl(string $tokenUrl): self
102
    {
103 4
        $this->tokenUrl = $tokenUrl;
104
105
        return $this;
106
    }
107
108
    /**
109
     * @param mixed|null $body
110
     *
111
     * @throws \JsonException
112 4
     */
113
    protected function createRequest(string $method, string $uri, $body = null): RequestInterface
114
    {
115 4
        $request = $this->requestFactory->createRequest($method, $this->baseUrl.ltrim($uri, '/'));
116
117
        if ($body !== null) {
118
            $request = $request->withBody($this->streamFactory->createStream(json_encode($body, JSON_THROW_ON_ERROR)))
119
                ->withHeader('Content-Type', 'application/json');
120 4
        }
121
122
        return $request;
123
    }
124 4
125
    /**
126 4
     * @throws \JsonException
127
     * @throws \Swis\Melvin\Exceptions\RequestException
128
     *
129
     * @return mixed
130
     */
131
    protected function sendRequest(RequestInterface $request)
132
    {
133
        try {
134 4
            $response = $this->httpClient->sendRequest($request);
135
        } catch (ClientExceptionInterface $exception) {
136 4
            throw RequestException::create($request, null, $exception);
137 4
        }
138
139
        if ($response->getStatusCode() !== 200) {
140 4
            throw RequestException::create($request, $response);
141
        }
142
143
        $responseBody = (string) $response->getBody();
144
145
        return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR);
146
    }
147
148 4
    /**
149
     * @throws \JsonException
150 4
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
151
     * @throws \Swis\Melvin\Exceptions\RequestException
152
     */
153 4
    protected function getToken(): string
154
    {
155 4
        if (!isset($this->token)) {
156
            $this->token = $this->fetchToken();
157 4
        }
158
159
        return $this->token;
160
    }
161 4
162
    /**
163
     * @throws \JsonException
164
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
165
     * @throws \Swis\Melvin\Exceptions\RequestException
166
     */
167
    protected function fetchToken(): string
168
    {
169
        $body = [
170
            'client_id' => $this->clientId,
171
            'grant_type' => 'password',
172
            'username' => $this->username,
173
            'password' => $this->password,
174
        ];
175
        $request = $this->requestFactory->createRequest('POST', $this->tokenUrl)
176
            ->withHeader('Content-Type', 'application/x-www-form-urlencoded')
177
            ->withBody($this->streamFactory->createStream(http_build_query($body)));
178
179
        try {
180
            $result = $this->sendRequest($request);
181
        } catch (RequestException $exception) {
182
            throw new AuthenticationException('Failed to authenticate', 0, $exception);
183
        }
184
185
        return $result->access_token;
186
    }
187
}
188