diff --git a/examples/IntegrateApi.php b/examples/IntegrateApi.php index c1f40f3..6a501c7 100644 --- a/examples/IntegrateApi.php +++ b/examples/IntegrateApi.php @@ -19,7 +19,11 @@ 'sync_key' => 'foobar', ]); $account->upsert(); - echo "Record: " . json_encode($account->toArray(), JSON_PRETTY_PRINT) . "\n"; + echo "Record Created: " . json_encode($account->toArray(), JSON_PRETTY_PRINT) . "\n"; + $account->account_type = 'Prospect'; + $account->email1 = 'test@example.com'; + $account->upsert(); + echo "Account Updated: " . json_encode($account->toArray(), JSON_PRETTY_PRINT) . "\n"; $contact = $SugarAPI->module('Contacts'); $contact->set([ diff --git a/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php b/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php index 3507d71..ba20ac1 100644 --- a/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractSugarBeanEndpoint.php @@ -184,8 +184,17 @@ protected function configurePayload(): mixed $data = $this->getData(); switch ($this->getCurrentAction()) { case self::BEAN_ACTION_UPSERT: + $fields = []; + if (isset($data[AbstractSugarBeanCollectionEndpoint::SUGAR_FIELDS_DATA_PROPERTY])) { + $fields = $data[AbstractSugarBeanCollectionEndpoint::SUGAR_FIELDS_DATA_PROPERTY]; + } + $data->reset(); $data->set($this->toArray()); + if (!empty($fields)) { + $data[AbstractSugarBeanCollectionEndpoint::SUGAR_FIELDS_DATA_PROPERTY] = $fields; + } + $syncKeyField = $this->getSyncKeyField(); if (!empty($syncKeyField)) { $data[Integrate::DATA_SYNC_KEY_FIELD] = $syncKeyField; @@ -215,7 +224,7 @@ protected function configurePayload(): mixed protected function parseResponse(Response $response): void { $this->resetUploads(); - if ($response->getStatusCode() == 200) { + if (in_array($response->getStatusCode(), [200,201])) { $this->getData()->reset(); switch ($this->getCurrentAction()) { case self::BEAN_ACTION_TEMP_FILE_UPLOAD: @@ -236,7 +245,17 @@ protected function parseResponse(Response $response): void return; case self::BEAN_ACTION_UPSERT: $body = $this->getResponseContent($response); - $this->syncFromApi($this->parseResponseBodyToArray($body, Integrate::INTEGRATE_RESPONSE_PROP)); + $body = $this->parseResponseBodyToArray($body); + if (!empty($body[Integrate::INTEGRATE_RESPONSE_PROP])) { + if (is_string($body[Integrate::INTEGRATE_RESPONSE_PROP])) { + $model = ['id' => $body[Integrate::INTEGRATE_RESPONSE_PROP]]; + } else { + $model = $body[Integrate::INTEGRATE_RESPONSE_PROP]; + } + + $this->syncFromApi($model); + } + return; } } diff --git a/src/Endpoint/Integrate.php b/src/Endpoint/Integrate.php index 83cff9d..9c5a104 100644 --- a/src/Endpoint/Integrate.php +++ b/src/Endpoint/Integrate.php @@ -105,6 +105,7 @@ protected function configureAction(string $action, array $arguments = []): void $action = self::INTEGRATE_ACTION_RETRIEVE; break; } + if ($this->_action !== $action) { $this->_action = $action; } @@ -159,7 +160,7 @@ protected function configurePayload(): mixed protected function parseResponse(Response $response): void { - if ($response->getStatusCode() == 200) { + if (in_array($response->getStatusCode(), [200,201])) { switch ($this->getCurrentAction()) { case self::INTEGRATE_ACTION_DELETE: case self::MODEL_ACTION_DELETE: @@ -174,16 +175,25 @@ protected function parseResponse(Response $response): void $syncKeyField = $this->getSyncKeyField(); $syncKey = $this->getSyncKey(); $this->_sugarBean->set($syncKeyField ?: self::SYNC_KEY, $syncKey); - if ($syncKeyField !== '' && $syncKeyField !== '0') { + if ($syncKeyField !== '' && $syncKeyField !== self::SYNC_KEY) { $this->_sugarBean->setSyncKeyField($syncKeyField); } } if ($this->getCurrentAction() !== self::INTEGRATE_ACTION_SET_SK) { $body = $this->getResponseContent($response); - $this->syncFromApi($this->parseResponseBodyToArray($body, $this->getModelResponseProp())); - if (isset($this->_sugarBean)) { - $this->_sugarBean->set($this->toArray()); + $body = $this->parseResponseBodyToArray($body); + if (!empty($body[Integrate::INTEGRATE_RESPONSE_PROP])) { + if (is_string($body[Integrate::INTEGRATE_RESPONSE_PROP])) { + $model = ['id' => $body[Integrate::INTEGRATE_RESPONSE_PROP]]; + } else { + $model = $body[Integrate::INTEGRATE_RESPONSE_PROP]; + } + + $this->syncFromApi($model); + if (isset($this->_sugarBean)) { + $this->_sugarBean->set($this->toArray()); + } } } } diff --git a/src/Endpoint/Note.php b/src/Endpoint/Note.php index 184f9cb..8c2bb07 100644 --- a/src/Endpoint/Note.php +++ b/src/Endpoint/Note.php @@ -96,7 +96,7 @@ public function clear(): static * Reset the attachments link to default blank values * @return $this */ - public function resetAttachments(): self + public function resetAttachments(): static { $this->resetAttachmentsProp(); $this->getData()->offsetUnset($this->getAttachmentsLinkField()); diff --git a/src/Endpoint/Traits/IntegrateSyncKeyTrait.php b/src/Endpoint/Traits/IntegrateSyncKeyTrait.php index c8645dc..b666901 100644 --- a/src/Endpoint/Traits/IntegrateSyncKeyTrait.php +++ b/src/Endpoint/Traits/IntegrateSyncKeyTrait.php @@ -38,10 +38,12 @@ public function getSyncKey(): string|int|null if (method_exists($this, 'get')) { $key = $this->get($field); + //@codeCoverageIgnoreStart } elseif (property_exists($this, '_attributes')) { $key = $this->_attributes[$field]; } + //@codeCoverageIgnoreEnd return $key; } diff --git a/src/Endpoint/Traits/NoteAttachmentsTrait.php b/src/Endpoint/Traits/NoteAttachmentsTrait.php index d27a3fd..a44972a 100644 --- a/src/Endpoint/Traits/NoteAttachmentsTrait.php +++ b/src/Endpoint/Traits/NoteAttachmentsTrait.php @@ -11,7 +11,7 @@ */ trait NoteAttachmentsTrait { - private $_attachments = [ + private array $_attachments = [ 'add' => [], 'delete' => [], 'create' => [], @@ -26,7 +26,7 @@ protected function getAttachmentsLinkField(): string * Reset the attachments link to default blank values * @return $this */ - public function resetAttachments() + public function resetAttachments(): static { $this->_attachments = [ 'add' => [], diff --git a/tests/Endpoint/AbstractSugarBeanEndpointTest.php b/tests/Endpoint/AbstractSugarBeanEndpointTest.php index 5edc572..cd5de09 100644 --- a/tests/Endpoint/AbstractSugarBeanEndpointTest.php +++ b/tests/Endpoint/AbstractSugarBeanEndpointTest.php @@ -7,6 +7,7 @@ namespace Sugarcrm\REST\Tests\Endpoint; use PHPUnit\Framework\TestCase; +use Sugarcrm\REST\Endpoint\Abstracts\AbstractSugarBeanEndpoint; use Sugarcrm\REST\Endpoint\AuditLog; use Sugarcrm\REST\Endpoint\Data\FilterData; use MRussell\REST\Exception\Endpoint\InvalidRequest; @@ -51,6 +52,15 @@ public function testCompileRequest(): void $this->assertEquals("GET", $Request->getMethod()); $this->assertEquals('http://localhost/rest/v11/Foo/bar', $Request->getUri()->__toString()); $this->assertEmpty($Request->getBody()->getContents()); + + $Bean->setUrlArgs(['Accounts','12345']); + $Bean->setCurrentAction(AbstractSugarBeanEndpoint::BEAN_ACTION_UPSERT); + + $Bean->sync_key = '67890'; + $Request = $Bean->compileRequest(); + $this->assertEquals("PATCH", $Request->getMethod()); + $this->assertEquals('http://localhost/rest/v11/Accounts/sync_key/67890', $Request->getUri()->__toString()); + $this->assertNotEmpty($Request->getBody()->getContents()); } /** @@ -756,4 +766,55 @@ public function testDownloadFile(): void $this->assertEquals("test", file_get_contents($Bean->getDownloadedFile())); unlink($Bean->getDownloadedFile()); } + + /** + * @covers ::configureAction + * @covers ::configurePayload + * @covers ::parseResponse + */ + public function testUpsertAction(): void + { + $this->client->mockResponses->append(new Response(201, [], json_encode(['record' => '12345']))); + $Bean = new SugarBean(); + $Bean->setClient($this->client); + $Bean->setModule('Accounts'); + $Bean->set([ + 'name' => 'Test Account', + 'account_type' => 'Prospect', + 'sync_key' => '098765', + ]); + $Bean->upsert(); + + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('upsert', $Bean->getCurrentAction()); + $this->assertEquals('/rest/v11/Accounts/sync_key/098765', $request->getUri()->getPath()); + $this->assertEquals('PATCH', $request->getMethod()); + $payload = $request->getBody()->getContents(); + $this->assertEquals([ + 'name' => 'Test Account', + 'account_type' => 'Prospect', + 'sync_key' => '098765', + 'sync_key_field_value' => '098765', + ], json_decode($payload, true)); + $this->assertEquals('12345', $Bean->id); + + $this->client->mockResponses->append(new Response(201, [], json_encode(['record' => ['test' => 'foobar']]))); + $Bean->getData()['fields'] = ['test']; + $Bean->upsert(); + + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('/rest/v11/Accounts/sync_key/098765', $request->getUri()->getPath()); + $this->assertEquals('PATCH', $request->getMethod()); + $payload = $request->getBody()->getContents(); + $this->assertEquals([ + 'id' => '12345', + 'name' => 'Test Account', + 'account_type' => 'Prospect', + 'sync_key' => '098765', + 'sync_key_field_value' => '098765', + 'fields' => ['test'], + ], json_decode($payload, true)); + $this->assertEquals('12345', $Bean->id); + $this->assertEquals('foobar', $Bean->test); + } } diff --git a/tests/Endpoint/IntegrateTest.php b/tests/Endpoint/IntegrateTest.php index 5ad22df..9ef5bc0 100644 --- a/tests/Endpoint/IntegrateTest.php +++ b/tests/Endpoint/IntegrateTest.php @@ -64,6 +64,42 @@ public function testConfigureAction(): void $this->assertEquals(Integrate::INTEGRATE_ACTION_DELETE, $endpoint->getCurrentAction()); } + public function testUpsert(): void + { + $this->client->mockResponses->append(new Response('201', [], json_encode(['record' => '12345']))); + $endpoint = new Integrate(); + $endpoint->setClient($this->client); + $endpoint->setModule('Accounts'); + $endpoint['name'] = 'Test Account'; + $endpoint['sync_key'] = 'test'; + $endpoint->upsert(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('PATCH', $request->getMethod()); + $this->assertEquals('/rest/v11/integrate/Accounts', $request->getUri()->getPath()); + $body = json_decode($request->getBody()->getContents(), true); + $this->assertNotEmpty($body); + $this->assertEquals('test', $body[Integrate::DATA_SYNC_KEY_VALUE]); + $this->assertEquals('test', $endpoint->getSyncKey()); + $this->assertEquals('12345', $endpoint->getId()); + + $this->client->mockResponses->append(new Response('201', [], json_encode(['record' => ['foobar_c' => 'test', 'account_type' => 'Prospect']]))); + $endpoint->setSyncKeyField('sync_key'); + $endpoint->getData()['fields'] = 'foobar_c,account_type'; + $endpoint->upsert(); + $request = $this->client->mockResponses->getLastRequest(); + $this->assertEquals('PATCH', $request->getMethod()); + $this->assertEquals('/rest/v11/integrate/Accounts/sync_key/test', $request->getUri()->getPath()); + $body = json_decode($request->getBody()->getContents(), true); + $this->assertNotEmpty($body); + $this->assertEquals('test', $body[Integrate::DATA_SYNC_KEY_VALUE]); + $this->assertEquals('12345', $body['id']); + $this->assertEquals('foobar_c,account_type', $body['fields']); + $this->assertEquals('test', $endpoint->getSyncKey()); + $this->assertEquals('12345', $endpoint->getId()); + $this->assertEquals('test', $endpoint->foobar_c); + $this->assertEquals('Prospect', $endpoint['account_type']); + } + /** * @covers ::getSyncKey * @covers ::getSyncKeyField @@ -161,6 +197,7 @@ public function testGetBySyncKey(): void $this->client->mockResponses->append(new Response('200', [], json_encode($this->responsePayload))); $endpoint->setCurrentAction(Integrate::MODEL_ACTION_RETRIEVE); $endpoint->execute(); + $request = $this->client->mockResponses->getLastRequest(); $this->assertEquals('GET', $request->getMethod()); $this->assertEquals('/rest/v11/integrate/Accounts', $request->getUri()->getPath()); diff --git a/tests/Endpoint/NoteTest.php b/tests/Endpoint/NoteTest.php index dbd917b..69520d8 100644 --- a/tests/Endpoint/NoteTest.php +++ b/tests/Endpoint/NoteTest.php @@ -36,6 +36,8 @@ protected function tearDown(): void } /** + * @covers \Sugarcrm\REST\Endpoint\Traits\NoteAttachmentsTrait::resetAttachments + * @covers \Sugarcrm\REST\Endpoint\Traits\NoteAttachmentsTrait::getAttachmentsLinkField * @covers ::resetAttachments * @covers ::deleteAttachments * @covers ::clear @@ -120,6 +122,8 @@ public function testParseFiles(): void * @covers ::multiAttach * @covers ::parseResponse * @covers ::configurePayload + * @covers \Sugarcrm\REST\Endpoint\Traits\NoteAttachmentsTrait::configureAttachmentsPayload + * @covers \Sugarcrm\REST\Endpoint\Traits\NoteAttachmentsTrait::parseAttachmentUploadResponse * @covers ::configureUrl */ public function testMultiattach(): void