Completed
Pull Request — master (#14)
by
unknown
02:09
created

Client::makeRequest()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 4
nc 4
nop 3
1
<?php namespace Crunch\Salesforce;
2
3
use Crunch\Salesforce\Exceptions\RequestException;
4
use GuzzleHttp\Exception\RequestException as GuzzleRequestException;
5
use Crunch\Salesforce\Exceptions\AuthenticationException;
6
7
class Client
8
{
9
    /**
10
     * @var string
11
     */
12
    protected $salesforceLoginUrl;
13
14
    /**
15
     * @var string
16
     */
17
    protected $clientId;
18
19
    /**
20
     * @var string
21
     */
22
    protected $clientSecret;
23
24
    /**
25
     * @var AccessToken
26
     */
27
    private $accessToken;
28
29
    /**
30
     * @var string
31
     */
32
    private $baseUrl;
33
34
    /**
35
     * @var \GuzzleHttp\Client
36
     */
37
    private $guzzleClient;
38
39
    /**
40
     * @const Salesforce API version
41
     */
42
    const SALESFORCE_API_VERSION = 'v43.0';
43
44
    /**
45
     * Create a sf client using a client config object or an array of params
46
     *
47
     * @param ClientConfigInterface $clientConfig
48
     * @param \GuzzleHttp\Client    $guzzleClient
49
     * @throws \Exception
50
     */
51
    public function __construct(ClientConfigInterface $clientConfig, \GuzzleHttp\Client $guzzleClient)
52
    {
53
        $this->salesforceLoginUrl = $clientConfig->getLoginUrl();
54
        $this->clientId           = $clientConfig->getClientId();
55
        $this->clientSecret       = $clientConfig->getClientSecret();
56
57
        $this->guzzleClient = $guzzleClient;
58
    }
59
60
    /**
61
     * Create an instance of the salesforce client using the passed in config data
62
     *
63
     * @param $salesforceLoginUrl
64
     * @param $clientId
65
     * @param $clientSecret
66
     * @return Client
67
     */
68
    public static function create($salesforceLoginUrl, $clientId, $clientSecret)
69
    {
70
        return new self(new ClientConfig($salesforceLoginUrl, $clientId, $clientSecret), new \GuzzleHttp\Client);
71
    }
72
73
    /**
74
     * Fetch a specific object
75
     *
76
     * @param string $objectType
77
     * @param string $sfId
78
     * @param array  $fields
79
     * @return string
80
     */
81
    public function getRecord($objectType, $sfId, array $fields)
82
    {
83
        $url      = $this->baseUrl . '/services/data/' . self::SALESFORCE_API_VERSION . '/sobjects/' . $objectType . '/' . $sfId . '?fields=' . implode(',', $fields);
84
        $response = $this->makeRequest('get', $url, ['headers' => ['Authorization' => $this->getAuthHeader()]]);
85
86
        return json_decode($response->getBody(), true);
87
    }
88
89
    /**
90
     * Execute an SOQL query and return the result set
91
     * This will loop through large result sets collecting all the data so the query should be limited
92
     *
93
     * @param string|null $query
94
     * @param string|null $next_url
95
     * @return array
96
     * @throws \Exception
97
     */
98
    public function search($query = null, $next_url = null)
99
    {
100
        if ( ! empty($next_url)) {
101
            $url = $this->baseUrl . '/' . $next_url;
102
        } else {
103
            $url = $this->baseUrl . '/services/data/v24.0/query/?q=' . urlencode($query);
104
        }
105
        $response = $this->makeRequest('get', $url, ['headers' => ['Authorization' => $this->getAuthHeader()]]);
106
        $data     = json_decode($response->getBody(), true);
107
108
        $results = $data['records'];
109
        if ( ! $data['done']) {
110
            $more_results = $this->search(null, substr($data['nextRecordsUrl'], 1));
111
            if ( ! empty($more_results)) {
112
                $results = array_merge($results, $more_results);
113
            }
114
        }
115
116
        return $results;
117
    }
118
119
    /**
120
     * Make an update request
121
     *
122
     * @param string $object The object type to update
123
     * @param string $id The ID of the record to update
124
     * @param array  $data The data to put into the record
125
     * @return bool
126
     * @throws \Exception
127
     */
128
    public function updateRecord($object, $id, array $data)
129
    {
130
        $url = $this->baseUrl . '/services/data/' . self::SALESFORCE_API_VERSION . '/sobjects/' . $object . '/' . $id;
131
132
        $this->makeRequest('patch', $url, [
133
            'headers' => ['Content-Type' => 'application/json', 'Authorization' => $this->getAuthHeader()],
134
            'body'    => json_encode($data)
135
        ]);
136
137
        return true;
138
    }
139
140
    /**
141
     * Create a new object in salesforce
142
     *
143
     * @param string $object
144
     * @param array|object $data
145
     * @return string The id of the newly created record
146
     * @throws \Exception
147
     */
148
    public function createRecord($object, $data)
149
    {
150
        $url = $this->baseUrl . '/services/data/' . self::SALESFORCE_API_VERSION . '/sobjects/' . $object . '/';
151
152
        $response     = $this->makeRequest('post', $url, [
153
            'headers' => ['Content-Type' => 'application/json', 'Authorization' => $this->getAuthHeader()],
154
            'body'    => json_encode($data)
155
        ]);
156
        $responseBody = json_decode($response->getBody(), true);
157
158
        return $responseBody['id'];
159
    }
160
161
    /**
162
     * Delete an object with th specified id
163
     *
164
     * @param string $object
165
     * @param string $id
166
     * @return bool
167
     * @throws \Exception
168
     */
169 View Code Duplication
    public function deleteRecord($object, $id)
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...
170
    {
171
        $url = $this->baseUrl . '/services/data/' . self::SALESFORCE_API_VERSION . '/sobjects/' . $object . '/' . $id;
172
173
        $this->makeRequest('delete', $url, ['headers' => ['Authorization' => $this->getAuthHeader()]]);
174
175
        return true;
176
    }
177
178
179
180
    /**
181
     * Delete multiple objects with specified ids
182
     *
183
     * @param $object
184
     * @param $id
185
     * @return bool
186
     * @throws \Exception
187
     */
188 View Code Duplication
    public function bulkDeleteRecords(array $ids)
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...
189
    {
190
        $url = $this->baseUrl . '/services/data/' . self::SALESFORCE_API_VERSION . '/composite/sobjects?ids=' . \implode(',', $ids);
191
192
        $this->makeRequest('delete', $url, ['headers' => ['Authorization' => $this->getAuthHeader()]]);
193
194
        return true;
195
    }
196
197
    /**
198
     * Login with user and password
199
     *
200
     * @param $username
201
     * @param $password
202
     * @return array|mixed
203
     * @throws \Exception
204
     */
205 View Code Duplication
    public function login($username, $password)
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...
206
    {
207
        $url = $this->salesforceLoginUrl . 'services/oauth2/token';
208
209
        $post_data = [
210
            'grant_type'    => 'password',
211
            'client_id'     => $this->clientId,
212
            'client_secret' => $this->clientSecret,
213
            'username'      => $username,
214
            'password'      => $password
215
        ];
216
217
        $response = $this->makeRequest('post', $url, ['form_params' => $post_data]);
218
219
        return json_decode($response->getBody(), true);
220
    }
221
222
    /**
223
     * Complete the oauth process by confirming the code and returning an access token
224
     *
225
     * @param $code
226
     * @param $redirect_url
227
     * @return array|mixed
228
     * @throws \Exception
229
     */
230 View Code Duplication
    public function authorizeConfirm($code, $redirect_url)
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...
231
    {
232
        $url = $this->salesforceLoginUrl . 'services/oauth2/token';
233
234
        $post_data = [
235
            'grant_type'    => 'authorization_code',
236
            'client_id'     => $this->clientId,
237
            'client_secret' => $this->clientSecret,
238
            'code'          => $code,
239
            'redirect_uri'  => $redirect_url
240
        ];
241
242
        $response = $this->makeRequest('post', $url, ['form_params' => $post_data]);
243
244
        return json_decode($response->getBody(), true);
245
    }
246
247
    /**
248
     * Get the url to redirect users to when setting up a salesforce access token
249
     *
250
     * @param $redirectUrl
251
     * @return string
252
     */
253
    public function getLoginUrl($redirectUrl)
254
    {
255
        $params = [
256
            'client_id'     => $this->clientId,
257
            'redirect_uri'  => $redirectUrl,
258
            'response_type' => 'code',
259
            'grant_type'    => 'authorization_code'
260
        ];
261
262
        return $this->salesforceLoginUrl . 'services/oauth2/authorize?' . http_build_query($params);
263
    }
264
265
    /**
266
     * Refresh an existing access token
267
     *
268
     * @return AccessToken
269
     * @throws \Exception
270
     */
271
    public function refreshToken()
272
    {
273
        $url = $this->salesforceLoginUrl . 'services/oauth2/token';
274
275
        $post_data = [
276
            'grant_type'    => 'refresh_token',
277
            'client_id'     => $this->clientId,
278
            'client_secret' => $this->clientSecret,
279
            'refresh_token' => $this->accessToken->getRefreshToken()
280
        ];
281
282
        $response = $this->makeRequest('post', $url, ['form_params' => $post_data]);
283
284
        $update = json_decode($response->getBody(), true);
285
        $this->accessToken->updateFromSalesforceRefresh($update);
286
287
        return $this->accessToken;
288
    }
289
290
    /**
291
     * @param AccessToken $accessToken
292
     */
293
    public function setAccessToken(AccessToken $accessToken)
294
    {
295
        $this->accessToken = $accessToken;
296
        $this->baseUrl     = $accessToken->getApiUrl();
297
    }
298
299
    /**
300
     * @param string $method
301
     * @param string $url
302
     * @param array  $data
303
     * @return mixed
304
     * @throws AuthenticationException
305
     * @throws RequestException
306
     */
307
    private function makeRequest($method, $url, $data)
308
    {
309
        try {
310
            $response = $this->guzzleClient->$method($url, $data);
311
312
            return $response;
313
        } catch (GuzzleRequestException $e) {
314
            
315
            if ($e->getResponse() === null) {
316
        		throw $e;
317
        	}
318
319
            //If its an auth error convert to an auth exception
320
            if ($e->getResponse()->getStatusCode() == 401) {
321
                $error = json_decode($e->getResponse()->getBody(), true);
322
                throw new AuthenticationException($error[0]['errorCode'], $error[0]['message']);
323
            }
324
            throw new RequestException($e->getMessage(), (string)$e->getResponse()->getBody());
325
        }
326
327
    }
328
329
    /**
330
     * @return string
331
     */
332
    private function getAuthHeader()
333
    {
334
        if ($this->accessToken === null) {
335
    		throw new AuthenticationException(0, "Access token not set");
336
    	}
337
    	
338
        return 'Bearer ' . $this->accessToken->getAccessToken();
339
    }
340
341
}
342