Test Setup Failed
Push — master ( d6bd2d...bc363c )
by Alexey
03:03
created

UserApi::getAvatarUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Skobkin\Bundle\PointToolsBundle\Service;
4
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\EntityRepository;
7
use GuzzleHttp\ClientInterface;
8
use GuzzleHttp\Exception\RequestException;
9
use JMS\Serializer\Serializer;
10
use Psr\Log\LoggerInterface;
11
use Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth;
12
use Skobkin\Bundle\PointToolsBundle\Entity\User;
13
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException;
14
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
15
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\UserNotFoundException;
16
use Symfony\Component\HttpFoundation\Response;
17
18
/**
19
 * Basic Point.im user API functions from /api/user/*
20
 */
21
class UserApi extends AbstractApi
22
{
23
    /**
24
     * @var EntityManager
25
     */
26
    protected $em;
27
28
    /**
29
     * @var EntityRepository
30
     */
31
    protected $userRepository;
32
33
    /**
34
     * @var Serializer
35
     */
36
    private $serializer;
37
38
    public function __construct(ClientInterface $httpClient, LoggerInterface $logger, EntityManager $entityManager, Serializer $serializer)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
39
    {
40
        parent::__construct($httpClient, $logger);
41
42
        $this->em = $entityManager;
43
        $this->serializer = $serializer;
44
        // @todo refactor
45
        $this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User');
46
    }
47 12
48
    public function isAuthDataValid(string $login, string $password): bool
49 12
    {
50
        $this->logger->info('Checking user auth data via point.im API');
51 12
52 12
        $auth = $this->authenticate($login, $password);
53 12
54 12
        if (null === $auth->getError() && null !== $auth->getToken()) {
55
            $this->logger->debug('Authentication successfull. Logging out.');
56
57
            $this->logout($auth);
58
59
            return true;
60
        }
61
62
        return false;
63
    }
64
65
    public function authenticate(string $login, string $password): Auth
66
    {
67
        $this->logger->debug('Trying to authenticate user via Point.im API', ['login' => $login]);
68
69
        try {
70
            $authData = $this->getPostRequestData(
71
                '/api/login',
72
                [
73
                    'login' => $login,
74
                    'password' => $password,
75
                ]
76
            );
77
78
            return $this->serializer->deserialize($authData, Auth::class, 'json');
0 ignored issues
show
Bug introduced by
It seems like $authData defined by $this->getPostRequestDat...assword' => $password)) on line 70 can also be of type array or object; however, JMS\Serializer\Serializer::deserialize() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
79
        } catch (RequestException $e) {
80
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
81
                throw new InvalidResponseException('API method not found', 0, $e);
82
            } else {
83
                throw $e;
84
            }
85
        }
86
    }
87
88
    public function logout(Auth $auth): bool
89
    {
90
        $this->logger->debug('Trying to log user out via Point.im API');
91
92
        try {
93
            $this->getPostRequestData('/api/logout', ['csrf_token' => $auth->getCsRfToken()]);
94
95
            return true;
96
        } catch (RequestException $e) {
97
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
98
                throw new InvalidResponseException('API method not found', 0, $e);
99
            } elseif (Response::HTTP_FORBIDDEN === $e->getResponse()->getStatusCode()) {
100
                return true;
101
            } else {
102
                throw $e;
103
            }
104
        }
105
    }
106
107
    /**
108
     * Get user subscribers by user login
109
     *
110
     * @param string $login
111
     *
112
     * @return User[]
113
     *
114
     * @throws ApiException
115
     * @throws InvalidResponseException
116
     * @throws UserNotFoundException
117
     */
118 View Code Duplication
    public function getUserSubscribersByLogin(string $login): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
    {
120
        $this->logger->debug('Trying to get user subscribers by login', ['login' => $login]);
121
122
        try {
123
            $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscribers', [], true);
124
        } catch (RequestException $e) {
125
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
126
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
127
            } else {
128
                throw $e;
129
            }
130
        }
131
132
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ribers', array(), true) on line 123 can also be of type object or string; however, Skobkin\Bundle\PointTool...Api::getUsersFromList() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
133
    }
134
135
    /**
136
     * Get user subscribers by user id
137
     *
138
     * @param int $id
139
     *
140
     * @return User[]
141
     *
142
     * @throws ApiException
143
     * @throws InvalidResponseException
144
     * @throws UserNotFoundException
145
     */
146 View Code Duplication
    public function getUserSubscribersById(int $id): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
    {
148
        $this->logger->debug('Trying to get user subscribers by id', ['id' => $id]);
149
150
        try {
151
            $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscribers', [], true);
152
        } catch (RequestException $e) {
153
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
154
                throw new UserNotFoundException('User not found', 0, $e, $id);
155
            } else {
156
                throw $e;
157
            }
158
        }
159
160
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ribers', array(), true) on line 151 can also be of type object or string; however, Skobkin\Bundle\PointTool...Api::getUsersFromList() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
161
    }
162
163
    /**
164
     * Get user subscriptions by user login
165
     *
166
     * @param string $login
167
     *
168
     * @return User[]
169
     *
170
     * @throws ApiException
171
     * @throws InvalidResponseException
172
     * @throws UserNotFoundException
173
     */
