Test Failed
Push — master ( 997f41...cf0056 )
by Alexey
03:26
created

UserApi::getUserFromUserInfo()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 22
Code Lines 10

Duplication

Lines 5
Ratio 22.73 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 5
loc 22
ccs 0
cts 9
cp 0
rs 8.6737
c 0
b 0
f 0
cc 6
eloc 10
nc 3
nop 1
crap 42
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 Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth;
11
use Skobkin\Bundle\PointToolsBundle\Entity\User;
12
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException;
13
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
14
use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\UserNotFoundException;
15
use Symfony\Component\HttpFoundation\Response;
16
17
/**
18
 * Basic Point.im user API functions from /api/user/*
19
 */
20
class UserApi extends AbstractApi
21
{
22
    const AVATAR_SIZE_SMALL = '24';
23
    const AVATAR_SIZE_MEDIUM = '40';
24
    const AVATAR_SIZE_LARGE = '80';
25
26
    /**
27
     * @var string Base URL for user avatars
28
     */
29
    protected $avatarsBaseUrl = '//point.im/avatar/';
30
31
    /**
32
     * @var EntityManager
33
     */
34
    protected $em;
35
36
    /**
37
     * @var EntityRepository
38
     */
39
    protected $userRepository;
40
41
    /**
42
     * @var Serializer
43
     */
44
    private $serializer;
45
46
47 12
    public function __construct(ClientInterface $httpClient, 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...
48
    {
49 12
        parent::__construct($httpClient);
50
51 12
        $this->em = $entityManager;
52 12
        $this->serializer = $serializer;
53 12
        $this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User');
54 12
    }
55
56
    public function isAuthDataValid(string $login, string $password): bool
57
    {
58
        $auth = $this->authenticate($login, $password);
59
60
        if (null === $auth->getError() && null !== $auth->getToken()) {
61
            $this->logout($auth);
62
63
            return true;
64
        }
65
66
        return false;
67
    }
68
69
    public function authenticate(string $login, string $password): Auth
70
    {
71
        try {
72
            $authData = $this->getPostRequestData(
73
                '/api/login',
74
                [
75
                    'login' => $login,
76
                    'password' => $password,
77
                ]
78
            );
79
80
            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 72 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...
81
        } catch (RequestException $e) {
82
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
83
                throw new InvalidResponseException('API method not found', 0, $e);
84
            } else {
85
                throw $e;
86
            }
87
        }
88
    }
89
90
    public function logout(Auth $auth): bool
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
        try {
121
            $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscribers', [], true);
122
        } catch (RequestException $e) {
123
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
124
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
125
            } else {
126
                throw $e;
127
            }
128
        }
129
130
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ribers', array(), true) on line 121 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...
131
    }
132
133
    /**
134
     * Get user subscribers by user id
135
     *
136
     * @param int $id
137
     *
138
     * @return User[]
139
     *
140
     * @throws ApiException
141
     * @throws InvalidResponseException
142
     * @throws UserNotFoundException
143
     */
144 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...
145
    {
146
        try {
147
            $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscribers', [], true);
148
        } catch (RequestException $e) {
149
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
150
                throw new UserNotFoundException('User not found', 0, $e, $id);
151
            } else {
152
                throw $e;
153
            }
154
        }
155
156
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ribers', array(), true) on line 147 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...
157
    }
158
159
    /**
160
     * Get user subscriptions by user login
161
     *
162
     * @param string $login
163
     *
164
     * @return User[]
165
     *
166
     * @throws ApiException
167
     * @throws InvalidResponseException
168
     * @throws UserNotFoundException
169
     */
170 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...
171
    {
172
        try {
173
            $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscriptions', [], true);
174
        } catch (RequestException $e) {
175
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
176
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
177
            } else {
178
                throw $e;
179
            }
180
        }
181
182
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ptions', array(), true) on line 173 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...
183
    }
184
185
    /**
186
     * Get user subscriptions by user id
187
     *
188
     * @param int $id
189
     *
190
     * @return User[]
191
     *
192
     * @throws ApiException
193
     * @throws InvalidResponseException
194
     * @throws UserNotFoundException
195
     */
196 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...
197
    {
198
        try {
199
            $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscriptions', [], true);
200
        } catch (RequestException $e) {
201
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
202
                throw new UserNotFoundException('User not found', 0, $e, $id);
203
            } else {
204
                throw $e;
205
            }
206
        }
207
208
        return $this->getUsersFromList($usersList);
