Skip to content

Commit 6c6b8a2

Browse files
authored
Merge pull request #5 from sugarcrm-developers/develop
v1.0.1
2 parents 91a3483 + 19bd6ae commit 6c6b8a2

18 files changed

+814
-171
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
],
2121
"require": {
2222
"php": ">=8.0",
23-
"michaelj2324/php-rest-client": ">=3.0.2"
23+
"michaelj2324/php-rest-client": ">=3.0.5"
2424
},
2525
"require-dev": {
2626
"phpunit/phpunit": "9.*",

examples/IntegrateApi.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/**
4+
* ©[2025] SugarCRM Inc. Licensed by SugarCRM under the Apache 2.0 license.
5+
*/
6+
7+
require_once 'include.php';
8+
9+
$SugarAPI = new \Sugarcrm\REST\Client\SugarApi($server, $credentials);
10+
$logger = new \ColinODell\PsrTestLogger\TestLogger();
11+
$SugarAPI->getAuth()->setLogger($logger);
12+
try {
13+
if ($SugarAPI->isAuthenticated()) {
14+
echo "Logged In: " . json_encode($SugarAPI->getAuth()->getToken(), JSON_PRETTY_PRINT) . "\n";
15+
16+
$account = $SugarAPI->module('Accounts');
17+
$account->set([
18+
'name' => 'Upsert Test Account',
19+
'sync_key' => 'foobar',
20+
]);
21+
$account->upsert();
22+
echo "Record: " . json_encode($account->toArray(), JSON_PRETTY_PRINT) . "\n";
23+
24+
$contact = $SugarAPI->module('Contacts');
25+
$contact->set([
26+
'first_name' => 'Upsert',
27+
'last_name' => 'Test Contact',
28+
]);
29+
//Create a contact
30+
$contact->save();
31+
echo "Created Contact: " . $contact->getId() . "\n";
32+
33+
$integrate = $SugarAPI->integrate();
34+
$integrate->fromBean($contact);
35+
$integrate->setSyncKey('uniqueSyncKey');
36+
if ($integrate->getResponse()->getStatusCode() == 200) {
37+
echo "Contact Sync Key set: " . $contact->getSyncKey() . "\n";
38+
}
39+
40+
$samecontact = $SugarAPI->integrate('Contacts');
41+
$samecontact->getBySyncKey('uniqueSyncKey');
42+
echo "Retrieved Contact by Sync Key: " . $samecontact->getId() . "\n";
43+
44+
$integrate->deleteBySyncKey();
45+
echo "Deleted Contact: " . json_encode($account->toArray(), JSON_PRETTY_PRINT) . "\n";
46+
} else {
47+
echo "Could not login.";
48+
print_r($logger->records);
49+
$oauthEndpoint = $SugarAPI->getAuth()->getActionEndpoint('authenticate');
50+
$response = $oauthEndpoint->getResponse();
51+
if ($response) {
52+
$statusCode = $oauthEndpoint->getResponse()->getStatusCode();
53+
echo "[$statusCode] - " . $oauthEndpoint->getResponse()->getBody()->getContents();
54+
}
55+
}
56+
} catch (Exception $ex) {
57+
echo "Exception Occurred: " . $ex->getMessage();
58+
echo $ex->getTraceAsString();
59+
}

src/Client/SugarApi.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
namespace Sugarcrm\REST\Client;
88

9+
use GuzzleHttp\Client;
910
use MRussell\REST\Auth\AuthControllerInterface;
1011
use Psr\Http\Message\MessageInterface;
1112
use Sugarcrm\REST\Endpoint\Generic;
13+
use Sugarcrm\REST\Endpoint\Integrate;
1214
use Sugarcrm\REST\Endpoint\MLPackage;
1315
use Sugarcrm\REST\Endpoint\ModuleLoader;
1416
use Sugarcrm\REST\Endpoint\Ping;
@@ -45,6 +47,7 @@
4547
* @method Note note(string $id = null) -
4648
* @method ModuleLoader moduleLoader() -
4749
* @method MLPackage mlp(string $id = null)
50+
* @method Integrate integrate(string $module = '', string $id = '')
4851
*/
4952
class SugarApi extends AbstractClient implements PlatformAwareInterface
5053
{
@@ -108,6 +111,11 @@ public function __construct($server = '', array $credentials = [])
108111
}
109112
}
110113