174 View Code Duplication
    public function getUserSubscriptionsByLogin(string $login): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
    {
176
        $this->logger->debug('Trying to get user subscriptions by login', ['login' => $login]);
177
178
        try {
179
            $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscriptions', [], true);
180
        } catch (RequestException $e) {
181
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
182
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
183
            } else {
184
                throw $e;
185
            }
186
        }
187
188
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ptions', array(), true) on line 179 can also be of type object or string; however, Skobkin\Bundle\PointTool...Api::getUsersFromList() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
189
    }
190
191
    /**
192
     * Get user subscriptions by user id
193
     *
194
     * @param int $id
195
     *
196
     * @return User[]
197
     *
198
     * @throws ApiException
199
     * @throws InvalidResponseException
200
     * @throws UserNotFoundException
201
     */
202 View Code Duplication
    public function getUserSubscriptionsById(int $id): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
    {
204
        $this->logger->debug('Trying to get user subscriptions by id', ['id' => $id]);
205
206
        try {
207
            $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscriptions', [], true);
208
        } catch (RequestException $e) {
209
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
210
                throw new UserNotFoundException('User not found', 0, $e, $id);
211
            } else {
212
                throw $e;
213
            }
214
        }
215
216
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ptions', array(), true) on line 207 can also be of type object or string; however, Skobkin\Bundle\PointTool...Api::getUsersFromList() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
217
    }
218
219
    /**
220
     * Get single user by login
221
     *
222
     * @param string $login
223
     *
224
     * @return User
225
     *
226
     * @throws UserNotFoundException
227
     * @throws RequestException
228
     */
229 View Code Duplication
    public function getUserByLogin(string $login): User
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
    {
231
        $this->logger->debug('Trying to get user by login', ['login' => $login]);
232
233
        try {
234
            $userInfo = $this->getGetRequestData('/api/user/login/'.urlencode($login), [], true);
235
        } catch (RequestException $e) {
236
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
237
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
238
            } else {
239
                throw $e;
240
            }
241
        }
242
243
        return $this->getUserFromUserInfo($userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo defined by $this->getGetRequestData...$login), array(), true) on line 234 can also be of type object or string; however, Skobkin\Bundle\PointTool...::getUserFromUserInfo() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
244
    }
245
246
    /**
247
     * Get single user by id
248
     *
249
     * @param int $id
250
     *
251
     * @return User
252
     *
253
     * @throws UserNotFoundException
254
     * @throws RequestException
255
     */
256 View Code Duplication
    public function getUserById(int $id): User
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258
        $this->logger->debug('Trying to get user by id', ['id' => $id]);
259
260
        try {
261
            $userInfo = $this->getGetRequestData('/api/user/id/'.$id, [], true);
262
        } catch (RequestException $e) {
263
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
264
                throw new UserNotFoundException('User not found', 0, $e, $id);
265
            } else {
266
                throw $e;
267
            }
268
        }
269
270
        return $this->getUserFromUserInfo($userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo defined by $this->getGetRequestData...' . $id, array(), true) on line 261 can also be of type object or string; however, Skobkin\Bundle\PointTool...::getUserFromUserInfo() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
271
    }
272
273
    /**
274
     * Finds and updates or create new user from API response data
275
     *
276
     * @param array $userInfo
277
     *
278
     * @return User
279
     *
280
     * @throws ApiException
281
     * @throws InvalidResponseException
282
     */
283
    public function getUserFromUserInfo(array $userInfo): User
284
    {
285
        $this->logger->debug('Trying to create user from array', ['array' => $userInfo]);
286
287
        // @todo Refactor to UserFactory->createFromArray()
288
        if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
289
            /** @var User $user */
290 View Code Duplication
            if (null === ($user = $this->userRepository->find($userInfo['id']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
                // Creating new user
292
                $user = new User($userInfo['id']);
293
                $this->em->persist($user);
294
            }
295
296
            // Updating data
297
            $user
298
                ->setLogin($userInfo['login'])
299
                ->setName($userInfo['name'])
300
            ;
301
302
            return $user;
303
        }
304
305
        throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
306
    }
307
308
    /**
309
     * Get array of User objects from API response containing user list
310
     *
311
     * @todo refactor
312
     *
313
     * @param array $users
314
     *
315
     * @return User[]
316
     *
317
     * @throws ApiException
318
     * @throws InvalidResponseException
319
     */
320
    private function getUsersFromList(array $users = []): array
321
    {
322
        $this->logger->debug('Trying to create multiple users from array', ['array' => $users]);
323
324
        /** @var User[] $resultUsers */
325
        $resultUsers = [];
326
327
        foreach ($users as $userInfo) {
328
            if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
329
330
                // @todo Optimize with prehashed id's list
331 View Code Duplication
                if (null === ($user = $this->userRepository->find($userInfo['id']))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
332
                    $user = new User((int) $userInfo['id']);
333
                    $this->em->persist($user);
334
                }
335
336
                // Updating data
337
                $user
338
                    ->setLogin($userInfo['login'])
339
                    ->setName($userInfo['name'])
340
                ;
341
342
                $resultUsers[] = $user;
343
            } else {
344
                throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
345
            }
346
        }
347
348
        return $resultUsers;
349
    }
350
}
351