Skip to content

Commit

Permalink
Add Soap Auth Client and fix issues with dates and composite collecti…
Browse files Browse the repository at this point in the history
…on requests and responses (#8)
curiosity26 authored Oct 23, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 12884bb commit 441aafb
Showing 20 changed files with 426 additions and 194 deletions.
6 changes: 3 additions & 3 deletions Tests/AuthProvider/LoginAuthProviderTest.php
Original file line number Diff line number Diff line change
@@ -8,22 +8,22 @@

namespace AE\SalesforceRestSdk\Tests\AuthProvider;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use PHPUnit\Framework\TestCase;

class LoginAuthProviderTest extends TestCase
{
public function testReauthorize()
{
$auth = new LoginProvider(
$auth = new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
getenv("SF_PASS"),
getenv("SF_LOGIN_URL")
);

$class = new \ReflectionClass(LoginProvider::class);
$class = new \ReflectionClass(OAuthProvider::class);

$tokenProperty = $class->getProperty('token');
$tokenProperty->setAccessible(true);
43 changes: 43 additions & 0 deletions Tests/AuthProvider/SoapAuthProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* Created by PhpStorm.
* User: alex.boyce
* Date: 9/27/18
* Time: 6:02 PM
*/

namespace AE\SalesforceRestSdk\Tests\AuthProvider;

use AE\SalesforceRestSdk\AuthProvider\SoapProvider;
use PHPUnit\Framework\TestCase;

class SoapAuthProviderTest extends TestCase
{
public function testReauthorize()
{
$auth = new SoapProvider(
getenv("SF_USER"),
getenv("SF_PASS"),
getenv("SF_LOGIN_URL")
);

$header = $auth->authorize();

$this->assertNotNull($header);
$this->assertNotNull($auth->getToken());
$this->assertNotNull($auth->getInstanceUrl());

$class = new \ReflectionClass(SoapProvider::class);

$tokenProperty = $class->getProperty('token');
$tokenProperty->setAccessible(true);
$tokenProperty->setValue($auth, 'BAD_VALUE');

$header = $auth->reauthorize();

$this->assertNotNull($header);
$this->assertNotNull($auth->getToken());
$this->assertNotNull($auth->getInstanceUrl());
$this->assertEquals('Bearer', $auth->getTokenType());
}
}
6 changes: 3 additions & 3 deletions Tests/Bayeux/BayeuxClientTest.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

namespace AE\SalesforceRestSdk\Tests\Bayeux;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use AE\SalesforceRestSdk\Bayeux\BayeuxClient;
use AE\SalesforceRestSdk\Bayeux\ChannelInterface;
use AE\SalesforceRestSdk\Bayeux\Consumer;
@@ -31,7 +31,7 @@ protected function setUp()
{
$this->client = new BayeuxClient(
new LongPollingTransport(),
new LoginProvider(
new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
@@ -112,7 +112,7 @@ public function testHandshakeReauth()
$this->client->disconnect();
}

$class = new \ReflectionClass(LoginProvider::class);
$class = new \ReflectionClass(OAuthProvider::class);

$tokenProperty = $class->getProperty('token');
$tokenProperty->setAccessible(true);
4 changes: 2 additions & 2 deletions Tests/Bulk/BulkClientTest.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

namespace AE\SalesforceRestSdk\Tests\Bulk;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use AE\SalesforceRestSdk\Bulk\BatchInfo;
use AE\SalesforceRestSdk\Bulk\Client;
use AE\SalesforceRestSdk\Bulk\JobInfo;
@@ -39,7 +39,7 @@ class BulkClientTest extends TestCase
protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
$this->client = new Client(
new LoginProvider(
new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
6 changes: 3 additions & 3 deletions Tests/Rest/ClientTest.php
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
*/
namespace AE\SalesforceRestSdk\Tests\Rest;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use AE\SalesforceRestSdk\Rest\Client;
use PHPUnit\Framework\TestCase;

@@ -21,7 +21,7 @@ class ClientTest extends TestCase
protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
$this->client = new Client(
new LoginProvider(
new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
@@ -46,7 +46,7 @@ public function testRetry()

$this->assertNotNull($limits);

$class = new \ReflectionClass(LoginProvider::class);
$class = new \ReflectionClass(OAuthProvider::class);

$tokenProperty = $class->getProperty('token');
$tokenProperty->setAccessible(true);
4 changes: 2 additions & 2 deletions Tests/Rest/Composite/CompositeClientTest.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

namespace AE\SalesforceRestSdk\Tests\Composite;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use AE\SalesforceRestSdk\Model\Rest\Composite\CollectionResponse;
use AE\SalesforceRestSdk\Model\Rest\Composite\CompositeCollection;
use AE\SalesforceRestSdk\Model\Rest\Composite\CompositeSObject;
@@ -30,7 +30,7 @@ class CompositeClientTest extends TestCase
protected function setUp()
{
$client = new Client(
new LoginProvider(
new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
4 changes: 2 additions & 2 deletions Tests/Rest/SObject/SObjectClientTest.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

namespace AE\SalesforceRestSdk\Tests\Rest\SObject;

use AE\SalesforceRestSdk\AuthProvider\LoginProvider;
use AE\SalesforceRestSdk\AuthProvider\OAuthProvider;
use AE\SalesforceRestSdk\Model\SObject;
use AE\SalesforceRestSdk\Rest\SObject\Client;
use PHPUnit\Framework\TestCase;
@@ -24,7 +24,7 @@ class SObjectClientTest extends TestCase
protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
$client = new \AE\SalesforceRestSdk\Rest\Client(
new LoginProvider(
new OAuthProvider(
getenv("SF_CLIENT_ID"),
getenv("SF_CLIENT_SECRET"),
getenv("SF_USER"),
164 changes: 9 additions & 155 deletions src/AuthProvider/LoginProvider.php
Original file line number Diff line number Diff line change
@@ -2,165 +2,19 @@
/**
* Created by PhpStorm.
* User: alex.boyce
* Date: 9/6/18
* Time: 3:27 PM
* Date: 10/23/18
* Time: 9:07 AM
*/

namespace AE\SalesforceRestSdk\AuthProvider;

use GuzzleHttp\Client;

class LoginProvider implements AuthProviderInterface
/**
* Class LoginProvider
*
* @package AE\SalesforceRestSdk\AuthProvider
* @deprecated To be removed in a later version
*/
class LoginProvider extends OAuthProvider
{
/**
* @var bool
*/
private $isAuthorized = false;

/**
* @var string
*/
private $token;

/**
* @var string
*/
private $tokenType;

/**
* @var Client
*/
private $httpClient;

/**
* @var string
*/
private $username;

/**
* @var string
*/
private $password;

/**
* @var string
*/
private $clientId;

/**
* @var string
*/
private $clientSecret;

/**
* @var null|string
*/
private $instanceUrl;

public function __construct(string $clientId, string $clientSecret, string $username, string $password, string $url)
{
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->username = $username;
$this->password = $password;

$this->httpClient = new Client(
[
'base_uri' => $url,
]
);
}

/**
* @param bool $reauth
*
* @throws SessionExpiredOrInvalidException
* @return string
*/
public function authorize($reauth = false): string
{
if (!$reauth && $this->isAuthorized && strlen($this->token) > 0) {
return "{$this->tokenType} {$this->token}";
}

$response = $this->httpClient->post(
'/services/oauth2/token',
[
'form_params' => [
'grant_type' => 'password',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'username' => $this->username,
'password' => $this->password,
],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
],
]
);

$body = (string)$response->getBody();
$parts = json_decode($body, true);

if (401 === $response->getStatusCode()) {
throw new SessionExpiredOrInvalidException($parts['message'], $parts['errorCode']);
}

$this->tokenType = $parts['token_type'];
$this->token = $parts['access_token'];
$this->instanceUrl = $parts['instance_url'];

$this->isAuthorized = true;

return "{$this->tokenType} {$this->token}";
}

/**
* @return string
*/
public function reauthorize(): string
{
return $this->authorize(true);
}


public function revoke(): void
{
$this->token = null;
$this->tokenType = null;
$this->isAuthorized = false;
}

/**
* @return string
*/
public function getToken(): ?string
{
return $this->token;
}

/**
* @return string
*/
public function getTokenType(): ?string
{
return $this->tokenType;
}

/**
* @return bool
*/
public function isAuthorized(): bool
{
return $this->isAuthorized;
}

/**
* @return null|string
*/
public function getInstanceUrl(): ?string
{
return $this->instanceUrl;
}
}
166 changes: 166 additions & 0 deletions src/AuthProvider/OAuthProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php
/**
* Created by PhpStorm.
* User: alex.boyce
* Date: 9/6/18
* Time: 3:27 PM
*/

namespace AE\SalesforceRestSdk\AuthProvider;

use GuzzleHttp\Client;

class OAuthProvider implements AuthProviderInterface
{
/**
* @var bool
*/
private $isAuthorized = false;

/**
* @var string
*/
private $token;

/**
* @var string
*/
private $tokenType;

/**
* @var Client
*/
private $httpClient;

/**
* @var string
*/
private $username;

/**
* @var string
*/
private $password;

/**
* @var string
*/
private $clientId;

/**
* @var string
*/
private $clientSecret;

/**
* @var null|string
*/
private $instanceUrl;

public function __construct(string $clientId, string $clientSecret, string $username, string $password, string $url)
{
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->username = $username;
$this->password = $password;

$this->httpClient = new Client(
[
'base_uri' => $url,
]
);
}

/**
* @param bool $reauth
*
* @throws SessionExpiredOrInvalidException
* @return string
*/
public function authorize($reauth = false): string
{
if (!$reauth && $this->isAuthorized && strlen($this->token) > 0) {
return "{$this->tokenType} {$this->token}";
}

$response = $this->httpClient->post(
'/services/oauth2/token',
[
'form_params' => [
'grant_type' => 'password',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'username' => $this->username,
'password' => $this->password,
],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
],
]
);

$body = (string)$response->getBody();
$parts = json_decode($body, true);

if (401 === $response->getStatusCode()) {
throw new SessionExpiredOrInvalidException($parts['message'], $parts['errorCode']);
}

$this->tokenType = $parts['token_type'];
$this->token = $parts['access_token'];
$this->instanceUrl = $parts['instance_url'];

$this->isAuthorized = true;

return "{$this->tokenType} {$this->token}";
}

/**
* @return string
*/
public function reauthorize(): string
{
return $this->authorize(true);
}


public function revoke(): void
{
$this->token = null;
$this->tokenType = null;
$this->isAuthorized = false;
}

/**
* @return string
*/
public function getToken(): ?string
{
return $this->token;
}

/**
* @return string
*/
public function getTokenType(): ?string
{
return $this->tokenType;
}

/**
* @return bool
*/
public function isAuthorized(): bool
{
return $this->isAuthorized;
}

/**
* @return null|string
*/
public function getInstanceUrl(): ?string
{
return $this->instanceUrl;
}
}
158 changes: 158 additions & 0 deletions src/AuthProvider/SoapProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php
/**
* Created by PhpStorm.
* User: alex.boyce
* Date: 10/23/18
* Time: 9:08 AM
*/

namespace AE\SalesforceRestSdk\AuthProvider;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class SoapProvider implements AuthProviderInterface
{
public const VERSION = "44.0";
/**
* @var bool
*/
private $isAuthorized = false;

/**
* @var string
*/
private $token;

/**
* @var string
*/
private $tokenType = 'Bearer';

/**
* @var Client
*/
private $httpClient;

/**
* @var string
*/
private $username;

/**
* @var string
*/
private $password;

/**
* @var null|string
*/
private $instanceUrl;

public function __construct(string $username, string $password, string $url = 'https://login.salesforce.com/')
{
$this->username = $username;
$this->password = $password;
$this->httpClient = new Client(
[
'base_uri' => $url,
'headers' => [
'Content-Type' => 'text/xml',
'SOAPAction' => '""',
],
]
);
}

public function authorize($reauth = false)
{
if (!$reauth && $this->isAuthorized && strlen($this->token) > 0) {
return "{$this->tokenType} {$this->token}";
}

$body
= "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soapenv:Body>
<login xmlns=\"urn:partner.soap.sforce.com\">
<username>{$this->username}</username>
<password>{$this->password}</password>
</login>
</soapenv:Body>
</soapenv:Envelope>";

try {
$response = $this->httpClient->post(
'/services/Soap/u/'.self::VERSION,
[
'body' => $body,
]
);

$soapBody = (string)$response->getBody();

$matches = [];
if (false != preg_match(
'/<serverUrl>(?<serverUrl>.*?)<\/serverUrl>.*?<sessionId>(?<sessionId>.*?)<\/sessionId>/',
$soapBody,
$matches
)) {
$this->instanceUrl = $matches['serverUrl'];
$this->token = $matches['sessionId'];
} else {
throw new SessionExpiredOrInvalidException("Failed to login to Salesforce.", "INVALID_CREDENTIALS");
}

return "{$this->tokenType} {$this->token}";
} catch (RequestException $e) {
$response = $e->getResponse();
$body = (string) $response->getBody();
throw new SessionExpiredOrInvalidException(
"Failed to authenticate with Salesforce.",
"INVALID_CREDENTIALS"
);
}
}

public function reauthorize()
{
return $this->authorize(true);
}

public function revoke(): void
{
$this->token = null;
$this->isAuthorized = false;
}

/**
* @return string
*/
public function getToken(): ?string
{
return $this->token;
}

/**
* @return string
*/
public function getTokenType(): ?string
{
return $this->tokenType;
}

/**
* @return bool
*/
public function isAuthorized(): bool
{
return $this->isAuthorized;
}

/**
* @return null|string
*/
public function getInstanceUrl(): ?string
{
return $this->instanceUrl;
}
}
2 changes: 1 addition & 1 deletion src/Bulk/Client.php
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ class Client extends AbstractClient
/**
*
*/
public const VERSION = "43.0";
public const VERSION = "44.0";

/**
*
2 changes: 1 addition & 1 deletion src/Model/Rest/Composite/CollectionRequest.php
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ class CollectionRequest implements CollectionRequestInterface

/**
* @var Collection
* @Serializer\Type("ArrayCollection<AE\SalesforceRestSdk\Model\SObject>")
* @Serializer\Type("ArrayCollection<AE\SalesforceRestSdk\Model\Rest\Composite\CompositeSObject>")
*/
private $records;

9 changes: 6 additions & 3 deletions src/Model/Rest/Composite/SObject/GetDeletedSubRequest.php
Original file line number Diff line number Diff line change
@@ -117,9 +117,12 @@ public function preSerialize()
$this->url = '/'.Client::BASE_PATH.'sobjects/'.$this->sObjectType.'/deleted/?'
.http_build_query(
[
'start' => $this->start->format(\DATE_ISO8601),
'end' => $this->end->format(\DATE_ISO8601),
]
'start' => $this->start->format('Y-m-d\Th:i:sP'),
'end' => $this->end->format('Y-m-d\Th:i:sP'),
],
null,
'&',
PHP_QUERY_RFC3986
);
}
}
9 changes: 6 additions & 3 deletions src/Model/Rest/Composite/SObject/GetUpdatedSubRequest.php
Original file line number Diff line number Diff line change
@@ -113,9 +113,12 @@ public function preSerialize()
$this->url = '/'.Client::BASE_PATH.'sobjects/'.$this->sObjectType.'/updated/?'
.http_build_query(
[
'start' => $this->start->format(\DATE_ISO8601),
'end' => $this->end->format(\DATE_ISO8601),
]
'start' => $this->start->format('Y-m-d\Th:i:sP'),
'end' => $this->end->format('Y-m-d\Th:i:sP'),
],
null,
'&',
PHP_QUERY_RFC3986
);
}
}
9 changes: 4 additions & 5 deletions src/Model/Rest/DeletedResponse.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@

namespace AE\SalesforceRestSdk\Model\Rest;

use AE\SalesforceRestSdk\Model\SObject;
use JMS\Serializer\Annotation as Serializer;

/**
@@ -20,7 +19,7 @@
class DeletedResponse
{
/**
* @var array|SObject[]
* @var array|DeletedRecord[]
* @Serializer\Type("array<AE\SalesforceRestSdk\Model\Rest\DeletedRecord>")
*/
private $deletedRecords = [];
@@ -38,15 +37,15 @@ class DeletedResponse
private $latestDateCovered;

/**
* @return SObject[]|array
* @return DeletedRecord[]|array
*/
public function getDeletedRecords()
{
return $this->deletedRecords;
}

/**
* @param SObject[]|array $deletedRecords
* @param DeletedRecord[]|array $deletedRecords
*
* @return DeletedResponse
*/
@@ -58,7 +57,7 @@ public function setDeletedRecords($deletedRecords)
}

/**
* @return \DateTime|null
* @return \DateTime|\DateTimeImmutable|null
*/
public function getEarliestDateAvailable(): ?\DateTime
{
2 changes: 1 addition & 1 deletion src/Rest/Client.php
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@

class Client extends AbstractClient
{
public const VERSION = "43.0";
public const VERSION = "44.0";

/**
* @var string
2 changes: 1 addition & 1 deletion src/Rest/Composite/Builder/ArrayReference.php
Original file line number Diff line number Diff line change
@@ -12,6 +12,6 @@ class ArrayReference extends Reference
{
public function field(string $field, int $index = 0)
{
return "@{{$this->getReferenceId()}[$index].$field}";
return "@{{$this->getReferenceId()}.[$index].$field}";
}
}
2 changes: 1 addition & 1 deletion src/Rest/Composite/Builder/CompositeRequestBuilder.php
Original file line number Diff line number Diff line change
@@ -117,7 +117,7 @@ public function getDeleted(
string $referenceId,
string $sObjectType,
\DateTime $start,
?\DateTime $end
?\DateTime $end = null
): CompositeRequestBuilder {
return $this->addSubRequest(
new GetDeletedSubRequest(
2 changes: 1 addition & 1 deletion src/Rest/Composite/CompositeClient.php
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@

class CompositeClient extends AbstractClient
{
public const VERSION = '43.0';
public const VERSION = '44.0';

public const BASE_PATH = '/services/data/v'.self::VERSION.'/composite';

20 changes: 13 additions & 7 deletions src/Rest/SObject/Client.php
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@

class Client extends AbstractClient
{
public const VERSION = "43.0";
public const VERSION = "44.0";

public const BASE_PATH = "services/data/v".self::VERSION."/";

@@ -155,9 +155,12 @@ public function getUpdated(string $sObjectType, \DateTime $start, \DateTime $end
self::BASE_PATH.'sobjects/'.$sObjectType.'/updated/?'.
http_build_query(
[
'start' => $start->format(\DateTime::ISO8601),
'end' => $end->format(\DateTime::ISO8601),
]
'start' => $start->format('Y-m-d\Th:i:sP'),
'end' => $end->format('Y-m-d\Th:i:sP'),
],
null,
'&',
PHP_QUERY_RFC3986
)
)
);
@@ -195,9 +198,12 @@ public function getDeleted(string $sObjectType, \DateTime $start, \DateTime $end
self::BASE_PATH.'sobjects/'.$sObjectType.'/deleted/?'.
http_build_query(
[
'start' => $start->format(\DateTime::ISO8601),
'end' => $end->format(\DateTime::ISO8601),
]
'start' => $start->format('Y-m-d\Th:i:sP'),
'end' => $end->format('Y-m-d\Th:i:sP'),
],
null,
'&',
PHP_QUERY_RFC3986
)
)
);

0 comments on commit 441aafb

Please sign in to comment.