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

GoogleDriveApp::rpcRequest()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

201
            return $this->sendRequest($url, /** @scrutinizer ignore-type */ $params, $headers, $method);
Loading history...
202
        }catch (Exception $e){
203
            throw new Exception($e->getMessage(), (int)$e->getCode());
204
        }
205
    }
206
207
    /**
208
     * Gets file information
209
     * @param string $fileId
210
     * @param bool $media
211
     * @param array $params
212
     * @return mixed|null
213
     * @throws Exception
214
     */
215
    public function getFileInfo(string $fileId, bool $media = false, array $params = []){
216
        $queryParam = $media ? '?alt=media' : '?fields=*';
217
        return $this->rpcRequest(GoogleDriveApp::FILE_METADATA_URL . '/' . $fileId . $queryParam, $params, 'GET');
218
    }
219
}