Passed
Pull Request — master (#167)
by
unknown
02:36
created

GoogleDriveApp::fetchTokens()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 8
nop 3
dl 0
loc 22
rs 9.5555
c 0
b 0
f 0
1
<?php
2
3
namespace Quantum\Libraries\Storage\Adapters\GoogleDrive;
4
5
use Quantum\Libraries\Curl\HttpClient;
6
use Quantum\Http\Response;
7
use Exception;
8
9
class GoogleDriveApp
10
{
11
    /**
12
     * Authorization URL
13
     */
14
    const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
15
16
    /**
17
     * Authorization scope
18
     */
19
    const AUTH_SCOPE = 'https://www.googleapis.com/auth/drive';
20
21
    /**
22
     * Token URL
23
     */
24
    const AUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token';
25
26
    /**
27
     * URL for file metadata operations
28
     */
29
    const FILE_METADATA_URL = 'https://www.googleapis.com/drive/v3/files';
30
31
    /**
32
     * URL for file media operations
33
     */
34
    const FILE_MEDIA_URL = 'https://www.googleapis.com/upload/drive/v3/files';
35
36
    /**
37
     * Folder mimetype
38
     */
39
    const FOLDER_MIMETYPE = 'application/vnd.google-apps.folder';
40
41
    /**
42
     * Kind/Type  of drive file
43
     */
44
    const DRIVE_FILE_KIND = 'drive#file';
45
46
    /**
47
     * Error code for invalid token
48
     */
49
    const INVALID_TOKEN_ERROR_CODE = 401;
50
51
    /**
52
     * @var HttpClient
53
     */
54
    private $httpClient;
55
56
    /**
57
     * @var string
58
     */
59
    private $appKey = null;
60
61
    /**
62
     * @var string
63
     */
64
    private $appSecret = null;
65
66
    /**
67
     * @var TokenServiceInterface
68
     */
69
    private $tokenService = null;
70
71
    /**
72
     * GoogleDriveApp constructor
73
     * @param string $appKey
74
     * @param string $appSecret
75
     * @param TokenServiceInterface $tokenService
76
     * @param HttpClient $httpClient
77
     */
78
    public function __construct(string $appKey, string $appSecret, TokenServiceInterface $tokenService, HttpClient $httpClient)
79
    {
80
        $this->appKey = $appKey;
81
        $this->appSecret = $appSecret;
82
        $this->tokenService = $tokenService;
83
        $this->httpClient = $httpClient;
84
    }
85
86
    public function getAuthUrl($redirectUrl, $accessType = "offline"): string
87
    {
88
        $params = [
89
            'client_id' => $this->appKey,
90
            'response_type' => 'code',
91
            'state' => csrf_token(),
92
            'scope' => self::AUTH_SCOPE,
93
            'redirect_uri' => $redirectUrl,
94
            'access_type' => $accessType,
95
        ];
96
97
        return self::AUTH_URL . '?' . http_build_query($params, '', '&');
98
    }
99
100
    public function fetchTokens($code, $redirectUrl = '', $byRefresh = false): ?object
101
    {
102
        $codeKey = $byRefresh ? 'refresh_token' : 'code';
103
104
        $params = [
105
            $codeKey => $code,
106
            'grant_type' => $byRefresh ? 'refresh_token' : 'authorization_code',
107
            'client_id' => $this->appKey,
108
            'client_secret' => $this->appSecret,
109
        ];
110
111
        if(!$byRefresh){
112
            $params['redirect_uri'] = $redirectUrl;
113
        }
114
115
        $tokenUrl = self::AUTH_TOKEN_URL;
116
117
        $response = $this->sendRequest($tokenUrl, $params);
118
119
        $this->tokenService->saveTokens($response->access_token, !$byRefresh ? $response->refresh_token : null);
120
121
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response could return the type mixed which is incompatible with the type-hinted return null|object. Consider adding an additional type-check to rule them out.
Loading history...
122
    }
123
124
    public function sendRequest(string $uri, $data = null, array $headers = [], $method = 'POST')
125
    {
126
        $this->httpClient
127
            ->createRequest($uri)
128
            ->setMethod($method)
129
            ->setData($data)
130
            ->setHeaders($headers)
131
            ->start();
132
133
134
        $errors = $this->httpClient->getErrors();
135
        $responseBody = $this->httpClient->getResponseBody();
136
137
        if ($errors) {
138
            $code = $errors['code'];
139
140
            if ($code == self::INVALID_TOKEN_ERROR_CODE) {
141
                $prevUrl = $this->httpClient->url();
142
                $prevData = $this->httpClient->getData();
143
                $prevHeaders = $this->httpClient->getRequestHeaders();
144
145
                $refreshToken = $this->tokenService->getRefreshToken();
146
147
                $response = $this->fetchTokens($refreshToken , '', true);
148
149
                $prevHeaders['Authorization'] = 'Bearer ' . $response->access_token;
150
151
                $responseBody = $this->sendRequest($prevUrl, $prevData, $prevHeaders);
152
153
            } else {
154
                throw new Exception(json_encode($responseBody ?? $errors), E_ERROR);
155
            }
156
        }
157
158
        return $responseBody;
159
    }
160
161
    /**
162
     * Sends rpc request
163
     * @param string $url
164
     * @param mixed $params
165
     * @param string $method
166
     * @param string $contentType
167
     * @return mixed|null
168
     * @throws Exception
169
     */
170
    public function rpcRequest(string $url, $params = [], $method = 'POST', $contentType = 'application/json')
171
    {
172
        try {
173
            $headers = [
174
                'Authorization' => 'Bearer ' . $this->tokenService->getAccessToken(),
175
                'Content-Type' => $contentType
176
            ];
177
            return $this->sendRequest($url, $params, $headers, $method);
178
        }catch (Exception $e){
179
            throw new Exception($e->getMessage(), (int)$e->getCode());
180
        }
181
    }
182
183
    /**
184
     * Gets file information
185
     * @param string $fileId
186
     * @param bool $media
187
     * @param mixed $params
188
     * @return mixed|null
189
     * @throws Exception
190
     */
191
    public function getFileInfo(string $fileId, $media = false, $params = []){
192
        $queryParam = $media ? '?alt=media' : '?fields=*';
193
        return $this->rpcRequest(GoogleDriveApp::FILE_METADATA_URL . '/' . $fileId . $queryParam, $params, 'GET');
194
    }
195
}