0 ignored issues
show
Bug introduced by
It seems like $usersList defined by $this->getGetRequestData...ptions', array(), true) on line 199 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...
209
    }
210
211
    /**
212
     * Get single user by login
213
     *
214
     * @param string $login
215
     *
216
     * @return User
217
     *
218
     * @throws UserNotFoundException
219
     * @throws RequestException
220
     */
221 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...
222
    {
223
        try {
224
            $userInfo = $this->getGetRequestData('/api/user/login/'.urlencode($login), [], true);
225
        } catch (RequestException $e) {
226
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
227
                throw new UserNotFoundException('User not found', 0, $e, null, $login);
228
            } else {
229
                throw $e;
230
            }
231
        }
232
233
        return $this->getUserFromUserInfo($userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo defined by $this->getGetRequestData...$login), array(), true) on line 224 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...
234
    }
235
236
    /**
237
     * Get single user by id
238
     *
239
     * @param int $id
240
     *
241
     * @return User
242
     *
243
     * @throws UserNotFoundException
244
     * @throws RequestException
245
     */
246 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...
247
    {
248
        try {
249
            $userInfo = $this->getGetRequestData('/api/user/id/'.$id, [], true);
250
        } catch (RequestException $e) {
251
            if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
252
                throw new UserNotFoundException('User not found', 0, $e, $id);
253
            } else {
254
                throw $e;
255
            }
256
        }
257
258
        return $this->getUserFromUserInfo($userInfo);
0 ignored issues
show
Bug introduced by
It seems like $userInfo defined by $this->getGetRequestData...' . $id, array(), true) on line 249 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...
259
    }
260
261
    /**
262
     * Finds and updates or create new user from API response data
263
     *
264
     * @param array $userInfo
265
     *
266
     * @return User
267
     *
268
     * @throws ApiException
269
     * @throws InvalidResponseException
270
     */
271
    public function getUserFromUserInfo(array $userInfo): User
272
    {
273
        // @todo Refactor to UserFactory->createFromArray()
274
        if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
275
            /** @var User $user */
276 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...
277
                // Creating new user
278
                $user = new User($userInfo['id']);
279
                $this->em->persist($user);
280
            }
281
282
            // Updating data
283
            $user
284
                ->setLogin($userInfo['login'])
285
                ->setName($userInfo['name'])
286
            ;
287
288
            return $user;
289
        }
290
291
        throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
292
    }
293
294
    /**
295
     * Get array of User objects from API response containing user list
296
     *
297
     * @param array $users
298
     *
299
     * @return User[]
300
     *
301
     * @throws ApiException
302
     * @throws InvalidResponseException
303
     */
304
    private function getUsersFromList(array $users = []): array
305
    {
306
        /** @var User[] $resultUsers */
307
        $resultUsers = [];
308
309
        foreach ($users as $userInfo) {
310
            if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
311
312
                // @todo Optimize with prehashed id's list
313 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...
314
                    $user = new User((int) $userInfo['id']);
315
                    $this->em->persist($user);
316
                }
317
318
                // Updating data
319
                $user
320
                    ->setLogin($userInfo['login'])
321
                    ->setName($userInfo['name'])
322
                ;
323
324
                $resultUsers[] = $user;
325
            } else {
326
                throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
327
            }
328
        }
329
330
        return $resultUsers;
331
    }
332
333
    /**
334
     * Creates URL of avatar with specified size by User object
335
     *
336
     * @param User $user
337
     * @param string $size
338
     *
339
     * @return string
340
     */
341
    public function getAvatarUrl(User $user, string $size): string
342
    {
343
        return $this->getAvatarUrlByLogin($user->getLogin(), $size);
344 3
    }
345
346 3
    /**
347
     * Creates URL of avatar with specified size by login string
348
     *
349
     * @param string $login
350
     * @param string $size
351
     *
352
     * @return string
353
     */
354
    public function getAvatarUrlByLogin(string $login, string $size): string
355
    {
356
        if (!in_array($size, [self::AVATAR_SIZE_SMALL, self::AVATAR_SIZE_MEDIUM, self::AVATAR_SIZE_LARGE], true)) {
357 4
            throw new \InvalidArgumentException('Avatar size must be one of restricted variants. See UserApi class AVATAR_SIZE_* constants.');
358
        }
359 4
360
        return $this->avatarsBaseUrl.urlencode($login).'/'.$size;
361
    }
362
}
363