Completed
Pull Request — master (#4)
by
unknown
04:33
created

Client::isSuccessful()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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