Skip to content

Commit ee4364d

Browse files
committed
#105 CallerId is broken #42
1 parent 2458c92 commit ee4364d

File tree

4 files changed

+167
-16
lines changed

4 files changed

+167
-16
lines changed

src/Enum/UpsertBehaviorEnum.php

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
/**
3+
* Copyright 2018 AlexaCRM
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6+
* associated documentation files (the "Software"), to deal in the Software without restriction, including
7+
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15+
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
16+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
17+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
18+
* OR OTHER DEALINGS IN THE SOFTWARE.
19+
*
20+
*/
21+
22+
namespace AlexaCRM\Enum;
23+
24+
/**
25+
* Specifies the behavior for an Upsert operation.
26+
*/
27+
enum UpsertBehaviorEnum: int {
28+
29+
case CreateOrUpdate = 0;
30+
case PreventUpdate = 1;
31+
case PreventCreate = 2;
32+
}

src/WebAPI/Client.php

+66-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace AlexaCRM\WebAPI;
2323

24+
use AlexaCRM\Enum\UpsertBehaviorEnum;
2425
use AlexaCRM\WebAPI\OData\AuthenticationException;
2526
use AlexaCRM\WebAPI\OData\Client as ODataClient;
2627
use AlexaCRM\WebAPI\OData\EntityNotSupportedException;
@@ -49,13 +50,20 @@ class Client implements IOrganizationService {
4950
*/
5051
protected ODataClient $client;
5152

53+
protected ?string $MSCRMCallerID;
54+
55+
protected ?string $callerObjectId;
56+
57+
protected UpsertBehaviorEnum $upsertBehavior;
58+
5259
/**
5360
* Client constructor.
5461
*
5562
* @param ODataClient $client
5663
*/
5764
public function __construct( ODataClient $client ) {
5865
$this->client = $client;
66+
$this->upsertBehavior = UpsertBehaviorEnum::CreateOrUpdate;
5967
}
6068

6169
/**
@@ -107,7 +115,7 @@ public function Create( Entity $entity ): string {
107115
$translatedData = $serializer->serializeEntity( $entity );
108116

109117
$collectionName = $this->client->getMetadata()->getEntitySetName( $entity->LogicalName );
110-
118+
$this->updateWebApiHeaderRequest();
111119
$responseId = $this->client->create( $collectionName, $translatedData );
112120

113121
$entity->getAttributeState()->reset();
@@ -233,9 +241,8 @@ public function Retrieve( string $entityName, string $entityId, ColumnSet $colum
233241
$response = $this->client->getRecord( $collectionName, $entityId, $options );
234242

235243
$serializer = new SerializationHelper( $this->client );
236-
$entity = $serializer->deserializeEntity( $response, new EntityReference( $entityName, $entityId ) );
237244

238-
return $entity;
245+
return $serializer->deserializeEntity( $response, new EntityReference( $entityName, $entityId ) );
239246
} catch ( ODataException $e ) {
240247
if ( $e->getCode() === 404 ) {
241248
return null;
@@ -388,7 +395,7 @@ protected function retrieveViaQueryByAttribute( QueryByAttribute $query ): Entit
388395
$queryValue = "'{$value}'";
389396
break;
390397
case is_bool( $value ):
391-
$queryValue = $value? 'true' : 'false';
398+
$queryValue = $value ? 'true' : 'false';
392399
break;
393400
case $value === null:
394401
$queryValue = 'null';
@@ -491,6 +498,30 @@ protected function retrieveViaQueryByAttribute( QueryByAttribute $query ): Entit
491498
}
492499
}
493500

501+
/**
502+
* Updating WebApi Request Headers
503+
*
504+
* @return void
505+
*/
506+
private function updateWebApiHeaderRequest(): void {
507+
switch ( $this->upsertBehavior ) {
508+
case UpsertBehaviorEnum::PreventCreate:
509+
$this->client->setWepApiHeaderByName( "If-Match", "*" );
510+
break;
511+
case UpsertBehaviorEnum::PreventUpdate:
512+
$this->client->setWepApiHeaderByName( "If-None-Match", "*" );
513+
break;
514+
default:
515+
break;
516+
}
517+
518+
if ( $this->callerObjectId !== null ) {
519+
$this->client->setWepApiHeaderByName( 'CallerObjectId', $this->callerObjectId );
520+
} elseif ( $this->MSCRMCallerID !== null ) {
521+
$this->client->setWepApiHeaderByName( 'MSCRMCallerID', $this->MSCRMCallerID );
522+
}
523+
}
524+
494525
/**
495526
* Updates an existing record.
496527
*
@@ -508,6 +539,7 @@ public function Update( Entity $entity ): void {
508539

509540
$collectionName = $this->client->getMetadata()->getEntitySetName( $entity->LogicalName );
510541

542+
$this->updateWebApiHeaderRequest();
511543
$this->client->update( $collectionName, $entity->Id, $translatedData );
512544

513545
$entity->getAttributeState()->reset();
@@ -536,4 +568,34 @@ public function getCachePool(): CacheItemPoolInterface {
536568
return $this->client->getCachePool();
537569
}
538570

571+
public function getMSCRMCallerID(): ?string {
572+
return $this->MSCRMCallerID;
573+
}
574+
575+
public function setMSCRMCallerID( ?string $MSCRMCallerID ): void {
576+
try {
577+
//search Microsoft Entra ID object ID
578+
$entity = $this->Retrieve( 'systemuser', $MSCRMCallerID, new ColumnSet( [ 'azureactivedirectoryobjectid' ] ) );
579+
$this->callerObjectId = $entity?->Attributes['azureactivedirectoryobjectid'];
580+
} catch ( Exception ) {
581+
}
582+
$this->MSCRMCallerID = $MSCRMCallerID;
583+
}
584+
585+
public function getCallerObjectId(): ?string {
586+
return $this->callerObjectId;
587+
}
588+
589+
public function setCallerObjectId( ?string $callerObjectId ): void {
590+
$this->callerObjectId = $callerObjectId;
591+
}
592+
593+
public function getUpsertBehavior(): UpsertBehaviorEnum {
594+
return $this->upsertBehavior;
595+
}
596+
597+
public function setUpsertBehavior( UpsertBehaviorEnum $upsertBehavior ): void {
598+
$this->upsertBehavior = $upsertBehavior;
599+
}
600+
539601
}

src/WebAPI/OData/Client.php

+60-10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use GuzzleHttp\Exception\RequestException;
3535
use GuzzleHttp\HandlerStack;
3636
use Psr\Cache\CacheItemPoolInterface;
37+
use Psr\Cache\InvalidArgumentException;
3738
use Psr\Http\Message\ResponseInterface;
3839
use Psr\Log\LoggerInterface;
3940

@@ -66,6 +67,13 @@ class Client {
6667
*/
6768
protected array $middlewares = [];
6869

70+
/**
71+
* header to Web API request
72+
*
73+
* @var array
74+
*/
75+
protected array $wepApiHeaders = [];
76+
6977
/**
7078
* Client constructor.
7179
*
@@ -129,8 +137,11 @@ public function getHttpClient(): HttpClient {
129137
/**
130138
* Retrieves OData Service Metadata.
131139
*
140+
* @return Metadata
132141
* @throws AuthenticationException
142+
* @throws GuzzleException
133143
* @throws TransportException
144+
* @throws InvalidArgumentException
134145
*/
135146
public function getMetadata(): Metadata {
136147
if ( $this->metadata instanceof Metadata ) {
@@ -178,6 +189,34 @@ public function getMetadata(): Metadata {
178189
return $this->metadata;
179190
}
180191

192+
/**
193+
* Get header for Web Api Request
194+
*
195+
* @param array|null $headers
196+
* @param string|null $method
197+
*
198+
* @return array
199+
*/
200+
private function getRequestHeaders( ?array $headers = [], ?string $method = 'GET' ): array {
201+
if ( in_array( $method, [ 'POST', 'PATCH' ] ) ) {
202+
$headers = array_merge( [ 'Content-Type' => 'application/json' ], $headers );
203+
}
204+
205+
//Odata/Client/Setting more important than the headers in the request
206+
if ( $this->settings->callerObjectId || $this->settings->callerID ) {
207+
$this->unsetWepApiHeaderByName( 'CallerObjectId' );
208+
$this->unsetWepApiHeaderByName( 'MSCRMCallerID' );
209+
}
210+
211+
if ( $this->settings->callerObjectId !== null ) {
212+
$headers = array_merge( [ 'CallerObjectId' => $this->settings->callerObjectId ], $headers );
213+
} elseif ( $this->settings->callerID !== null ) {
214+
$headers = array_merge( [ 'MSCRMCallerID' => $this->settings->callerID ], $headers );
215+
}
216+
217+
return $headers;
218+
}
219+
181220
/**
182221
* @param string $method
183222
* @param string $url
@@ -190,13 +229,7 @@ public function getMetadata(): Metadata {
190229
* @throws TransportException
191230
*/
192231
private function doRequest( string $method, string $url, $data = null, array $headers = [] ): ResponseInterface {
193-
if ( in_array( $method, [ 'POST', 'PATCH' ] ) ) {
194-
$headers = array_merge( [ 'Content-Type' => 'application/json' ], $headers );
195-
}
196-
197-
if ( $this->settings->callerID !== null ) {
198-
$headers = array_merge( [ 'MSCRMCallerID' => '$this->settings->callerID' ], $headers );
199-
}
232+
$headers = $this->getRequestHeaders( $headers, $method );
200233

201234
try {
202235
$payload = [
@@ -577,7 +610,7 @@ public function associate(
577610
string $fromEntityId,
578611
string $navProperty,
579612
string $toEntityCollection,
580-
string $toEntityId
613+
string $toEntityId,
581614
): void {
582615
$url = sprintf( '%s%s(%s)/%s/$ref', $this->settings->getEndpointURI(), $fromEntityCollection, $fromEntityId, $navProperty );
583616
$data = [ Annotation::ODATA_ID => sprintf( '%s%s(%s)', $this->settings->getEndpointURI(), $toEntityCollection, $toEntityId ) ];
@@ -600,9 +633,10 @@ public function disassociate(
600633
string $fromEntityId,
601634
string $navProperty,
602635
string $toEntityCollection,
603-
string $toEntityId
636+
string $toEntityId,
604637
): void {
605-
$url = sprintf( '%s%s(%s)/%s/$ref?$id=%s%s(%s)', $this->settings->getEndpointURI(), $fromEntityCollection, $fromEntityId, $navProperty, $this->settings->getEndpointURI(), $toEntityCollection, $toEntityId );
638+
$url = sprintf( '%s%s(%s)/%s/$ref?$id=%s%s(%s)', $this->settings->getEndpointURI(), $fromEntityCollection, $fromEntityId, $navProperty, $this->settings->getEndpointURI(),
639+
$toEntityCollection, $toEntityId );
606640
$this->doRequest( 'DELETE', $url );
607641
}
608642

@@ -795,4 +829,20 @@ protected static function getEntityId( ResponseInterface $response ): ?string {
795829
return $id;
796830
}
797831

832+
public function getWepApiHeaderByName( $headerName ): array {
833+
return $this?->wepApiHeaders[ $headerName ];
834+
}
835+
836+
public function unsetWepApiHeaderByName( $headerName ): void {
837+
unset( $this->wepApiHeaders[ $headerName ] );
838+
}
839+
840+
public function setWepApiHeaderByName( $headerName, $headerValue ): void {
841+
$this->wepApiHeaders[ $headerName ] = $headerValue;
842+
}
843+
844+
public function getWepApiHeaders(): array {
845+
return $this->wepApiHeaders;
846+
}
847+
798848
}

src/WebAPI/OData/Settings.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,19 @@ abstract class Settings implements LoggerAwareInterface {
7575
public LoggerInterface $logger;
7676

7777
/**
78-
* ID of the user to impersonate during calls.
79-
*
78+
* ID of the user (systemuserid) to impersonate during calls.
79+
* @deprecated
8080
* Null value means impersonation is not performed.
8181
*/
8282
public ?string $callerID = null;
8383

84+
/**
85+
* Microsoft Entra ID object ID (Azure AD Object ID) - azureactivedirectoryobjectid
86+
*
87+
* Null value means impersonation is not performed.
88+
*/
89+
public ?string $callerObjectId = null;
90+
8491
/**
8592
* Settings constructor.
8693
*/

0 commit comments

Comments
 (0)