Completed
Pull Request — master (#6)
by
unknown
04:05
created

SalesForceClient::getHeaderJsonAndAuthorization()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Akeneo\SalesForce\Connector;
4
5
use Akeneo\SalesForce\Authentification\AccessToken;
6
use Akeneo\SalesForce\Authentification\AccessTokenGenerator;
7
use Akeneo\SalesForce\Exception\AuthenticationException;
8
use GuzzleHttp\Client as GuzzleClient;
9
use GuzzleHttp\Exception\RequestException;
10
11
/**
12
 * @author Anael Chardan <[email protected]>
13
 */
14
class SalesForceClient
15
{
16
    const BASE_API_URL   = '/services/data/v37.0/sobjects';
17
    const BASE_QUERY_URL = '/services/data/v37.0/query';
18
19
    /**
20
     * @var string
21
     */
22
    protected $salesforceLoginUrl;
23
24
    /**
25
     * @var string
26
     */
27
    protected $clientId;
28
29
    /**
30
     * @var string
31
     */
32
    protected $clientSecret;
33
34
    /**
35
     * @var GuzzleClient
36
     */
37
    protected $clientGuzzle;
38
39
    /**
40
     * @var string
41
     */
42
    protected $username;
43
44
    /**
45
     * @var string
46
     */
47
    protected $password;
48
49
    /**
50
     * @var AccessTokenGenerator
51
     */
52
    protected $accessTokenGenerator;
53
54
    /**
55
     * @var AccessToken
56
     */
57
    protected $salesForceAccessToken;
58
59
    /**
60
     * @var string
61
     */
62
    protected $baseUrl;
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function __construct(
68
        string $username,
69
        string $password,
70
        string $clientId,
71
        string $clientSecret,
72
        string $salesForceLoginUrl,
73
        GuzzleClient $guzzleClient,
74
        AccessTokenGenerator $accessTokenGenerator
75
    ) {
76
        $this->username             = $username;
77
        $this->password             = $password;
78
        $this->clientId             = $clientId;
79
        $this->clientSecret         = $clientSecret;
80
        $this->salesforceLoginUrl   = $salesForceLoginUrl;
81
        $this->clientGuzzle         = $guzzleClient;
82
        $this->accessTokenGenerator = $accessTokenGenerator;
83
    }
84
85
    public function search($query = null, $nextUrl = null)
86
    {
87
        $url = !empty($nextUrl) ?
88
            sprintf('%s/%s', $this->getBaseUrl(), $nextUrl) :
89
            sprintf(
90
                '%s%s/?q=%s',
91
                $this->getBaseUrl(),
92
                static::BASE_QUERY_URL,
93
                urlencode($query)
94
            )
95
        ;
96
97
        $response = $this->request(HttpWords::GET, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
98
        $data     = json_decode($response->getBody(), true);
99
100
        $results = $data['records'];
101
102
        if (!$data['done']) {
103
            $more_results = $this->search(null, substr($data['nextRecordsUrl'], 1));
104
            if (!empty($more_results)) {
105
                $results = array_merge($results, $more_results);
106
            }
107
        }
108
109
        return $results;
110
    }
111
112
    public function getAllRessources()
113
    {
114
        $url      = sprintf('%s%s', $this->getBaseUrl(), static::BASE_API_URL);
115
        $response = $this->request(HttpWords::GET, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
116
117
        return json_decode($response->getBody(), true);
118
    }
119
120 View Code Duplication
    public function getBasicInformation(string $objectName)
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...
121
    {
122
        $url      = sprintf('%s%s/%s', $this->getBaseUrl(), static::BASE_API_URL, $objectName);
123
        $response = $this->request(HttpWords::GET, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
124
125
        return json_decode($response->getBody(), true);
126
    }
127
128 View Code Duplication
    public function getDescribedObject(string $objectName)
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...
129
    {
130
        $url      = sprintf(
131
            '%s%s/%s/describe',
132
            $this->getBaseUrl(),
133
            static::BASE_API_URL,
134
            $objectName
135
        );
136
        $response = $this->request(HttpWords::GET, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137
138
        return json_decode($response->getBody(), true);
139
    }
140
141 View Code Duplication
    public function findById($objectName, $objectId, array $fields = [])
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...
142
    {
143
        $url      = sprintf(
144
            '%s%s/%s/%s?fields=%s',
145
            $this->getBaseUrl(),
146
            static::BASE_API_URL,
147
            $objectName,
148
            $objectId,
149
            implode(',', $fields)
150
        );
151
        $response = $this->request(HttpWords::GET, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
152
153
        return json_decode($response->getBody(), true);
154
    }
155
156 View Code Duplication
    public function insert($objectName, array $data = [])
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...
157
    {
158
        $url      = sprintf('%s%s/%s/', $this->getBaseUrl(), static::BASE_API_URL, $objectName);
159
        $response = $this->request(HttpWords::POST, $url, $this->getHeaderWithAuthorizationAndData($data));
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorizationAndData($data) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
160
161
        return json_decode($response->getBody(), true);
162
    }
163
164 View Code Duplication
    public function upsertByExternalId(
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...
165
        string $objectName,
166
        string $externalIdName,
167
        string $externalIdValue,
168
        array $data = []
169
    ) {
170
        $url      = sprintf(
171
            '%s%s/%s/%s/%s',
172
            $this->getBaseUrl(),
173
            static::BASE_API_URL,
174
            $objectName,
175
            $externalIdName,
176
            $externalIdValue
177
        );
178
        $response = $this->request(HttpWords::PATCH, $url, $this->getHeaderWithAuthorizationAndData($data));
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorizationAndData($data) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
179
180
        return json_decode($response->getBody(), true);
181
    }
182
183 View Code Duplication
    public function update($objectName, $objectId, $data = [])
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...
184
    {
185
        $url      = sprintf(
186
            '%s%s/%s/%s',
187
            $this->getBaseUrl(),
188
            static::BASE_API_URL,
189
            $objectName,
190
            $objectId
191
        );
192
        $response = $this->request(HttpWords::PATCH, $url, $this->getHeaderWithAuthorizationAndData($data));
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorizationAndData($data) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
193
194
        return json_decode($response->getBody(), true);
195
    }
196
197
    public function delete($objectName, $objectId)
198
    {
199
        $url = sprintf(
200
            '%s%s/%s/%s',
201
            $this->getBaseUrl(),
202
            static::BASE_API_URL,
203
            $objectName,
204
            $objectId
205
        );
206
207
        $this->request(HttpWords::DELETE, $url, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
208
209
        return true;
210
    }
211
212
    protected function getBaseUrl()
213
    {
214
        if ($this->baseUrl == null) {
215
            $this->setupToken();
216
        }
217
218
        return $this->baseUrl;
219
    }
220
221
    /**
222
     * @param AccessToken $accessToken
223
     */
224
    public function setAccessToken(AccessToken $accessToken)
225
    {
226
        $this->salesForceAccessToken = $accessToken;
227
        $this->baseUrl               = $accessToken->getApiUrl();
228
    }
229
230
    protected function getHeaderWithAuthorizationAndData(array $data = [])
231
    {
232
        return [
233
            HttpWords::HEADERS => $this->getHeaderJsonAndAuthorization(),
234
            HttpWords::BODY    => json_encode($data),
235
        ];
236
    }
237
238
    protected function getHeaderWithAuthorization()
239
    {
240
        return [HttpWords::HEADERS => $this->getAuthorizationField()];
241
    }
242
243
    protected function getHeaderJsonAndAuthorization()
244
    {
245
        return array_merge([HttpWords::CONTENT_TYPE => HttpWords::APPLICATION_JSON], $this->getAuthorizationField());
246
    }
247
248
    protected function getAuthorizationField()
249
    {
250
        $this->setupToken();
251
252
        $value = sprintf('%s %s', HttpWords::BEARER, $this->salesForceAccessToken->getAccessToken());
253
254
        return [HttpWords::AUTHORIZATION => $value];
255
    }
256
257
    /**
258
     * Refresh an existing access token.
259
     *
260
     * @throws \Exception
261
     *
262
     * @return AccessToken
263
     */
264
    protected function refreshToken()
265
    {
266
        $url = sprintf(
267
            '%s%s',
268
            $this->salesforceLoginUrl,
269
            SalesForceWords::TOKEN_ENDPOINT
270
        );
271
272
        $postData = [
273
            SalesForceWords::GRANT_TYPE    => SalesForceWords::REFRESH_TOKEN,
274
            SalesForceWords::CLIENT_ID     => $this->clientId,
275
            SalesForceWords::CLIENT_SECRET => $this->clientSecret,
276
            SalesForceWords::REFRESH_TOKEN => $this->salesForceAccessToken->getRefreshToken(),
277
        ];
278
279
        $response = $this->request(HttpWords::POST, $url, [SalesForceWords::FORM_PARAMS => $postData]);
0 ignored issues
show
Documentation introduced by
array(\Akeneo\SalesForce...RM_PARAMS => $postData) is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
280
281
        $update = json_decode($response->getBody(), true);
282
        $this->salesForceAccessToken->updateFromSalesforceRefresh($update);
283
        $this->baseUrl = $this->salesForceAccessToken->getApiUrl();
284
285
        return $this->salesForceAccessToken;
286
    }
287
288
    /**
289
     * @return AccessToken
290
     */
291
    protected function setupToken()
292
    {
293
        if ($this->salesForceAccessToken === null) {
294
            $this->authenticate();
295
296
            return;
297
        }
298
299
        if ($this->salesForceAccessToken->needsRefresh()) {
300
            $this->salesForceAccessToken = $this->refreshToken();
301
        }
302
    }
303
304
    protected function authenticate()
305
    {
306
        $url = sprintf(
307
            '%s%s',
308
            $this->salesforceLoginUrl,
309
            SalesForceWords::TOKEN_ENDPOINT
310
        );
311
312
        $postData = [
313
            SalesForceWords::GRANT_TYPE    => SalesForceWords::PASSWORD,
314
            SalesForceWords::CLIENT_ID     => $this->clientId,
315
            SalesForceWords::CLIENT_SECRET => $this->clientSecret,
316
            SalesForceWords::USERNAME      => $this->username,
317
            SalesForceWords::PASSWORD      => $this->password,
318
        ];
319
320
        $response = $this->request(HttpWords::POST, $url, [SalesForceWords::FORM_PARAMS => $postData]);
0 ignored issues
show
Documentation introduced by
array(\Akeneo\SalesForce...RM_PARAMS => $postData) is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
321
322
        $this->salesForceAccessToken = $this
323
            ->accessTokenGenerator
324
            ->createFromSalesforceResponse(json_decode($response->getBody(), true))
325
        ;
326
327
        $this->baseUrl = $this->salesForceAccessToken->getApiUrl();
328
    }
329
330
    /**
331
     * @param string $imageUrl
332
     *
333
     * @return mixed
334
     * @throws AuthenticationException
335
     * @throws \Exception
336
     */
337
    public function downloadImageFromUrl(string $imageUrl)
338
    {
339
        return $this->request(HttpWords::GET, $imageUrl, $this->getHeaderWithAuthorization());
0 ignored issues
show
Documentation introduced by
$this->getHeaderWithAuthorization() is of type array<string|integer,arr...string|integer,string>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
340
    }
341
342
343
    /**
344
     * @param string $method
345
     * @param string $url
346
     * @param string $data
347
     *
348
     * @throws AuthenticationException
349
     * @throws \Exception
350
     *
351
     * @return mixed
352
     */
353
    protected function request($method, $url, $data)
354
    {
355
        try {
356
            $response = $this->clientGuzzle->$method($url, $data);
357
358
            return $response;
359
        } catch (RequestException $e) {
360
            if ($e->getResponse() === null) {
361
                throw $e;
362
            }
363
364
            $error = json_decode($e->getResponse()->getBody(), true);
365
366
            //If its an auth error convert to an auth exception
367
            if ($e->getResponse()->getStatusCode() == 401) {
368
                throw new AuthenticationException($error[0]['errorCode'], $error[0]['message']);
369
            }
370
371
            //Invalid data sent to salesforce
372
            if ($e->getResponse()->getStatusCode() == 400 && $error[0]['errorCode'] == 'DUPLICATES_DETECTED') {
373
                throw new DuplicateDetectedException($error, $e->getRequest()->getUri());
374
            }
375
376
            throw new \Exception(json_encode([$e->getMessage(), (string) $e->getResponse()->getBody()]));
377
        }
378
    }
379
}
380