114+
protected function initHttpClient(): void
115+
{
116+
$this->httpClient = new Client(['handler' => $this->getHandlerStack(),'verify' => false]);
117+
}
118+
111119
/**
112120
* Setup the default Auth Controller and EndpointProvider
113121
*/

src/Endpoint/Abstracts/AbstractSugarBeanCollectionEndpoint.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@ abstract class AbstractSugarBeanCollectionEndpoint extends AbstractSugarCollecti
3838

3939
protected string $_modelInterface = SugarBean::class;
4040

41-
public function getCollectionResponseProp(): string
41+
public function setUrlArgs(array $args): static
4242
{
43-
$prop = parent::getCollectionResponseProp();
44-
return empty($prop) ? self::SUGAR_COLLECTION_RESP_PROP : $prop;
43+
parent::setUrlArgs($args);
44+
$this->syncModuleAndUrlArgs();
45+
return $this;
4546
}
4647

47-
public function setUrlArgs(array $args): static
48+
public function getCollectionResponseProp(): string
4849
{
49-
$args = $this->configureModuleUrlArg($args);
50-
return parent::setUrlArgs($args);
50+
$prop = parent::getCollectionResponseProp();
51+
return empty($prop) ? self::SUGAR_COLLECTION_RESP_PROP : $prop;
5152
}
5253

5354
/**
@@ -102,7 +103,7 @@ protected function configurePayload(): mixed
102103
*/
103104
protected function configureURL(array $urlArgs): string
104105
{
105-
$urlArgs['module'] = $this->getModule();
106+
$urlArgs = $this->addModuleToUrlArgs($urlArgs);
106107
return parent::configureURL($urlArgs);
107108
}
108109

src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php

Lines changed: 79 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
use MRussell\REST\Traits\PsrLoggerTrait;
2020
use Sugarcrm\REST\Endpoint\Data\FilterData;
2121
use Sugarcrm\REST\Endpoint\AuditLog;
22+
use Sugarcrm\REST\Endpoint\Integrate;
2223
use Sugarcrm\REST\Endpoint\SugarEndpointInterface;
2324
use Sugarcrm\REST\Endpoint\Traits\CompileRequestTrait;
2425
use Sugarcrm\REST\Endpoint\Traits\FieldsDataTrait;
26+
use Sugarcrm\REST\Endpoint\Traits\IntegrateSyncKeyTrait;
2527
use Sugarcrm\REST\Endpoint\Traits\ModuleAwareTrait;
2628

2729
/**
@@ -39,14 +41,18 @@
3941
* @method $this audit()
4042
* @method $this file()
4143
* @method $this duplicateCheck()
44+
* @method $this upsert()
4245
*/
4346
abstract class AbstractSugarBeanEndpoint extends ModelEndpoint implements SugarEndpointInterface
4447
{
45-
use CompileRequestTrait;
48+
use CompileRequestTrait {
49+
compileRequest as private doCompileRequest;
50+
}
4651
use PsrLoggerTrait;
4752
use ModuleAwareTrait;
4853
use FieldsDataTrait;
4954
use FileUploadsTrait;
55+
use IntegrateSyncKeyTrait;
5056

5157
public const MODEL_ACTION_VAR = 'action';
5258

@@ -80,19 +86,21 @@ abstract class AbstractSugarBeanEndpoint extends ModelEndpoint implements SugarE
8086

8187
public const BEAN_ACTION_DUPLICATE_CHECK = 'duplicateCheck';
8288

83-
public const BEAN_ACTION_ARG1_VAR = 'actionArg1';
89+
public const BEAN_ACTION_UPSERT = 'upsert';
90+
91+
public const BEAN_ACTION_ARG1_VAR = 'actArg1';
8492

85-
public const BEAN_ACTION_ARG2_VAR = 'actionArg2';
93+
public const BEAN_ACTION_ARG2_VAR = 'actArg2';
8694

87-
public const BEAN_ACTION_ARG3_VAR = 'actionArg3';
95+
public const BEAN_ACTION_ARG3_VAR = 'actArg3';
8896

8997
public const BEAN_MODULE_URL_ARG = 'module';
9098

9199
/**
92100
* @inheritdoc
93101
*/
94102
protected static array $_DEFAULT_PROPERTIES = [
95-
self::PROPERTY_URL => '$module/$id/$:action/$:actionArg1/$:actionArg2/$:actionArg3',
103+
self::PROPERTY_URL => '$module/$:id/$:action/$:actArg1/$:actArg2/$:actArg3',
96104
self::PROPERTY_HTTP_METHOD => 'GET',
97105
self::PROPERTY_AUTH => true,
98106
];
@@ -116,6 +124,7 @@ abstract class AbstractSugarBeanEndpoint extends ModelEndpoint implements SugarE
116124
self::BEAN_ACTION_ATTACH_FILE => "POST",
117125
self::BEAN_ACTION_TEMP_FILE_UPLOAD => "POST",
118126
self::BEAN_ACTION_DUPLICATE_CHECK => "POST",
127+
self::BEAN_ACTION_UPSERT => "PATCH",
119128
];
120129

