diff --git a/app/Jobs/Dav/GetMultipleVCard.php b/app/Jobs/Dav/GetMultipleVCard.php index 0e8833a8880..bc19d00524d 100644 --- a/app/Jobs/Dav/GetMultipleVCard.php +++ b/app/Jobs/Dav/GetMultipleVCard.php @@ -10,7 +10,6 @@ use Sabre\CardDAV\Plugin as CardDAVPlugin; use Illuminate\Contracts\Queue\ShouldQueue; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\ContactUpdateDto; class GetMultipleVCard implements ShouldQueue @@ -51,10 +50,11 @@ public function handle(): void return; } - $datas = app(DavClient::class)->addressbookMultiget($this->subscription->getRequest(), [ - '{DAV:}getetag', - $this->getAddressDataProperty(), - ], $this->hrefs); + $datas = $this->subscription->getClient() + ->addressbookMultiget([ + '{DAV:}getetag', + $this->getAddressDataProperty(), + ], $this->hrefs); collect($datas) ->filter(function (array $contact): bool { diff --git a/app/Jobs/Dav/GetVCard.php b/app/Jobs/Dav/GetVCard.php index 9bfa4238e57..577416b8753 100644 --- a/app/Jobs/Dav/GetVCard.php +++ b/app/Jobs/Dav/GetVCard.php @@ -52,10 +52,8 @@ public function handle(): void Log::info(__CLASS__.' '.$this->contact->uri); - $response = $this->subscription->getRequest() - ->get($this->contact->uri); - - $response->throw(); + $response = $this->subscription->getClient() + ->request('GET', $this->contact->uri); $this->chainUpdateVCard($response->body()); } diff --git a/app/Jobs/Dav/PushVCard.php b/app/Jobs/Dav/PushVCard.php index 520ae730db8..f3daf6f2148 100644 --- a/app/Jobs/Dav/PushVCard.php +++ b/app/Jobs/Dav/PushVCard.php @@ -59,11 +59,8 @@ public function handle(): void $headers['If-Match'] = '*'; } - $response = $this->subscription->getRequest() - ->withHeaders($headers) - ->put($this->contact->uri, [$this->contact->card]); - - $response->throw(); + $response = $this->subscription->getClient() + ->request('PUT', $this->contact->uri, $this->contact->card, $headers); if (! empty($etag = $response->header('Etag')) && $etag !== $this->contact->etag) { Log::warning(__CLASS__.' wrong etag when updating contact. Expected '.$this->contact->etag.', get '.$etag); diff --git a/app/Models/Account/AddressBookSubscription.php b/app/Models/Account/AddressBookSubscription.php index 92429a1847b..533bd4b2f9a 100644 --- a/app/Models/Account/AddressBookSubscription.php +++ b/app/Models/Account/AddressBookSubscription.php @@ -5,10 +5,9 @@ use App\Models\User\User; use function safe\json_decode; use function safe\json_encode; -use Illuminate\Support\Facades\Http; use App\Models\ModelBinding as Model; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Http\Client\PendingRequest; +use App\Services\DavClient\Utils\Dav\DavClient; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -153,14 +152,14 @@ public function scopeActive($query) } /** - * Get a pending request. + * Get a new client. * - * @return PendingRequest + * @return DavClient */ - public function getRequest(): PendingRequest + public function getClient(): DavClient { - return Http::withBasicAuth($this->username, $this->password) - ->baseUrl($this->uri) - ->withUserAgent('Monica DavClient '.config('monica.app_version')); + return app(DavClient::class) + ->setBaseUri($this->uri) + ->setCredentials($this->username, $this->password); } } diff --git a/app/Services/DavClient/CreateAddressBookSubscription.php b/app/Services/DavClient/CreateAddressBookSubscription.php index e102aa69493..326abf56bdb 100644 --- a/app/Services/DavClient/CreateAddressBookSubscription.php +++ b/app/Services/DavClient/CreateAddressBookSubscription.php @@ -6,7 +6,6 @@ use App\Services\BaseService; use function Safe\preg_replace; use App\Models\Account\AddressBook; -use GuzzleHttp\Client as GuzzleClient; use App\Models\Account\AddressBookSubscription; use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\AddressBookGetter; @@ -34,14 +33,13 @@ public function rules() * Add a new Adress Book. * * @param array $data - * @param GuzzleClient|null $httpClient * @return AddressBookSubscription|null */ - public function execute(array $data, GuzzleClient $httpClient = null): ?AddressBookSubscription + public function execute(array $data): ?AddressBookSubscription { $this->validate($data); - $addressBookData = $this->getAddressBookData($data, $httpClient); + $addressBookData = $this->getAddressBookData($data); if (! $addressBookData) { throw new DavClientException(__('Could not get address book data.')); } @@ -76,22 +74,18 @@ public function execute(array $data, GuzzleClient $httpClient = null): ?AddressB return $subscription; } - private function getAddressBookData(array $data, ?GuzzleClient $httpClient): ?array + private function getAddressBookData(array $data): ?array { - $client = $this->getClient($data, $httpClient); + $client = $this->getClient($data); return app(AddressBookGetter::class) ->execute($client); } - private function getClient(array $data, ?GuzzleClient $client): DavClient + private function getClient(array $data): DavClient { - $settings = Arr::only($data, [ - 'base_uri', - 'username', - 'password', - ]); - - return app(DavClient::class)->init($settings, $client); + return app(DavClient::class) + ->setBaseUri(Arr::get($data, 'base_uri')) + ->setCredentials(Arr::get($data, 'username'), Arr::get($data, 'password')); } } diff --git a/app/Services/DavClient/SynchronizeAddressBook.php b/app/Services/DavClient/SynchronizeAddressBook.php index 24ccde5209b..686efcc9205 100644 --- a/app/Services/DavClient/SynchronizeAddressBook.php +++ b/app/Services/DavClient/SynchronizeAddressBook.php @@ -8,7 +8,6 @@ use App\Helpers\AccountHelper; use App\Models\Account\Account; use Illuminate\Support\Facades\Log; -use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Exception\ClientException; use App\Models\Account\AddressBookSubscription; use App\Services\DavClient\Utils\Dav\DavClient; @@ -37,7 +36,7 @@ public function rules() * @param array $data * @return void */ - public function execute(array $data, GuzzleClient $httpClient = null) + public function execute(array $data) { $this->validate($data); @@ -57,7 +56,7 @@ public function execute(array $data, GuzzleClient $httpClient = null) $backend = new CardDAVBackend($user); try { - $this->sync($data, $subscription, $backend, $httpClient); + $this->sync($data, $subscription, $backend); } catch (ClientException $e) { Log::error(__CLASS__.' execute: '.$e->getMessage(), [$e]); if ($e->hasResponse()) { @@ -66,9 +65,9 @@ public function execute(array $data, GuzzleClient $httpClient = null) } } - private function sync(array $data, AddressBookSubscription $subscription, CardDAVBackend $backend, ?GuzzleClient $httpClient) + private function sync(array $data, AddressBookSubscription $subscription, CardDAVBackend $backend) { - $client = $this->getDavClient($subscription, $httpClient); + $client = $this->getDavClient($subscription); $sync = new SyncDto($subscription, $client, $backend); $force = Arr::get($data, 'force', false); @@ -76,13 +75,10 @@ private function sync(array $data, AddressBookSubscription $subscription, CardDA ->execute($sync, $force); } - private function getDavClient(AddressBookSubscription $subscription, ?GuzzleClient $client): DavClient + private function getDavClient(AddressBookSubscription $subscription): DavClient { return app(DavClient::class) - ->init([ - 'base_uri' => $subscription->uri, - 'username' => $subscription->username, - 'password' => $subscription->password, - ], $client); + ->setBaseUri($subscription->uri) + ->setCredentials($subscription->username, $subscription->password); } } diff --git a/app/Services/DavClient/Utils/AddressBookContactsPush.php b/app/Services/DavClient/Utils/AddressBookContactsPush.php index 2954247c8ef..2f297e71052 100644 --- a/app/Services/DavClient/Utils/AddressBookContactsPush.php +++ b/app/Services/DavClient/Utils/AddressBookContactsPush.php @@ -51,7 +51,7 @@ private function preparePushAddedContacts(array $contacts): Collection $card = $this->sync->backend->getCard($this->sync->addressBookName(), $uri); return $card !== false - ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], 0)) + ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'])) : null; }); } @@ -79,7 +79,7 @@ private function preparePushChangedContacts(Collection $changes, array $contacts $card = $this->sync->backend->getCard($this->sync->addressBookName(), $uri); return $card !== false - ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], 1)) + ? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], ContactPushDto::MODE_MATCH_ETAG)) : null; }); } diff --git a/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php b/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php index 16cba25703d..0a4b188aded 100644 --- a/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php +++ b/app/Services/DavClient/Utils/AddressBookContactsPushMissed.php @@ -64,7 +64,7 @@ private function preparePushMissedContacts(array $added, Collection $distContact })->map(function (Contact $contact): PushVCard { $card = $this->sync->backend->prepareCard($contact); - return new PushVCard($this->sync->subscription, new ContactPushDto($card['uri'], $card['etag'], $card['carddata'], 2)); + return new PushVCard($this->sync->subscription, new ContactPushDto($card['uri'], $card['etag'], $card['carddata'], ContactPushDto::MODE_MATCH_ANY)); }); } } diff --git a/app/Services/DavClient/Utils/AddressBookGetter.php b/app/Services/DavClient/Utils/AddressBookGetter.php index 1f0e569ae08..5f9f2dae8b4 100644 --- a/app/Services/DavClient/Utils/AddressBookGetter.php +++ b/app/Services/DavClient/Utils/AddressBookGetter.php @@ -100,7 +100,7 @@ private function getAddressBookBaseUri(): string throw new DavClientException('No address book found'); } - return $this->client->getBaseUri($addressBook); + return $this->client->path($addressBook); } /** @@ -181,7 +181,7 @@ private function getAddressBookUrl(string $principal): ?string { $home = $this->getAddressBookHome($principal); - $books = $this->client->propfind($home, '{DAV:}resourcetype', 1); + $books = $this->client->propfind('{DAV:}resourcetype', 1, [], $home); foreach ($books as $book => $properties) { if ($book == $home) { diff --git a/app/Services/DavClient/Utils/AddressBookSynchronizer.php b/app/Services/DavClient/Utils/AddressBookSynchronizer.php index 747a535658c..bf9ca019ceb 100644 --- a/app/Services/DavClient/Utils/AddressBookSynchronizer.php +++ b/app/Services/DavClient/Utils/AddressBookSynchronizer.php @@ -5,10 +5,8 @@ use Illuminate\Bus\Batch; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use GuzzleHttp\Promise\Promise; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Bus; -use GuzzleHttp\Promise\PromiseInterface; use App\Services\DavClient\Utils\Model\SyncDto; use App\Services\DavClient\Utils\Model\ContactDto; use App\Services\DavClient\Utils\Traits\HasCapability; @@ -106,17 +104,13 @@ private function forcesync() */ private function getDistantChanges(): Collection { - return $this->getDistantEtags() - ->then(function ($collection) { - return collect($collection) - ->filter(function ($contact, $href): bool { - return $this->filterDistantContacts($contact, $href); - }) - ->map(function ($contact, $href): ContactDto { - return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); - }); - }) - ->wait(); + return collect($this->getDistantEtags()) + ->filter(function ($contact, $href): bool { + return $this->filterDistantContacts($contact, $href); + }) + ->map(function ($contact, $href): ContactDto { + return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); + }); } /** @@ -142,16 +136,16 @@ private function filterDistantContacts($contact, $href): bool /** * Get refreshed etags. * - * @return PromiseInterface + * @return array */ - private function getDistantEtags(): PromiseInterface + private function getDistantEtags(): array { if ($this->hasCapability('syncCollection')) { // With sync-collection return $this->callSyncCollectionWhenNeeded(); } else { // With PROPFIND - return $this->sync->client->propFindAsync('', [ + return $this->sync->propFind([ '{DAV:}getcontenttype', '{DAV:}getetag', ], 1); @@ -161,46 +155,43 @@ private function getDistantEtags(): PromiseInterface /** * Make sync-collection request if sync-token has changed. * - * @return PromiseInterface + * @return array */ - private function callSyncCollectionWhenNeeded(): PromiseInterface + private function callSyncCollectionWhenNeeded(): array { // get the current distant syncToken - return $this->sync->client->getPropertyAsync('{DAV:}sync-token') - ->then(function ($distantSyncToken) { - $syncToken = $this->sync->subscription->syncToken ?? ''; + $distantSyncToken = $this->sync->getProperty('{DAV:}sync-token'); - if ($syncToken === $distantSyncToken) { - // no change at all - return $this->emptyPromise(); - } + if (($this->sync->subscription->syncToken ?? '') === $distantSyncToken) { + // no change at all + return []; + } - return $this->callSyncCollection(); - }); + return $this->callSyncCollection(); } /** * Make sync-collection request. * - * @return PromiseInterface + * @return array */ - private function callSyncCollection(): PromiseInterface + private function callSyncCollection(): array { $syncToken = $this->sync->subscription->syncToken ?? ''; // get sync - return $this->sync->client->syncCollectionAsync('', [ + $collection = $this->sync->syncCollection([ '{DAV:}getcontenttype', '{DAV:}getetag', - ], $syncToken)->then(function ($collection) { - // save the new syncToken as current one - if ($newSyncToken = Arr::get($collection, 'synctoken')) { - $this->sync->subscription->syncToken = $newSyncToken; - $this->sync->subscription->save(); - } + ], $syncToken); + + // save the new syncToken as current one + if ($newSyncToken = Arr::get($collection, 'synctoken')) { + $this->sync->subscription->syncToken = $newSyncToken; + $this->sync->subscription->save(); + } - return $collection; - }); + return $collection; } /** @@ -214,30 +205,14 @@ private function getAllContactsEtag(): Collection return collect(); } - return $this->sync->client->addressbookQueryAsync('', '{DAV:}getetag') - ->then(function ($datas) { - return collect($datas) - ->filter(function ($contact) { - return isset($contact[200]); - }) - ->map(function ($contact, $href): ContactDto { - return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); - }); - }) - ->wait(); - } + $datas = $this->sync->addressbookQuery('{DAV:}getetag'); - /** - * Get an empty Promise. - * - * @return PromiseInterface - */ - private function emptyPromise(): PromiseInterface - { - $promise = new Promise(function () use (&$promise) { - $promise->resolve([]); - }); - - return $promise; + return collect($datas) + ->filter(function ($contact) { + return isset($contact[200]); + }) + ->map(function ($contact, $href): ContactDto { + return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); + }); } } diff --git a/app/Services/DavClient/Utils/Dav/DavClient.php b/app/Services/DavClient/Utils/Dav/DavClient.php index 513c6ecbb14..8e70254a28f 100644 --- a/app/Services/DavClient/Utils/Dav/DavClient.php +++ b/app/Services/DavClient/Utils/Dav/DavClient.php @@ -2,54 +2,88 @@ namespace App\Services\DavClient\Utils\Dav; -use GuzzleHttp\Pool; use Sabre\DAV\Xml\Service; use Illuminate\Support\Arr; -use GuzzleHttp\Psr7\Request; -use GuzzleHttp\RequestOptions; -use Illuminate\Support\Facades\App; +use Illuminate\Support\Str; +use Illuminate\Http\Client\Response; +use Illuminate\Support\Facades\Http; use Sabre\DAV\Xml\Request\PropPatch; -use GuzzleHttp\Client as GuzzleClient; -use Psr\Http\Message\ResponseInterface; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Psr7\Utils as GuzzleUtils; use Illuminate\Http\Client\PendingRequest; use Sabre\CardDAV\Plugin as CardDAVPlugin; class DavClient { /** - * @var GuzzleClient + * @var string|null */ - protected $client; + protected $baseUri; /** - * Initialize the client. + * @var string|null + */ + protected $username; + + /** + * @var string|null + */ + protected $password; + + /** + * Set the base uri of client. * - * @param array $settings - * @param GuzzleClient $client - * @return DavClient + * @param string $uri + * @return self */ - public static function init(array $settings, GuzzleClient $client = null): DavClient + public function setBaseUri(string $uri): self { - if (is_null($client) && ! isset($settings['base_uri'])) { - throw new \InvalidArgumentException('A baseUri must be provided'); - } + $this->baseUri = $uri; + + return $this; + } + + /** + * Set credentials. + * + * @param string $username + * @param string $password + * @return self + */ + public function setCredentials(string $username, string $password): self + { + $this->username = $username; + $this->password = $password; + + return $this; + } + + /** + * Get current uri. + * + * @param string|null $path + * @return string + */ + public function path(?string $path = null): string + { + $uri = GuzzleUtils::uriFor($this->baseUri); - $me = new self(); + return (string) (is_null($path) || empty($path) ? $uri : $uri->withPath($path)); + } + + /** + * Get a PendingRequest. + * + * @return PendingRequest + */ + public function getRequest(): PendingRequest + { + $request = Http::withUserAgent('Monica DavClient '.config('monica.app_version').'/Guzzle'); - $me->client = is_null($client) - ? new GuzzleClient([ - 'base_uri' => $settings['base_uri'], - 'auth' => [ - $settings['username'], - $settings['password'], - ], - 'verify' => ! App::environment('local'), - ]) - : $client; + if (! is_null($this->username) && ! is_null($this->password)) { + $request = $request->withBasicAuth($this->username, $this->password); + } - return $me; + return $request; } /** @@ -59,104 +93,63 @@ public static function init(array $settings, GuzzleClient $client = null): DavCl */ public function getServiceUrl() { - $target = $this->standardServiceUrl(); + // first attempt on relative url + $target = $this->standardServiceUrl('.well-known/carddav'); + + if (! $target) { + // second attempt on absolute root url + $target = $this->standardServiceUrl('/.well-known/carddav'); + } if (! $target) { - // second attempt for non standard server, like Google API - $target = $this->nonStandardServiceUrl(); + // third attempt for non standard server, like Google API + $target = $this->nonStandardServiceUrl('/.well-known/carddav'); } if (! $target) { // Get service name register (section 9.2) - $target = app(ServiceUrlQuery::class)->execute('_carddavs._tcp', true, $this->getBaseUri()); + $target = app(ServiceUrlQuery::class)->execute('_carddavs._tcp', true, $this->path(), $this); if (is_null($target)) { - $target = app(ServiceUrlQuery::class)->execute('_carddav._tcp', false, $this->getBaseUri()); + $target = app(ServiceUrlQuery::class)->execute('_carddav._tcp', false, $this->path(), $this); } } return $target; } - private function standardServiceUrl(): ?string + private function standardServiceUrl(string $url): ?string { // Get well-known register (section 9.1) - $wkUri = $this->getBaseUri('/.well-known/carddav'); + $response = $this->getRequest() + ->withoutRedirecting() + ->get($this->path($url)); - try { - $response = $this->requestAsync('GET', $wkUri, [], null, [ - RequestOptions::ALLOW_REDIRECTS => false, - RequestOptions::SYNCHRONOUS => true, - ])->wait(); + $code = $response->status(); + if ($code === 301 || $code === 302) { + return $response->header('Location'); + } - $code = $response->getStatusCode(); - if (($code === 301 || $code === 302) && $response->hasHeader('Location')) { - return $response->getHeader('Location')[0]; - } - } catch (ClientException $e) { - if ($e->hasResponse()) { - $code = $e->getResponse()->getStatusCode(); - if ($code !== 400 && $code !== 401 && $code !== 404) { - throw $e; - } - } + if ($response->serverError()) { + $response->throw(); } return null; } - private function nonStandardServiceUrl(): ?string + private function nonStandardServiceUrl($url): ?string { - $wkUri = $this->getBaseUri('/.well-known/carddav'); - - try { - $response = $this->requestAsync('PROPFIND', $wkUri, [], null, [ - RequestOptions::ALLOW_REDIRECTS => false, - RequestOptions::SYNCHRONOUS => true, - ])->wait(); + $response = $this->getRequest() + ->withoutRedirecting() + ->send('PROPFIND', $this->path($url)); - $code = $response->getStatusCode(); - if (($code === 301 || $code === 302) && $response->hasHeader('Location')) { - $location = $response->getHeader('Location')[0]; - - return $this->getBaseUri($location); - } - } catch (ClientException $e) { - // catch exception and return null + $code = $response->status(); + if ($code === 301 || $code === 302) { + return $this->path($response->header('Location')); } return null; } - /** - * Get current uri. - * - * @param string|null $path - * @return string - */ - public function getBaseUri(?string $path = null): string - { - $baseUri = $this->client->getConfig('base_uri'); - - return (string) (is_null($path) ? $baseUri : $baseUri->withPath($path)); - } - - /** - * Set the base uri of client. - * - * @param string $uri - * @return self - */ - public function setBaseUri($uri): self - { - $this->client = new GuzzleClient( - Arr::except($this->client->getConfig(), ['base_uri']) - + - ['base_uri' => $uri] - ); - - return $this; - } - /** * Do a PROPFIND request. * @@ -178,66 +171,33 @@ public function setBaseUri($uri): self * @param int $depth * @return array */ - public function propFind(string $url, $properties, int $depth = 0): array - { - return $this->propFindAsync($url, $properties, $depth, [ - RequestOptions::SYNCHRONOUS => true, - ])->wait(); - } - - /** - * Do a PROPFIND request. - * - * The list of requested properties must be specified as an array, in clark - * notation. - * - * The returned array will contain a list of filenames as keys, and - * properties as values. - * - * The properties array will contain the list of properties. Only properties - * that are actually returned from the server (without error) will be - * returned, anything else is discarded. - * - * Depth should be either 0 or 1. A depth of 1 will cause a request to be - * made to the server to also return all child resources. - * - * @param string $url - * @param array|string $properties - * @param int $depth - * @return PromiseInterface - */ - public function propFindAsync(string $url, $properties, int $depth = 0, array $options = []): PromiseInterface + public function propFind($properties, int $depth = 0, array $options = [], string $url = ''): array { $dom = new \DOMDocument('1.0', 'UTF-8'); $root = self::addElementNS($dom, 'DAV:', 'd:propfind'); $prop = self::addElement($dom, $root, 'd:prop'); - $namespaces = [ - 'DAV:' => 'd', - ]; + $namespaces = ['DAV:' => 'd']; self::fetchProperties($dom, $prop, $properties, $namespaces); $body = $dom->saveXML(); - return $this->requestAsync('PROPFIND', $url, [ - 'Depth' => $depth, - 'Content-Type' => 'application/xml; charset=utf-8', - ], $body, $options)->then(function (ResponseInterface $response) use ($depth): array { - $result = self::parseMultiStatus((string) $response->getBody()); + $response = $this->request('PROPFIND', $url, $body, ['Depth' => $depth], $options); - // If depth was 0, we only return the top item value - if ($depth === 0) { - reset($result); - $result = current($result); + $result = self::parseMultiStatus($response->body()); - return Arr::get($result, 200, []); - } + // If depth was 0, we only return the top item value + if ($depth === 0) { + reset($result); + $result = current($result); + + return Arr::get($result, 200, []); + } - return array_map(function ($statusList) { - return Arr::get($statusList, 200, []); - }, $result); - }); + return array_map(function ($statusList) { + return Arr::get($statusList, 200, []); + }, $result); } /** @@ -246,11 +206,11 @@ public function propFindAsync(string $url, $properties, int $depth = 0, array $o * @param string $url * @param array|string $properties * @param string $syncToken - * @return PromiseInterface + * @return array * * @see https://datatracker.ietf.org/doc/html/rfc6578 */ - public function syncCollectionAsync(string $url, $properties, string $syncToken, array $options = []): PromiseInterface + public function syncCollection($properties, string $syncToken, array $options = [], string $url = ''): array { $dom = new \DOMDocument('1.0', 'UTF-8'); $root = self::addElementNS($dom, 'DAV:', 'd:sync-collection'); @@ -260,33 +220,29 @@ public function syncCollectionAsync(string $url, $properties, string $syncToken, $prop = self::addElement($dom, $root, 'd:prop'); - $namespaces = [ - 'DAV:' => 'd', - ]; + $namespaces = ['DAV:' => 'd']; self::fetchProperties($dom, $prop, $properties, $namespaces); $body = $dom->saveXML(); - return $this->requestAsync('REPORT', $url, [ - 'Depth' => '0', - 'Content-Type' => 'application/xml; charset=utf-8', - ], $body, $options)->then(function (ResponseInterface $response) { - return self::parseMultiStatus((string) $response->getBody()); - }); + $response = $this->request('REPORT', $url, $body, ['Depth' => '0'], $options); + + return self::parseMultiStatus($response->body()); } /** * Run a REPORT card:addressbook-multiget. * - * @param string $url * @param array|string $properties * @param iterable $contacts + * @param string $url + * @param array $options * @return array * * @see https://datatracker.ietf.org/doc/html/rfc6352#section-8.7 */ - public static function addressbookMultiget(PendingRequest $request, $properties, iterable $contacts, string $url = '', array $options = []): array + public function addressbookMultiget($properties, iterable $contacts, array $options = [], string $url = ''): array { $dom = new \DOMDocument('1.0', 'UTF-8'); $root = self::addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-multiget'); @@ -307,11 +263,7 @@ public static function addressbookMultiget(PendingRequest $request, $properties, $body = $dom->saveXML(); - $response = $request->withHeaders(['Depth' => '1']) - ->withBody($body, 'application/xml; charset=utf-8') - ->send('REPORT', $url, $options); - - $response->throw(); + $response = $this->request('REPORT', $url, $body, ['Depth' => '1'], $options); return self::parseMultiStatus($response->body()); } @@ -321,11 +273,11 @@ public static function addressbookMultiget(PendingRequest $request, $properties, * * @param string $url * @param array|string $properties - * @return PromiseInterface + * @return array * * @see https://datatracker.ietf.org/doc/html/rfc6352#section-8.6 */ - public function addressbookQueryAsync(string $url, $properties, array $options = []): PromiseInterface + public function addressbookQuery($properties, array $options = [], string $url = ''): array { $dom = new \DOMDocument('1.0', 'UTF-8'); $root = self::addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-query'); @@ -342,12 +294,9 @@ public function addressbookQueryAsync(string $url, $properties, array $options = $body = $dom->saveXML(); - return $this->requestAsync('REPORT', $url, [ - 'Depth' => '1', - 'Content-Type' => 'application/xml; charset=utf-8', - ], $body, $options)->then(function (ResponseInterface $response) { - return self::parseMultiStatus((string) $response->getBody()); - }); + $response = $this->request('REPORT', $url, $body, ['Depth' => '1'], $options); + + return self::parseMultiStatus($response->body()); } /** @@ -412,79 +361,48 @@ private static function fetchProperties(\DOMDocument $dom, \DOMNode $prop, $prop * * @param string $property * @param string $url - * @return string|array|null + * @return array|string|null */ - public function getProperty(string $property, string $url = '') + public function getProperty(string $property, string $url = '', array $options = []) { - return $this->getPropertyAsync($property, $url, [ - RequestOptions::SYNCHRONOUS => true, - ])->wait(); - } + $properties = $this->propfind($property, 0, $options, $url); - /** - * Get a WebDAV property. - * - * @param string $property - * @param string $url - * @return PromiseInterface - */ - public function getPropertyAsync(string $property, string $url = '', array $options = []): PromiseInterface - { - return $this->propfindAsync($url, $property, 0, $options) - ->then(function (array $properties) use ($property) { - if (($prop = Arr::get($properties, $property)) - && is_array($prop)) { - $value = $prop[0]; - - if (is_string($value)) { - $prop = $value; - } - } + if (($prop = Arr::get($properties, $property)) && is_array($prop)) { + $value = $prop[0]; - return $prop; - }); - } + if (is_string($value)) { + $prop = $value; + } + } - /** - * Get a {DAV:}supported-report-set propfind. - * - * @return array - * - * @see https://datatracker.ietf.org/doc/html/rfc3253#section-3.1.5 - */ - public function getSupportedReportSet(): array - { - return $this->getSupportedReportSetAsync([ - RequestOptions::SYNCHRONOUS => true, - ])->wait(); + return $prop; } /** * Get a {DAV:}supported-report-set propfind. * * @param array $options - * @return PromiseInterface + * @return array * * @see https://datatracker.ietf.org/doc/html/rfc3253#section-3.1.5 */ - public function getSupportedReportSetAsync(array $options = []): PromiseInterface + public function getSupportedReportSet(array $options = []): array { $propName = '{DAV:}supported-report-set'; - return $this->propFindAsync('', $propName, 0, $options) - ->then(function (array $properties) use ($propName): array { - if (($prop = Arr::get($properties, $propName)) && is_array($prop)) { - $prop = array_map(function ($supportedReport) { - return $this->iterateOver($supportedReport, '{DAV:}supported-report', function ($report) { - return $this->iterateOver($report, '{DAV:}report', function ($type) { - return Arr::get($type, 'name'); - }); + $properties = $this->propFind($propName, 0, $options); + + if (($prop = Arr::get($properties, $propName)) && is_array($prop)) { + $prop = array_map(function ($supportedReport) { + return $this->iterateOver($supportedReport, '{DAV:}supported-report', function ($report) { + return $this->iterateOver($report, '{DAV:}report', function ($type) { + return Arr::get($type, 'name'); }); - }, $prop); - } + }); + }, $prop); + } - return $prop; - }); + return $prop; } /** @@ -514,11 +432,11 @@ private function iterateOver(array $list, string $name, callable $callback) * * @param string $url * @param array $properties - * @return PromiseInterface + * @return bool * * @see https://datatracker.ietf.org/doc/html/rfc2518#section-12.13 */ - public function propPatchAsync(string $url, array $properties): PromiseInterface + public function propPatch(array $properties, string $url = ''): bool { $propPatch = new PropPatch(); $propPatch->properties = $properties; @@ -527,31 +445,29 @@ public function propPatchAsync(string $url, array $properties): PromiseInterface $propPatch ); - return $this->requestAsync('PROPPATCH', $url, [ - 'Content-Type' => 'application/xml; charset=utf-8', - ], $body)->then(function (ResponseInterface $response): bool { - if ($response->getStatusCode() === 207) { - // If it's a 207, the request could still have failed, but the - // information is hidden in the response body. - $result = self::parseMultiStatus((string) $response->getBody()); - - $errorProperties = []; - foreach ($result as $statusList) { - foreach ($statusList as $status => $properties) { - if ($status >= 400) { - foreach ($properties as $propName => $propValue) { - $errorProperties[] = $propName.' ('.$status.')'; - } + $response = $this->request('PROPPATCH', $url, $body); + + if ($response->status() === 207) { + // If it's a 207, the request could still have failed, but the + // information is hidden in the response body. + $result = self::parseMultiStatus($response->body()); + + $errorProperties = []; + foreach ($result as $statusList) { + foreach ($statusList as $status => $properties) { + if ($status >= 400) { + foreach ($properties as $propName => $propValue) { + $errorProperties[] = $propName.' ('.$status.')'; } } } - if (! empty($errorProperties)) { - throw new DavClientException('PROPPATCH failed. The following properties errored: '.implode(', ', $errorProperties)); - } } + if (! empty($errorProperties)) { + throw new DavClientException('PROPPATCH failed. The following properties errored: '.implode(', ', $errorProperties)); + } + } - return true; - }); + return true; } /** @@ -567,7 +483,7 @@ public function options(): array { $response = $this->request('OPTIONS'); - $dav = $response->getHeader('Dav'); + $dav = Arr::get($response->headers(), 'Dav'); if (! $dav) { return []; } @@ -586,41 +502,22 @@ public function options(): array * @param string $url * @param string|null|resource|\Psr\Http\Message\StreamInterface $body * @param array $headers - * @return ResponseInterface - * - * @throws \GuzzleHttp\Exception\ClientException in case a curl error occurred + * @return Response */ - public function request(string $method, string $url = '', array $headers = [], $body = null, array $options = []): ResponseInterface + public function request(string $method, string $url = '', $body = null, array $headers = [], array $options = []): Response { - return $this->client->send(new Request($method, $url, $headers, $body), $options); - } + $request = $this->getRequest() + ->withHeaders($headers); - /** - * Performs an actual HTTP request, and returns the result. - * - * @param string $method - * @param string $url - * @param string|null|resource|\Psr\Http\Message\StreamInterface $body - * @param array $headers - * @return PromiseInterface - * - * @throws \GuzzleHttp\Exception\ClientException in case a curl error occurred - */ - public function requestAsync(string $method, string $url = '', array $headers = [], $body = null, array $options = []): PromiseInterface - { - return $this->client->sendAsync(new Request($method, $url, $headers, $body), $options); - } + if ($body !== null) { + $request = $request->withBody($body, 'application/xml; charset=utf-8'); + } - /** - * Create multiple request in parallel. - * - * @param array $requests - * @param array $config - * @return PromiseInterface - */ - public function requestPool(array $requests, array $config = []): PromiseInterface - { - return (new Pool($this->client, $requests, $config))->promise(); + $url = Str::startsWith($url, 'http') ? $url : $this->path($url); + + return $request + ->send($method, $url, $options) + ->throw(); } /** diff --git a/app/Services/DavClient/Utils/Dav/ServiceUrlQuery.php b/app/Services/DavClient/Utils/Dav/ServiceUrlQuery.php index 24451168697..373d1c63ce6 100644 --- a/app/Services/DavClient/Utils/Dav/ServiceUrlQuery.php +++ b/app/Services/DavClient/Utils/Dav/ServiceUrlQuery.php @@ -3,8 +3,8 @@ namespace App\Services\DavClient\Utils\Dav; use GuzzleHttp\Psr7\Uri; -use Illuminate\Support\Arr; -use Safe\Exceptions\NetworkException; +use Illuminate\Support\Collection; +use Http\Client\Exception\RequestException; class ServiceUrlQuery { @@ -14,38 +14,68 @@ class ServiceUrlQuery * @return string|null * * @see https://datatracker.ietf.org/doc/html/rfc6352#section-11 + * @see https://datatracker.ietf.org/doc/html/rfc2782 */ - public function execute(string $name, bool $https, string $baseUri): ?string + public function execute(string $name, bool $https, string $baseUri, DavClient $client): ?string { try { $host = \Safe\parse_url($baseUri, PHP_URL_HOST); - $entry = $this->dns_get_record($name.'.'.$host, DNS_SRV); + } catch (\Safe\Exceptions\UrlException $e) { + return null; + } - if ($entry && $target = Arr::get($entry, '0.target')) { - $uri = (new Uri()) - ->withScheme($https ? 'https' : 'http') - ->withPort(Arr::get($entry, '0.port')) - ->withHost($target); + $entries = $this->dns_get_record($name.'.'.$host, DNS_SRV); - return (string) $uri; + if ($entries && $entries->count() > 0) { + $entries = collect($entries) + ->groupBy('pri') + ->sortKeys() + ->first() + ->sortByDesc('weight'); + + foreach ($entries as $entry) { + try { + return $this->getUri($entry, $https, $client); + } catch (RequestException $e) { + // no exception + } } - } catch (\Safe\Exceptions\UrlException $e) { - // catch exception and return null - } catch (\Safe\Exceptions\NetworkException $e) { - // catch exception and return null } return null; } - private function dns_get_record(string $hostname, int $type = DNS_ANY, ?array &$authns = null, ?array &$addtl = null, bool $raw = false): array + /** + * Get uri from entry. + * + * @param array $entry + * @param bool $https + * @param DavClient $client + * @return string + * + * @throws \Http\Client\Exception\RequestException + */ + private function getUri(array $entry, bool $https, DavClient $client): string + { + $uri = (new Uri()) + ->withScheme($https ? 'https' : 'http') + ->withPort($entry['port']) + ->withHost($entry['target']); + + // Test connection + $client->request('GET', $uri); + + return (string) $uri; + } + + private function dns_get_record(string $hostname, int $type = DNS_ANY, ?array &$authns = null, ?array &$addtl = null, bool $raw = false): ?Collection { error_clear_last(); $result = \dns_get_record($hostname, $type, $authns, $addtl, $raw); if ($result === false) { - throw NetworkException::createFromPhpError(); + return null; } - return $result; + return collect($result); } } diff --git a/app/Services/DavClient/Utils/Model/ContactPushDto.php b/app/Services/DavClient/Utils/Model/ContactPushDto.php index e39d74c20b2..3f35ee730eb 100644 --- a/app/Services/DavClient/Utils/Model/ContactPushDto.php +++ b/app/Services/DavClient/Utils/Model/ContactPushDto.php @@ -9,6 +9,12 @@ class ContactPushDto extends ContactUpdateDto */ public $mode; + public const MODE_MATCH_NONE = 0; + + public const MODE_MATCH_ETAG = 1; + + public const MODE_MATCH_ANY = 2; + /** * Create a new ContactPushDto. * @@ -17,7 +23,7 @@ class ContactPushDto extends ContactUpdateDto * @param string|resource $card * @param int $mode */ - public function __construct(string $uri, string $etag, $card, int $mode) + public function __construct(string $uri, string $etag, $card, int $mode = self::MODE_MATCH_NONE) { parent::__construct($uri, $etag, $card); $this->mode = $mode; diff --git a/app/Services/DavClient/Utils/Model/SyncDto.php b/app/Services/DavClient/Utils/Model/SyncDto.php index 4b4e9942a08..9dbf548a30e 100644 --- a/app/Services/DavClient/Utils/Model/SyncDto.php +++ b/app/Services/DavClient/Utils/Model/SyncDto.php @@ -2,12 +2,23 @@ namespace App\Services\DavClient\Utils\Model; +use Illuminate\Support\Traits\Macroable; use App\Models\Account\AddressBookSubscription; use App\Services\DavClient\Utils\Dav\DavClient; use App\Http\Controllers\DAV\Backend\CardDAV\CardDAVBackend; +/** + * @method array propFind($properties, int $depth = 0, array $options = [], string $url = '') + * @method array|string|null getProperty(string $property, string $url = '', array $options = []) + * @method array syncCollection($properties, string $syncToken, array $options = [], string $url = '') + * @method array addressbookQuery($properties, array $options = [], string $url = '') + */ class SyncDto { + use Macroable { + __call as macroCall; + } + /** * @var AddressBookSubscription */ @@ -42,4 +53,21 @@ public function addressBookName(): string { return $this->subscription->addressbook->name; } + + /** + * Execute a method against a new dav client instance. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + if (static::hasMacro($method)) { + return $this->macroCall($method, $parameters); + } + + return $this->subscription->getClient() + ->{$method}(...$parameters); + } } diff --git a/tests/Helpers/DavTester.php b/tests/Helpers/DavTester.php index 70bba46e95a..daf892943d5 100644 --- a/tests/Helpers/DavTester.php +++ b/tests/Helpers/DavTester.php @@ -3,11 +3,11 @@ namespace Tests\Helpers; use Tests\TestCase; -use GuzzleHttp\Client; -use GuzzleHttp\Middleware; -use GuzzleHttp\HandlerStack; -use GuzzleHttp\Psr7\Response; -use GuzzleHttp\Handler\MockHandler; +use Illuminate\Http\Client\Request; +use Illuminate\Http\Client\Response; +use Illuminate\Support\Facades\Http; +use GuzzleHttp\Promise\PromiseInterface; +use App\Services\DavClient\Utils\Dav\DavClient; class DavTester extends TestCase { @@ -16,6 +16,11 @@ class DavTester extends TestCase */ public $responses; + /** + * @var int + */ + private $current; + /** * @var string */ @@ -25,41 +30,61 @@ class DavTester extends TestCase public function __construct(string $baseUri = 'https://test') { + $this->current = 0; $this->baseUri = $baseUri; $this->responses = []; } - public function getClient() + public function client(): DavClient { - $this->container = []; - $history = Middleware::history($this->container); + return (new DavClient())->setBaseUri($this->baseUri); + } - $mock = new MockHandler(array_map(function ($response) { - return $response['response']; - }, $this->responses)); - $handlerStack = HandlerStack::create($mock); - $handlerStack->push($history); + public function fake() + { + Http::fake(function ($request) { + return $this->responses[$this->current++]['response']; + }); - return new Client(['handler' => $handlerStack, 'base_uri' => $this->baseUri]); + return $this; } public function assert() { - $this->assertCount(count($this->responses), $this->container, 'the number of response do not match the number of requests'); - foreach ($this->container as $index => $request) { - $srequest = $request['request']->getMethod().' '.(string) $request['request']->getUri(); - $this->assertEquals($this->responses[$index]['method'], $request['request']->getMethod(), "method for request $srequest differs"); - $this->assertEquals($this->responses[$index]['uri'], (string) $request['request']->getUri(), "uri for request $srequest differs"); - if (isset($this->responses[$index]['body'])) { - $this->assertEquals($this->responses[$index]['body'], (string) $request['request']->getBody(), "body for request $srequest differs"); - } - if (isset($this->responses[$index]['headers'])) { - foreach ($this->responses[$index]['headers'] as $key => $value) { - $this->assertArrayHasKey($key, $request['request']->getHeaders(), "header $key for request $srequest is missing"); - $this->assertEquals($value, $request['request']->getHeaderLine($key), "header $key for request $srequest differs"); + Http::assertSentInOrder(array_map(function ($data) { + return function (Request $request, Response $response) use ($data) { + $srequest = $request->method().' '.$request->url(); + $this->assertEquals($data['method'], $request->method(), "method for request $srequest differs"); + $this->assertEquals($data['uri'], $request->url(), "uri for request $srequest differs"); + if (isset($data['body'])) { + $this->assertEquals($data['body'], $request->body(), "body for request $srequest differs"); + } + if (isset($data['headers'])) { + foreach ($data['headers'] as $key => $value) { + $this->assertArrayHasKey($key, $request->headers(), "header $key for request $srequest is missing"); + $this->assertEquals($value, $request->header($key), "header $key for request $srequest differs"); + } } - } - } + + return true; + }; + }, $this->responses)); + + // $this->assertCount(count($this->responses), $this->container, 'the number of response do not match the number of requests'); + // foreach ($this->container as $index => $request) { + // $srequest = $request->getMethod().' '.(string) $request->getUri(); + // $this->assertEquals($this->responses[$index]['method'], $request->getMethod(), "method for request $srequest differs"); + // $this->assertEquals($this->responses[$index]['uri'], (string) $request->getUri(), "uri for request $srequest differs"); + // if (isset($this->responses[$index]['body'])) { + // $this->assertEquals($this->responses[$index]['body'], (string) $request->getBody(), "body for request $srequest differs"); + // } + // if (isset($this->responses[$index]['headers'])) { + // foreach ($this->responses[$index]['headers'] as $key => $value) { + // $this->assertArrayHasKey($key, $request->getHeaders(), "header $key for request $srequest is missing"); + // $this->assertEquals($value, $request->getHeaderLine($key), "header $key for request $srequest differs"); + // } + // } + // } } public function addressBookBaseUri() @@ -77,7 +102,7 @@ public function capabilities() ->supportedAddressData(); } - public function addResponse(string $uri, Response $response, string $body = null, string $method = 'PROPFIND', array $headers = null) + public function addResponse(string $uri, PromiseInterface $response, string $body = null, string $method = 'PROPFIND', array $headers = null) { $this->responses[] = [ 'uri' => $uri, @@ -92,35 +117,27 @@ public function addResponse(string $uri, Response $response, string $body = null public function serviceUrl() { - $this->addResponse('https://test/.well-known/carddav', new Response(301, ['Location' => $this->baseUri.'/dav/']), null, 'GET'); - - return $this; + return $this->addResponse('https://test/.well-known/carddav', Http::response(null, 301, ['Location' => $this->baseUri.'/dav/']), null, 'GET'); } public function nonStandardServiceUrl() { - $this->addResponse('https://test/.well-known/carddav', new Response(301, ['Location' => '/dav/'])); - - return $this; + return $this->addResponse('https://test/.well-known/carddav', Http::response(null, 301, ['Location' => '/dav/']), null, 'PROPFIND'); } public function optionsOk() { - $this->addResponse('https://test/dav/', new Response(200, ['Dav' => '1, 3, addressbook']), null, 'OPTIONS'); - - return $this; + return $this->addResponse('https://test/dav/', Http::response(null, 200, ['Dav' => '1, 3, addressbook']), null, 'OPTIONS'); } public function optionsFail() { - $this->addResponse('https://test/dav/', new Response(200, ['Dav' => 'bad']), null, 'OPTIONS'); - - return $this; + return $this->addResponse('https://test/dav/', Http::response(null, 200, ['Dav' => 'bad']), null, 'OPTIONS'); } public function userPrincipal() { - $this->addResponse('https://test/dav/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/', Http::response($this->multistatusHeader(). ''. '/dav/'. ''. @@ -133,13 +150,11 @@ public function userPrincipal() ''. ''. '')); - - return $this; } public function userPrincipalEmpty() { - $this->addResponse('https://test/dav/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/', Http::response($this->multistatusHeader(). ''. '/dav/'. ''. @@ -150,13 +165,11 @@ public function userPrincipalEmpty() ''. ''. '')); - - return $this; } public function addressbookHome() { - $this->addResponse('https://test/dav/principals/user@test.com/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/principals/user@test.com/', Http::response($this->multistatusHeader(). ''. '/dav/principals/user@test.com/'. ''. @@ -169,13 +182,11 @@ public function addressbookHome() ''. ''. '')); - - return $this; } public function addressbookEmpty() { - $this->addResponse('https://test/dav/principals/user@test.com/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/principals/user@test.com/', Http::response($this->multistatusHeader(). ''. '/dav/principals/user@test.com/'. ''. @@ -186,13 +197,11 @@ public function addressbookEmpty() ''. ''. '')); - - return $this; } public function resourceTypeAddressBook() { - $this->addResponse('https://test/dav/addressbooks/user@test.com/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -205,13 +214,11 @@ public function resourceTypeAddressBook() ''. ''. '')); - - return $this; } public function resourceTypeHomeOnly() { - $this->addResponse('https://test/dav/addressbooks/user@test.com/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/'. ''. @@ -222,13 +229,11 @@ public function resourceTypeHomeOnly() ''. ''. '')); - - return $this; } public function resourceTypeEmpty() { - $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -239,13 +244,11 @@ public function resourceTypeEmpty() ''. ''. '')); - - return $this; } public function supportedReportSet(array $reportSet = ['card:addressbook-multiget', 'card:addressbook-query', 'd:sync-collection']) { - $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -260,13 +263,11 @@ public function supportedReportSet(array $reportSet = ['card:addressbook-multige ''. ''. '')); - - return $this; } public function supportedAddressData(array $list = ['card:address-data-type content-type="text/vcard" version="4.0"']) { - $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -281,13 +282,11 @@ public function supportedAddressData(array $list = ['card:address-data-type cont ''. ''. '')); - - return $this; } public function displayName(string $name = 'Test') { - $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -298,13 +297,11 @@ public function displayName(string $name = 'Test') ''. ''. '')); - - return $this; } public function getSynctoken(string $synctoken = '"test"') { - $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. '/dav/addressbooks/user@test.com/contacts/'. ''. @@ -315,13 +312,11 @@ public function getSynctoken(string $synctoken = '"test"') ''. ''. '')); - - return $this; } public function getSyncCollection(string $synctoken = 'token', string $etag = '"etag"') { - return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. 'https://test/dav/addressbooks/user@test.com/contacts/uuid'. ''. @@ -338,7 +333,7 @@ public function getSyncCollection(string $synctoken = 'token', string $etag = '" public function addressMultiGet($etag, $card, $url) { - return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $this->multistatusHeader(). + return $this->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response($this->multistatusHeader(). ''. 'https://test/dav/addressbooks/user@test.com/contacts/uuid'. ''. diff --git a/tests/Unit/Jobs/Dav/GetMultipleVCardTest.php b/tests/Unit/Jobs/Dav/GetMultipleVCardTest.php index f60acbc3c08..b05122d657e 100644 --- a/tests/Unit/Jobs/Dav/GetMultipleVCardTest.php +++ b/tests/Unit/Jobs/Dav/GetMultipleVCardTest.php @@ -43,9 +43,11 @@ public function it_get_cards() $etag = $this->getEtag($contact, true); $this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) { + $mock->shouldReceive('setBaseUri')->once()->andReturn($mock); + $mock->shouldReceive('setCredentials')->once()->andReturn($mock); $mock->shouldReceive('addressbookMultiget') ->once() - ->withArgs(function ($request, $properties, $contacts) { + ->withArgs(function ($properties, $contacts) { $this->assertEquals([ '{DAV:}getetag', [ @@ -117,9 +119,11 @@ public function it_get_cards_mock_http() $etag = $this->getEtag($contact, true); $this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) { + $mock->shouldReceive('setBaseUri')->once()->andReturn($mock); + $mock->shouldReceive('setCredentials')->once()->andReturn($mock); $mock->shouldReceive('addressbookMultiget') ->once() - ->withArgs(function ($request, $properties, $contacts) { + ->withArgs(function ($properties, $contacts) { $this->assertEquals([ '{DAV:}getetag', [ diff --git a/tests/Unit/Jobs/Dav/PushVCardTest.php b/tests/Unit/Jobs/Dav/PushVCardTest.php index 5968d2e1470..9a97d029cfe 100644 --- a/tests/Unit/Jobs/Dav/PushVCardTest.php +++ b/tests/Unit/Jobs/Dav/PushVCardTest.php @@ -59,7 +59,7 @@ public function it_push_card($mode, $ifmatch) }); $pendingBatch = $fake->batch([ - $job = new PushVCard($addressBookSubscription, new ContactPushDto('uri', $etag, $card, $mode)), + $job = new PushVCard($addressBookSubscription, new ContactPushDto('https://test/dav/uri', $etag, $card, $mode)), ]); $batch = $pendingBatch->dispatch(); diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php index 97e7e4e51d0..3d7a600ee3d 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushMissedTest.php @@ -10,7 +10,6 @@ use App\Models\User\SyncToken; use App\Models\Contact\Contact; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\SyncDto; use App\Services\DavClient\Utils\Model\ContactDto; use App\Services\DavClient\Utils\Model\ContactPushDto; @@ -68,8 +67,7 @@ public function it_push_contacts_missed() ]); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsPushMissed()) ->execute(new SyncDto($subscription, $client, $backend), [], collect([ diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php index 095bc05f9f8..3ca04693fd5 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsPushTest.php @@ -11,7 +11,6 @@ use App\Models\User\SyncToken; use App\Models\Contact\Contact; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\SyncDto; use App\Services\DavClient\Utils\Model\ContactDto; use App\Services\DavClient\Utils\Model\ContactPushDto; @@ -66,8 +65,7 @@ public function it_push_contacts_added() ->andReturn('uricontact1'); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsPush()) ->execute(new SyncDto($subscription, $client, $backend), collect([ @@ -82,7 +80,7 @@ public function it_push_contacts_added() $dto = $this->getPrivateValue($batch, 'contact'); $this->assertInstanceOf(ContactPushDto::class, $dto); $this->assertEquals('uricontact2', $dto->uri); - $this->assertEquals(0, $dto->mode); + $this->assertEquals(ContactPushDto::MODE_MATCH_NONE, $dto->mode); } /** @test */ @@ -129,8 +127,7 @@ public function it_push_contacts_modified() ]); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsPush()) ->execute(new SyncDto($subscription, $client, $backend), collect([ diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterMissedTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterMissedTest.php index d774f9a856b..e2e28c86359 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterMissedTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterMissedTest.php @@ -10,7 +10,6 @@ use App\Models\Contact\Contact; use App\Jobs\Dav\GetMultipleVCard; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\SyncDto; use App\Services\DavClient\Utils\Model\ContactDto; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -62,8 +61,7 @@ public function it_sync_changes_missed() ->andReturn($etag); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsUpdaterMissed()) ->execute(new SyncDto($subscription, $client, $backend), collect([ diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterTest.php index 74442617b4f..a5854831703 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookContactsUpdaterTest.php @@ -11,7 +11,6 @@ use App\Models\Contact\Contact; use App\Jobs\Dav\GetMultipleVCard; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\SyncDto; use App\Services\DavClient\Utils\Model\ContactDto; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -56,8 +55,7 @@ public function it_sync_changes_multiget() ->andReturn($etag); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsUpdater()) ->execute(new SyncDto($subscription, $client, $backend), collect([ @@ -123,8 +121,7 @@ public function it_sync_changes_simple() ->andReturn($etag); }); - $tester = new DavTester(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = (new DavTester())->fake()->client(); $batchs = (new AddressBookContactsUpdater()) ->execute(new SyncDto($subscription, $client, $backend), collect([ diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookGetterTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookGetterTest.php index 9fc5b2c4468..2b971391201 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookGetterTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookGetterTest.php @@ -4,7 +4,6 @@ use Tests\TestCase; use Tests\Helpers\DavTester; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\AddressBookGetter; use Illuminate\Foundation\Testing\DatabaseTransactions; use App\Services\DavClient\Utils\Dav\DavClientException; @@ -20,8 +19,9 @@ public function it_get_address_book_data() $tester = (new DavTester()) ->addressBookBaseUri() ->capabilities() - ->displayName(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->displayName() + ->fake(); + $client = $tester->client(); $result = (new AddressBookGetter()) ->execute($client); @@ -46,8 +46,9 @@ public function it_fails_on_server_not_compliant() { $tester = (new DavTester()) ->serviceUrl() - ->optionsFail(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->optionsFail() + ->fake(); + $client = $tester->client(); $this->expectException(DavServerNotCompliantException::class); (new AddressBookGetter()) @@ -60,9 +61,9 @@ public function it_fails_if_no_userprincipal() $tester = (new DavTester()) ->serviceUrl() ->optionsOk() - ->userPrincipalEmpty(); - - $client = app(DavClient::class)->init([], $tester->getClient()); + ->userPrincipalEmpty() + ->fake(); + $client = $tester->client(); $this->expectException(DavServerNotCompliantException::class); (new AddressBookGetter()) @@ -76,9 +77,9 @@ public function it_fails_if_no_addressbook() ->serviceUrl() ->optionsOk() ->userPrincipal() - ->addressbookEmpty(); - - $client = app(DavClient::class)->init([], $tester->getClient()); + ->addressbookEmpty() + ->fake(); + $client = $tester->client(); $this->expectException(DavServerNotCompliantException::class); (new AddressBookGetter()) @@ -93,8 +94,9 @@ public function it_fails_if_no_addressbook_url() ->optionsOk() ->userPrincipal() ->addressbookHome() - ->resourceTypeHomeOnly(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->resourceTypeHomeOnly() + ->fake(); + $client = $tester->client(); $this->expectException(DavClientException::class); (new AddressBookGetter()) diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php index 2bf7118f417..98544db19b6 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php @@ -6,14 +6,13 @@ use Mockery\MockInterface; use Tests\Api\DAV\CardEtag; use Tests\Helpers\DavTester; -use GuzzleHttp\Psr7\Response; use App\Models\User\SyncToken; use App\Models\Contact\Contact; use Illuminate\Bus\PendingBatch; use App\Jobs\Dav\GetMultipleVCard; use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Http; use App\Models\Account\AddressBookSubscription; -use App\Services\DavClient\Utils\Dav\DavClient; use App\Services\DavClient\Utils\Model\SyncDto; use Illuminate\Foundation\Testing\DatabaseTransactions; use App\Services\DavClient\Utils\AddressBookSynchronizer; @@ -42,8 +41,9 @@ public function it_sync_empty_changes() $backend = new CardDAVBackend($subscription->user); $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')) - ->getSynctoken($subscription->syncToken); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->getSynctoken($subscription->syncToken) + ->fake(); + $client = $tester->client(); (new AddressBookSynchronizer()) ->execute(new SyncDto($subscription, $client, $backend)); @@ -67,9 +67,10 @@ public function it_sync_no_changes() $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')); $tester->getSynctoken('"test21"') - ->getSyncCollection('test20'); + ->getSyncCollection('test20') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); (new AddressBookSynchronizer()) ->execute(new SyncDto($subscription, $client, $backend)); @@ -93,9 +94,10 @@ public function it_sync_changes_added_local_contact() $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')); $tester->getSynctoken('"token"') - ->getSyncCollection('token', '"test2"'); + ->getSyncCollection('token', '"test2"') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $sync = new SyncDto($subscription, $client, $backend); $this->mock(AddressBookContactsUpdater::class, function (MockInterface $mock) use ($sync) { @@ -133,9 +135,10 @@ public function it_sync_changes_added_local_contact_batched() $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')); $tester->getSynctoken('"token"') - ->getSyncCollection('token', '"test2"'); + ->getSyncCollection('token', '"test2"') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $sync = new SyncDto($subscription, $client, $backend); @@ -169,8 +172,9 @@ public function it_forcesync_changes_added_local_contact() ]); $etag = $this->getEtag($contact, true); - $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')); - $tester->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')) + ->fake(); + $tester->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response(DavTester::multistatusHeader(). ''. 'https://test/dav/uuid1'. ''. @@ -187,7 +191,7 @@ public function it_forcesync_changes_added_local_contact() ''. "\n", 'REPORT'); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $sync = new SyncDto($subscription, $client, $backend); $this->mock(AddressBookContactsUpdaterMissed::class, function (MockInterface $mock) use ($sync, $contact, $etag) { @@ -230,8 +234,9 @@ public function it_forcesync_changes_added_local_contact_batched() ]); $etag = $this->getEtag($contact, true); - $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')); - $tester->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/')) + ->fake(); + $tester->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response(DavTester::multistatusHeader(). ''. 'https://test/dav/uuid1'. ''. @@ -248,7 +253,7 @@ public function it_forcesync_changes_added_local_contact_batched() ''. "\n", 'REPORT'); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $sync = new SyncDto($subscription, $client, $backend); @@ -269,7 +274,9 @@ public function it_forcesync_changes_added_local_contact_batched() private function getSubscription() { - $subscription = AddressBookSubscription::factory()->create(); + $subscription = AddressBookSubscription::factory()->create([ + 'uri' => 'https://test/dav/addressbooks/user@test.com/contacts/', + ]); $token = factory(SyncToken::class)->create([ 'account_id' => $subscription->account_id, 'user_id' => $subscription->user_id, diff --git a/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php b/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php index 0a697b4e189..76235630509 100644 --- a/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php +++ b/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php @@ -4,11 +4,8 @@ use Tests\TestCase; use Tests\Helpers\DavTester; -use GuzzleHttp\Psr7\Response; -use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; -use GuzzleHttp\Exception\ServerException; -use App\Services\DavClient\Utils\Dav\DavClient; +use Illuminate\Http\Client\RequestException; use Illuminate\Foundation\Testing\DatabaseTransactions; use App\Services\DavClient\Utils\Dav\DavClientException; @@ -16,28 +13,15 @@ class DavClientTest extends TestCase { use DatabaseTransactions; - /** @test */ - public function it_fails_if_no_baseuri() - { - $this->expectException(\InvalidArgumentException::class); - app(DavClient::class)->init([]); - } - - /** @test */ - public function it_accept_guzzle_client() - { - $client = app(DavClient::class)->init([], new \GuzzleHttp\Client()); - $this->assertInstanceOf(DavClient::class, $client); - } - /** @test */ public function it_get_options() { $tester = (new DavTester()) - ->addResponse('https://test', new Response(200, []), null, 'OPTIONS') - ->addResponse('https://test', new Response(200, ['Dav' => 'test']), null, 'OPTIONS') - ->addResponse('https://test', new Response(200, ['Dav' => ' test ']), null, 'OPTIONS'); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->addResponse('https://test', Http::response(), null, 'OPTIONS') + ->addResponse('https://test', Http::response(null, 200, ['Dav' => 'test']), null, 'OPTIONS') + ->addResponse('https://test', Http::response(null, 200, ['Dav' => ' test ']), null, 'OPTIONS') + ->fake(); + $client = $tester->client(); $result = $client->options(); $this->assertEquals([], $result); @@ -55,8 +39,9 @@ public function it_get_options() public function it_get_serviceurl() { $tester = (new DavTester()) - ->serviceUrl(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->serviceUrl() + ->fake(); + $client = $tester->client(); $result = $client->getServiceUrl(); @@ -68,9 +53,11 @@ public function it_get_serviceurl() public function it_get_non_standard_serviceurl() { $tester = (new DavTester()) - ->addResponse('https://test/.well-known/carddav', new Response(200), null, 'GET') - ->nonStandardServiceUrl(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->addResponse('https://test/.well-known/carddav', Http::response(), null, 'GET') + ->addResponse('https://test/.well-known/carddav', Http::response(), null, 'GET') + ->nonStandardServiceUrl() + ->fake(); + $client = $tester->client(); $result = $client->getServiceUrl(); @@ -82,9 +69,11 @@ public function it_get_non_standard_serviceurl() public function it_get_non_standard_serviceurl2() { $tester = (new DavTester()) - ->addResponse('https://test/.well-known/carddav', new Response(404), null, 'GET') - ->nonStandardServiceUrl(); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->addResponse('https://test/.well-known/carddav', Http::response(null, 404), null, 'GET') + ->addResponse('https://test/.well-known/carddav', Http::response(null, 404), null, 'GET') + ->nonStandardServiceUrl() + ->fake(); + $client = $tester->client(); $result = $client->getServiceUrl(); @@ -96,24 +85,26 @@ public function it_get_non_standard_serviceurl2() public function it_fail_non_standard() { $tester = (new DavTester()) - ->addResponse('https://test/.well-known/carddav', new Response(500), null, 'GET'); - $client = app(DavClient::class)->init([], $tester->getClient()); + ->addResponse('https://test/.well-known/carddav', Http::response(null, 500), null, 'GET') + ->fake(); + $client = $tester->client(); - $this->expectException(ServerException::class); + $this->expectException(RequestException::class); $client->getServiceUrl(); } /** @test */ public function it_get_base_uri() { - $tester = (new DavTester()); - $client = app(DavClient::class)->init([], $tester->getClient()); + $tester = (new DavTester()) + ->fake(); + $client = $tester->client(); - $result = $client->getBaseUri(); + $result = $client->path(); $this->assertEquals('https://test', $result); - $result = $client->getBaseUri('xxx'); + $result = $client->path('xxx'); $this->assertEquals('https://test/xxx', $result); } @@ -121,11 +112,12 @@ public function it_get_base_uri() /** @test */ public function it_set_base_uri() { - $tester = (new DavTester()); - $client = app(DavClient::class)->init([], $tester->getClient()); + $tester = (new DavTester()) + ->fake(); + $client = $tester->client(); $result = $client->setBaseUri('https://new') - ->getBaseUri(); + ->path(); $this->assertEquals('https://new', $result); } @@ -133,9 +125,8 @@ public function it_set_base_uri() /** @test */ public function it_call_propfind() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -150,11 +141,12 @@ public function it_call_propfind() ''. ''. ''. - "\n", 'PROPFIND'); + "\n", 'PROPFIND') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); - $result = $client->propFind('https://test/test', ['{DAV:}test']); + $result = $client->propFind(['{DAV:}test']); $tester->assert(); $this->assertEquals([ @@ -165,9 +157,8 @@ public function it_call_propfind() /** @test */ public function it_get_property() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test/test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -182,9 +173,10 @@ public function it_get_property() ''. ''. ''. - "\n", 'PROPFIND'); + "\n", 'PROPFIND') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $result = $client->getProperty('{DAV:}test', 'https://test/test'); @@ -195,9 +187,8 @@ public function it_get_property() /** @test */ public function it_get_supported_report() { - $tester = (new DavTester('https://test/dav')); - - $tester->addResponse('https://test/dav', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester('https://test/dav')) + ->addResponse('https://test/dav', Http::response(DavTester::multistatusHeader(). ''. '/dav'. ''. @@ -219,9 +210,10 @@ public function it_get_supported_report() ''. ''. ''. - "\n", 'PROPFIND'); + "\n", 'PROPFIND') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $result = $client->getSupportedReportSet(); @@ -232,9 +224,8 @@ public function it_get_supported_report() /** @test */ public function it_sync_collection() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -253,12 +244,12 @@ public function it_sync_collection() ''. ''. ''. - "\n", 'REPORT'); + "\n", 'REPORT') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); - $result = $client->syncCollectionAsync('https://test/test', ['{DAV:}test'], '') - ->wait(); + $result = $client->syncCollection(['{DAV:}test'], ''); $tester->assert(); $this->assertEquals([ @@ -275,9 +266,8 @@ public function it_sync_collection() /** @test */ public function it_sync_collection_with_synctoken() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -296,12 +286,12 @@ public function it_sync_collection_with_synctoken() ''. ''. ''. - "\n", 'REPORT'); + "\n", 'REPORT') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); - $result = $client->syncCollectionAsync('https://test/test', ['{DAV:}test'], '"00000-abcd0"') - ->wait(); + $result = $client->syncCollection(['{DAV:}test'], '"00000-abcd0"'); $tester->assert(); $this->assertEquals([ @@ -318,8 +308,8 @@ public function it_sync_collection_with_synctoken() /** @test */ public function it_run_addressbook_multiget_report() { - Http::fake([ - 'https://test/*' => Http::response(DavTester::multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -330,26 +320,18 @@ public function it_run_addressbook_multiget_report() 'HTTP/1.1 200 OK'. ''. ''. - ''), - ]); - - $result = app(DavClient::class)->addressbookMultiget(Http::baseUrl('https://test/test'), ['{DAV:}test'], [ - 'https://test/contacts/1', - ]); + ''), ''."\n". + ''. + ''. + ''. + ''. + 'https://test/contacts/1'. + "\n", 'REPORT') + ->fake(); - Http::assertSent(function (Request $request) { - $this->assertEquals('https://test/test/', $request->url()); - $this->assertEquals('REPORT', $request->method()); - $this->assertEquals(''."\n". - ''. - ''. - ''. - ''. - 'https://test/contacts/1'. - "\n", $request->body()); + $client = $tester->client(); - return true; - }); + $result = $client->addressbookMultiget(['{DAV:}test'], ['https://test/contacts/1']); $this->assertEquals([ 'href' => [ @@ -359,14 +341,15 @@ public function it_run_addressbook_multiget_report() ], ], ], $result); + + $tester->assert(); } /** @test */ public function it_run_addressbook_query_report() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -382,12 +365,12 @@ public function it_run_addressbook_query_report() ''. ''. ''. - "\n", 'REPORT'); + "\n", 'REPORT') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); - $result = $client->addressbookQueryAsync('https://test/test', ['{DAV:}test']) - ->wait(); + $result = $client->addressbookQuery(['{DAV:}test']); $tester->assert(); $this->assertEquals([ @@ -403,9 +386,8 @@ public function it_run_addressbook_query_report() /** @test */ public function it_run_proppatch() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(207, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -415,19 +397,19 @@ public function it_run_proppatch() 'HTTP/1.1 200 OK'. ''. ''. - ''), ''."\n". + '', 207), ''."\n". ''."\n". ' '."\n". ' '."\n". ' value'."\n". ' '."\n". ' '."\n". - "\n", 'PROPPATCH'); + "\n", 'PROPPATCH') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); - $result = $client->propPatchAsync('https://test/test', ['{DAV:}test' => 'value']) - ->wait(); + $result = $client->propPatch(['{DAV:}test' => 'value']); $tester->assert(); $this->assertTrue($result); @@ -436,9 +418,8 @@ public function it_run_proppatch() /** @test */ public function it_run_proppatch_error() { - $tester = (new DavTester()); - - $tester->addResponse('https://test/test', new Response(207, [], $tester->multistatusHeader(). + $tester = (new DavTester()) + ->addResponse('https://test', Http::response(DavTester::multistatusHeader(). ''. 'href'. ''. @@ -454,7 +435,7 @@ public function it_run_proppatch_error() 'HTTP/1.1 500 OK'. ''. ''. - ''), ''."\n". + '', 207), ''."\n". ''."\n". ' '."\n". ' '."\n". @@ -462,16 +443,16 @@ public function it_run_proppatch_error() ' value'."\n". ' '."\n". ' '."\n". - "\n", 'PROPPATCH'); + "\n", 'PROPPATCH') + ->fake(); - $client = app(DavClient::class)->init([], $tester->getClient()); + $client = $tester->client(); $this->expectException(DavClientException::class); $this->expectExceptionMessage('PROPPATCH failed. The following properties errored: {DAV:}test (405), {DAV:}excerpt (500)'); - $client->propPatchAsync('https://test/test', [ + $client->propPatch([ '{DAV:}test' => 'value', '{DAV:}excerpt' => 'value', - ]) - ->wait(); + ]); } }