HttpClient::authenticate()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
cc 1
nc 1
nop 3
crap 1
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://iam.ndw.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
    protected RequestFactoryInterface $requestFactory;
34
35
    protected StreamFactoryInterface $streamFactory;
36
37 12
    public function __construct(
38
        string $username,
39
        string $password,
40
        string $clientId = 'melvin-frontend-test',
41
        ?ClientInterface $httpClient = null,
42
        ?RequestFactoryInterface $requestFactory = null,
43
        ?StreamFactoryInterface $streamFactory = null
44
    ) {
45 12
        $this->authenticate($username, $password, $clientId);
46 12
        $this->httpClient = $httpClient ?: Psr18ClientDiscovery::find();
47 12
        $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory();
48 12
        $this->streamFactory = $streamFactory ?: Psr17FactoryDiscovery::findStreamFactory();
49 6
    }
50
51
    /**
52
     * @param mixed|null $body
53
     *
54
     * @throws \JsonException
55
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
56
     * @throws \Swis\Melvin\Exceptions\RequestException
57
     *
58
     * @return mixed
59
     */
60 12
    public function request(string $method, string $uri, $body = null)
61
    {
62 12
        $request = $this->createRequest($method, $uri, $body)
63 12
            ->withHeader('Authorization', sprintf('Bearer %s', $this->getToken()));
64
65 12
        return $this->sendRequest($request);
66
    }
67
68
    /**
69
     * @param string $username
70
     * @param string $password
71
     * @param string $clientId
72
     *
73
     * @return $this
74
     */
75 12
    public function authenticate(string $username, string $password, string $clientId = 'melvin-frontend-test'): self
76
    {
77 12
        $this->username = $username;
78 12
        $this->password = $password;
79 12
        $this->clientId = $clientId;
80
81 12
        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
    }
95
96
    /**
97
     * @param string $tokenUrl
98
     *
99
     * @return $this
100
     */
101
    public function setTokenUrl(string $tokenUrl): self
102
    {
103
        $this->tokenUrl = $tokenUrl;
104
105
        return $this;
106
    }
107
108
    /**
109
     * @param mixed|null $body
110
     *
111
     * @throws \JsonException
112
     */
113 12
    protected function createRequest(string $method, string $uri, $body = null): RequestInterface
114
    {
115 12
        $request = $this->requestFactory->createRequest($method, $this->baseUrl.ltrim($uri, '/'))
116 12
            ->withHeader('Accept', 'application/json');
117
118 12
        if ($body !== null) {
119
            $request = $request->withBody($this->streamFactory->createStream(json_encode($body, JSON_THROW_ON_ERROR)))
120
                ->withHeader('Content-Type', 'application/json');
121
        }
122
123 12
        return $request;
124
    }
125
126
    /**
127
     * @throws \JsonException
128
     * @throws \Swis\Melvin\Exceptions\RequestException
129
     *
130
     * @return mixed
131
     */
132 12
    protected function sendRequest(RequestInterface $request)
133
    {
134
        try {
135 12
            $response = $this->httpClient->sendRequest($request);
136
        } catch (ClientExceptionInterface $exception) {
137
            throw RequestException::create($request, null, $exception);
138
        }
139
140 12
        if ($response->getStatusCode() !== 200) {
141
            throw RequestException::create($request, $response);
142
        }
143
144 12
        $responseBody = (string) $response->getBody();
145
146 12
        return json_decode($responseBody, false, 512, JSON_THROW_ON_ERROR);
147
    }
148
149
    /**
150
     * @throws \JsonException
151
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
152
     * @throws \Swis\Melvin\Exceptions\RequestException
153
     */
154 12
    protected function getToken(): string
155
    {
156 12
        if (!isset($this->token)) {
157 12
            $this->token = $this->fetchToken();
158
        }
159
160 12
        return $this->token;
161
    }
162
163
    /**
164
     * @throws \JsonException
165
     * @throws \Swis\Melvin\Exceptions\AuthenticationException
166
     * @throws \Swis\Melvin\Exceptions\RequestException
167
     */
168 12
    protected function fetchToken(): string
169
    {
170 12
        $body = [
171 12
            'client_id' => $this->clientId,
172 12
            'grant_type' => 'password',
173 12
            'username' => $this->username,
174 12
            'password' => $this->password,
175 6
        ];
176 12
        $request = $this->requestFactory->createRequest('POST', $this->tokenUrl)
177 12
            ->withHeader('Content-Type', 'application/x-www-form-urlencoded')
178 12
            ->withBody($this->streamFactory->createStream(http_build_query($body)));
179
180
        try {
181 12
            $result = $this->sendRequest($request);
182
        } catch (RequestException $exception) {
183
            throw new AuthenticationException('Failed to authenticate', 0, $exception);
184
        }
185
186 12
        return $result->access_token;
187
    }
188
}
189