121130
/**
@@ -139,21 +148,16 @@ public function __construct(array $urlArgs = [], array $properties = [])
139148
}
140149

141150
/**
142-
* Passed in options get changed such that 1st Option (key 0) becomes Module
143-
* 2nd Option (Key 1) becomes ID
144151
* @inheritdoc
145152
*/
146153
public function setUrlArgs(array $args): static
147154
{
148-
$args = $this->configureModuleUrlArg($args);
149-
if (isset($args[1])) {
150-
$this->set($this->getKeyProperty(), $args[1]);
151-
unset($args[1]);
152-
}
153-
154-
return parent::setUrlArgs($args);
155+
parent::setUrlArgs($args);
156+
$this->syncModuleAndUrlArgs();
157+
return $this;
155158
}
156159

160+
157161
/**
158162
* Configure Uploads on Request
159163
* @inheritdoc
@@ -179,6 +183,16 @@ protected function configurePayload(): mixed
179183
{
180184
$data = $this->getData();
181185
switch ($this->getCurrentAction()) {
186+
case self::BEAN_ACTION_UPSERT:
187+
$data->reset();
188+
$data->set($this->toArray());
189+
$syncKeyField = $this->getSyncKeyField();
190+
if (!empty($syncKeyField)) {
191+
$data[Integrate::DATA_SYNC_KEY_FIELD] = $syncKeyField;
192+
}
193+
194+
$data[Integrate::DATA_SYNC_KEY_VALUE] = $this->getSyncKey();
195+
return $data;
182196
case self::MODEL_ACTION_CREATE:
183197
case self::MODEL_ACTION_UPDATE:
184198
$data->reset();
@@ -220,6 +234,10 @@ protected function parseResponse(Response $response): void
220234
$this->clear();
221235
$this->syncFromApi($this->parseResponseBodyToArray($body, $this->getModelResponseProp()));
222236
return;
237+
case self::BEAN_ACTION_UPSERT:
238+
$body = $this->getResponseContent($response);
239+
$this->syncFromApi($this->parseResponseBodyToArray($body, Integrate::INTEGRATE_RESPONSE_PROP));
240+
return;
223241
}
224242
}
225243

@@ -266,13 +284,21 @@ protected function resetUploads(): void
266284
protected function configureURL(array $urlArgs): string
267285
{
268286
$action = null;
269-
$urlArgs = $this->configureModuleUrlArg($urlArgs);
287+
$urlArgs = $this->addModuleToUrlArgs($urlArgs);
270288
switch ($this->getCurrentAction()) {
271289
case self::BEAN_ACTION_CREATE_RELATED:
272290
case self::BEAN_ACTION_MASS_RELATE:
273291
case self::BEAN_ACTION_UNLINK:
274292
case self::BEAN_ACTION_FILTER_RELATED:
275293
$action = self::BEAN_ACTION_RELATE;
294+
break;
295+
case self::BEAN_ACTION_UPSERT:
296+
$urlArgs[self::MODEL_ID_VAR] = Integrate::SYNC_KEY;
297+
$syncKey = $this->getSyncKey();
298+
if (!empty($syncKey)) {
299+
$action = $syncKey;
300+
}
301+
276302
break;
277303
case self::BEAN_ACTION_TEMP_FILE_UPLOAD:
278304
$urlArgs[self::MODEL_ID_VAR] = 'temp';
@@ -310,39 +336,43 @@ protected function configureURL(array $urlArgs): string
310336
protected function configureAction(string $action, array $arguments = []): void
311337
{
312338
$urlArgs = $this->getUrlArgs();
313-
if (isset($urlArgs[self::BEAN_ACTION_ARG1_VAR])) {
314-
unset($urlArgs[self::BEAN_ACTION_ARG1_VAR]);
315-
}
316-
317-
if (isset($urlArgs[self::BEAN_ACTION_ARG2_VAR])) {
318-
unset($urlArgs[self::BEAN_ACTION_ARG2_VAR]);
319-
}
320-
321-
if (isset($urlArgs[self::BEAN_ACTION_ARG3_VAR])) {
322-
unset($urlArgs[self::BEAN_ACTION_ARG3_VAR]);
339+
if (isset($urlArgs[self::MODEL_ACTION_VAR]) && $urlArgs[self::MODEL_ACTION_VAR] != $action) {
340+
unset($urlArgs[self::MODEL_ACTION_VAR]);
323341
}
324342

325-
if (!empty($arguments)) {
326-
switch ($action) {
327-
case self::BEAN_ACTION_TEMP_FILE_UPLOAD:
328-
case self::BEAN_ACTION_ATTACH_FILE:
329-
$this->_upload = true;
330-
// no break
331-
case self::BEAN_ACTION_RELATE:
332-
case self::BEAN_ACTION_DOWNLOAD_FILE:
333-
case self::BEAN_ACTION_UNLINK:
334-
case self::BEAN_ACTION_CREATE_RELATED:
335-
case self::BEAN_ACTION_FILTER_RELATED:
336-
if (isset($arguments[0])) {
337-
$urlArgs[self::BEAN_ACTION_ARG1_VAR] = $arguments[0];
338-
if (isset($arguments[1])) {
339-
$urlArgs[self::BEAN_ACTION_ARG2_VAR] = $arguments[1];
340-
if (isset($arguments[2])) {
341-
$urlArgs[self::BEAN_ACTION_ARG3_VAR] = $arguments[2];
342-
}
343+
switch ($action) {
344+
case self::BEAN_ACTION_TEMP_FILE_UPLOAD:
345+
case self::BEAN_ACTION_ATTACH_FILE:
346+
$this->_upload = true;
347+
// no break
348+
case self::BEAN_ACTION_RELATE:
349+
case self::BEAN_ACTION_DOWNLOAD_FILE:
350+
case self::BEAN_ACTION_UNLINK:
351+
case self::BEAN_ACTION_CREATE_RELATED:
352+
case self::BEAN_ACTION_FILTER_RELATED:
353+
if (isset($arguments[0])) {
354+
$urlArgs[self::BEAN_ACTION_ARG1_VAR] = $arguments[0];
355+
if (isset($arguments[1])) {
356+
$urlArgs[self::BEAN_ACTION_ARG2_VAR] = $arguments[1];
357+
if (isset($arguments[2])) {
358+
$urlArgs[self::BEAN_ACTION_ARG3_VAR] = $arguments[2];
343359
}
344360
}
345-
}
361+
}
362+
363+
break;
364+
default:
365+
//If action is not defined above, remove action args
366+
$actionArgs = [
367+
self::BEAN_ACTION_ARG1_VAR,
368+
self::BEAN_ACTION_ARG2_VAR,
369+
self::BEAN_ACTION_ARG3_VAR,
370+
];
371+
foreach ($actionArgs as $actionArg) {
372+
if (isset($urlArgs[$actionArg])) {
373+
unset($urlArgs[$actionArg]);
374+
}
375+
}
346376
}
347377

348378
$this->setUrlArgs($urlArgs);
@@ -542,13 +572,11 @@ protected function configureFileUploadQueryParams(): array
542572
'delete_if_fails' => $this->_deleteFileOnFail,
543573
];
544574

545-
if ($this->_deleteFileOnFail) {
546-
if (!empty($this->_client)) {
547-
$data['platform'] = $this->getClient()->getPlatform();
548-
$token = $this->getClient()->getAuth()->getTokenProp('access_token');
549-
if ($token) {
550-
$data['oauth_token'] = $this->getClient()->getAuth()->getTokenProp('access_token');
551-
}
575+
if ($this->_deleteFileOnFail && !empty($this->_client)) {
576+
$data['platform'] = $this->getClient()->getPlatform();
577+
$token = $this->getClient()->getAuth()->getTokenProp('access_token');
578+
if ($token) {
579+
$data['oauth_token'] = $this->getClient()->getAuth()->getTokenProp('access_token');
552580
}
553581
}
554582

0 commit comments

Comments
 (0)