refactor: use bus batch in davclient (#5542)
This commit is contained in:
@@ -41,6 +41,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$this->scheduleCommand($schedule, 'queue:prune-batches', 'daily');
|
||||
$this->scheduleCommand($schedule, 'send:reminders', 'hourly');
|
||||
$this->scheduleCommand($schedule, 'send:stay_in_touch', 'hourly');
|
||||
$this->scheduleCommand($schedule, 'monica:davclients', 'hourly');
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Dav;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Bus\Batchable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
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
|
||||
{
|
||||
use Batchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var AddressBookSubscription
|
||||
*/
|
||||
private $subscription;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $hrefs;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param AddressBookSubscription $subscription
|
||||
* @param array $hrefs
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AddressBookSubscription $subscription, array $hrefs)
|
||||
{
|
||||
$this->subscription = $subscription->withoutRelations();
|
||||
$this->hrefs = $hrefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Last Consulted At field for the given contact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
if (! $this->batching()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$datas = app(DavClient::class)->addressbookMultiget($this->subscription->getRequest(), [
|
||||
'{DAV:}getetag',
|
||||
$this->getAddressDataProperty(),
|
||||
], $this->hrefs);
|
||||
|
||||
collect($datas)
|
||||
->filter(function (array $contact): bool {
|
||||
return isset($contact[200]);
|
||||
})
|
||||
->each(function (array $contact, $href) {
|
||||
$this->updateVCard($contact, $href);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the contact.
|
||||
*
|
||||
* @param array $contact
|
||||
* @param string $href
|
||||
* @return void
|
||||
*/
|
||||
private function updateVCard(array $contact, $href): void
|
||||
{
|
||||
$card = Arr::get($contact, '200.{'.CardDAVPlugin::NS_CARDDAV.'}address-data');
|
||||
|
||||
if ($card !== null) {
|
||||
$dto = new ContactUpdateDto($href, Arr::get($contact, '200.{DAV:}getetag'), $card);
|
||||
|
||||
if (($batch = $this->batch()) !== null) {
|
||||
$batch->add([
|
||||
new UpdateVCard($this->subscription->user, $this->subscription->addressbook->name, $dto),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for address-data property.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAddressDataProperty(): array
|
||||
{
|
||||
$addressDataAttributes = Arr::get($this->subscription->capabilities, 'addressData', [
|
||||
'content-type' => 'text/vcard',
|
||||
'version' => '4.0',
|
||||
]);
|
||||
|
||||
return [
|
||||
'name' => '{'.CardDAVPlugin::NS_CARDDAV.'}address-data',
|
||||
'value' => null,
|
||||
'attributes' => $addressDataAttributes,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Dav;
|
||||
|
||||
use Illuminate\Bus\Batchable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Model\ContactDto;
|
||||
use App\Services\DavClient\Utils\Model\ContactUpdateDto;
|
||||
|
||||
class GetVCard implements ShouldQueue
|
||||
{
|
||||
use Batchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var AddressBookSubscription
|
||||
*/
|
||||
private $subscription;
|
||||
|
||||
/**
|
||||
* @var ContactDto
|
||||
*/
|
||||
private $contact;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param AddressBookSubscription $subscription
|
||||
* @param ContactDto $contact
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AddressBookSubscription $subscription, ContactDto $contact)
|
||||
{
|
||||
$this->subscription = $subscription->withoutRelations();
|
||||
$this->contact = $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Last Consulted At field for the given contact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
if (! $this->batching()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info(__CLASS__.' '.$this->contact->uri);
|
||||
|
||||
$response = $this->subscription->getRequest()
|
||||
->get($this->contact->uri);
|
||||
|
||||
$response->throw();
|
||||
|
||||
$this->chainUpdateVCard($response->body());
|
||||
}
|
||||
|
||||
private function chainUpdateVCard(string $card): void
|
||||
{
|
||||
$dto = new ContactUpdateDto($this->contact->uri, $this->contact->etag, $card);
|
||||
|
||||
if (($batch = $this->batch()) !== null) {
|
||||
$batch->add([
|
||||
new UpdateVCard($this->subscription->user, $this->subscription->addressbook->name, $dto),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Dav;
|
||||
|
||||
use Illuminate\Bus\Batchable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Model\ContactPushDto;
|
||||
|
||||
class PushVCard implements ShouldQueue
|
||||
{
|
||||
use Batchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var AddressBookSubscription
|
||||
*/
|
||||
private $subscription;
|
||||
|
||||
/**
|
||||
* @var ContactPushDto
|
||||
*/
|
||||
private $contact;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param AddressBookSubscription $subscription
|
||||
* @param ContactPushDto $contact
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(AddressBookSubscription $subscription, ContactPushDto $contact)
|
||||
{
|
||||
$this->subscription = $subscription->withoutRelations();
|
||||
$this->contact = $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Last Consulted At field for the given contact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
if (! $this->batching()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info(__CLASS__.' '.$this->contact->uri);
|
||||
|
||||
$headers = [];
|
||||
|
||||
if ($this->contact->mode === 1) {
|
||||
$headers['If-Match'] = $this->contact->etag;
|
||||
} elseif ($this->contact->mode === 2) {
|
||||
$headers['If-Match'] = '*';
|
||||
}
|
||||
|
||||
$response = $this->subscription->getRequest()
|
||||
->withHeaders($headers)
|
||||
->put($this->contact->uri, [$this->contact->card]);
|
||||
|
||||
$response->throw();
|
||||
|
||||
if (! empty($etag = $response->header('Etag')) && $etag !== $this->contact->etag) {
|
||||
Log::warning(__CLASS__.' wrong etag when updating contact. Expected '.$this->contact->etag.', get '.$etag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Dav;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Bus\Batchable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\Services\DavClient\Utils\Model\ContactUpdateDto;
|
||||
use App\Http\Controllers\DAV\Backend\CardDAV\CardDAVBackend;
|
||||
|
||||
class UpdateVCard implements ShouldQueue
|
||||
{
|
||||
use Batchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $addressBookName;
|
||||
|
||||
/**
|
||||
* @var ContactUpdateDto
|
||||
*/
|
||||
private $contact;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $addressBookName
|
||||
* @param ContactUpdateDto $contact
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, string $addressBookName, ContactUpdateDto $contact)
|
||||
{
|
||||
$this->user = $user->withoutRelations();
|
||||
$this->addressBookName = $addressBookName;
|
||||
$this->contact = $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Last Consulted At field for the given contact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
if (! $this->batching()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info(__CLASS__.' update '.$this->contact->uri);
|
||||
|
||||
$backend = new CardDAVBackend($this->user);
|
||||
$newtag = $backend->updateCard($this->addressBookName, $this->contact->uri, $this->contact->card);
|
||||
|
||||
if ($newtag !== $this->contact->etag) {
|
||||
Log::warning(__CLASS__.' wrong etag when updating contact. Expected '.$this->contact->etag.', get '.$newtag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,10 @@ namespace App\Models\Account;
|
||||
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 Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
@@ -149,4 +151,16 @@ class AddressBookSubscription extends Model
|
||||
{
|
||||
return $query->where('active', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pending request.
|
||||
*
|
||||
* @return PendingRequest
|
||||
*/
|
||||
public function getRequest(): PendingRequest
|
||||
{
|
||||
return Http::withBasicAuth($this->username, $this->password)
|
||||
->baseUrl($this->uri)
|
||||
->withUserAgent('Monica DavClient '.config('monica.app_version'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,6 @@ class CreateAddressBookSubscription extends BaseService
|
||||
'password',
|
||||
]);
|
||||
|
||||
return new DavClient($settings, $client);
|
||||
return app(DavClient::class)->init($settings, $client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +78,11 @@ class SynchronizeAddressBook extends BaseService
|
||||
|
||||
private function getDavClient(AddressBookSubscription $subscription, ?GuzzleClient $client): DavClient
|
||||
{
|
||||
return new DavClient([
|
||||
'base_uri' => $subscription->uri,
|
||||
'username' => $subscription->username,
|
||||
'password' => $subscription->password,
|
||||
], $client);
|
||||
return app(DavClient::class)
|
||||
->init([
|
||||
'base_uri' => $subscription->uri,
|
||||
'username' => $subscription->username,
|
||||
'password' => $subscription->password,
|
||||
], $client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use App\Jobs\Dav\PushVCard;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use IlluminateAgnostic\Collection\Support\Arr;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
use App\Services\DavClient\Utils\Model\ContactDto;
|
||||
@@ -25,72 +22,37 @@ class AddressBookContactsPush
|
||||
* @param SyncDto $sync
|
||||
* @param Collection<array-key, ContactDto> $changes
|
||||
* @param array<array-key, string>|null $localChanges
|
||||
* @param Collection<array-key, ContactPushDto>|null $missed
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
public function execute(SyncDto $sync, Collection $changes, ?array $localChanges, ?Collection $missed = null): PromiseInterface
|
||||
public function execute(SyncDto $sync, Collection $changes, ?array $localChanges): Collection
|
||||
{
|
||||
$this->sync = $sync;
|
||||
|
||||
$commands = $this->preparePushChanges($changes, $localChanges, $missed)
|
||||
->filter(function ($command) {
|
||||
return $command !== null;
|
||||
$changes = $this->preparePushChangedContacts($changes, Arr::get($localChanges, 'modified', []));
|
||||
$added = $this->preparePushAddedContacts(Arr::get($localChanges, 'added', []));
|
||||
|
||||
return $changes->union($added)
|
||||
->filter(function ($c) {
|
||||
return $c !== null;
|
||||
});
|
||||
|
||||
$requests = $commands->pluck('request')->toArray();
|
||||
|
||||
return $this->sync->client->requestPool($requests, [
|
||||
'concurrency' => 25,
|
||||
'fulfilled' => function (ResponseInterface $response, int $index) use ($commands) {
|
||||
/** @var ContactPushDto $command */
|
||||
$command = $commands[$index];
|
||||
|
||||
Log::info(__CLASS__.' pushContacts: PUT '.$command->uri);
|
||||
|
||||
$etags = $response->getHeader('Etag');
|
||||
if (! empty($etags) && $etags[0] !== $command->etag) {
|
||||
Log::warning(__CLASS__.' pushContacts: wrong etag when updating contact. Expected '.$command->etag.', get '.$etags[0]);
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of requests to push contacts that have changed.
|
||||
*
|
||||
* @param Collection<array-key, ContactDto> $changes
|
||||
* @param array<array-key, string>|null $localChanges
|
||||
* @param Collection<array-key, ContactPushDto>|null $missed
|
||||
* @return Collection<mixed, ?ContactPushDto>
|
||||
*/
|
||||
private function preparePushChanges(Collection $changes, ?array $localChanges, ?Collection $missed = null): Collection
|
||||
{
|
||||
$requestsChanges = $this->preparePushChangedContacts($changes, Arr::get($localChanges, 'modified', []));
|
||||
$requestsAdded = $this->preparePushAddedContacts(Arr::get($localChanges, 'added', []));
|
||||
|
||||
return $requestsChanges
|
||||
->union($requestsAdded)
|
||||
->union($missed ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of requests to push new contacts.
|
||||
*
|
||||
* @param array $contacts
|
||||
* @return Collection<mixed, ?ContactPushDto>
|
||||
* @return Collection
|
||||
*/
|
||||
private function preparePushAddedContacts(array $contacts): Collection
|
||||
{
|
||||
// All added contact must be pushed
|
||||
return collect($contacts)
|
||||
->map(function (string $uri): ?ContactPushDto {
|
||||
->map(function (string $uri): ?PushVCard {
|
||||
$card = $this->sync->backend->getCard($this->sync->addressBookName(), $uri);
|
||||
|
||||
if ($card === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ContactPushDto($uri, $card['etag'], new Request('PUT', $uri, [], $card['carddata']));
|
||||
return $card !== false
|
||||
? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], 0))
|
||||
: null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,7 +61,7 @@ class AddressBookContactsPush
|
||||
*
|
||||
* @param Collection<array-key, ContactDto> $changes
|
||||
* @param array $contacts
|
||||
* @return Collection<mixed, ?ContactPushDto>
|
||||
* @return Collection
|
||||
*/
|
||||
private function preparePushChangedContacts(Collection $changes, array $contacts): Collection
|
||||
{
|
||||
@@ -113,14 +75,12 @@ class AddressBookContactsPush
|
||||
$uuid = $this->sync->backend->getUuid($uri);
|
||||
|
||||
return $refreshIds->contains($uuid);
|
||||
})->map(function (string $uri): ?ContactPushDto {
|
||||
})->map(function (string $uri): ?PushVCard {
|
||||
$card = $this->sync->backend->getCard($this->sync->addressBookName(), $uri);
|
||||
|
||||
if ($card === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ContactPushDto($uri, $card['etag'], new Request('PUT', $uri, ['If-Match' => $card['etag']], $card['carddata']));
|
||||
return $card !== false
|
||||
? new PushVCard($this->sync->subscription, new ContactPushDto($uri, $card['etag'], $card['carddata'], 1))
|
||||
: null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use App\Jobs\Dav\PushVCard;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Support\Collection;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use IlluminateAgnostic\Collection\Support\Arr;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
use App\Services\DavClient\Utils\Model\ContactDto;
|
||||
@@ -25,16 +24,17 @@ class AddressBookContactsPushMissed
|
||||
* @param array<array-key, string>|null $localChanges
|
||||
* @param Collection<array-key, ContactDto> $distContacts
|
||||
* @param Collection<array-key, Contact> $localContacts
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
public function execute(SyncDto $sync, ?array $localChanges, Collection $distContacts, Collection $localContacts): PromiseInterface
|
||||
public function execute(SyncDto $sync, ?array $localChanges, Collection $distContacts, Collection $localContacts): Collection
|
||||
{
|
||||
$this->sync = $sync;
|
||||
|
||||
$missed = $this->preparePushMissedContacts(Arr::get($localChanges, 'added', []), $distContacts, $localContacts);
|
||||
$missings = $this->preparePushMissedContacts(Arr::get($localChanges, 'added', []), $distContacts, $localContacts);
|
||||
|
||||
return app(AddressBookContactsPush::class)
|
||||
->execute($sync, collect(), $localChanges, $missed);
|
||||
->execute($sync, collect(), $localChanges)
|
||||
->union($missings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ class AddressBookContactsPushMissed
|
||||
* @param array<array-key, string> $added
|
||||
* @param Collection<array-key, ContactDto> $distContacts
|
||||
* @param Collection<array-key, Contact> $localContacts
|
||||
* @return Collection<array-key, ContactPushDto>
|
||||
* @return Collection
|
||||
*/
|
||||
private function preparePushMissedContacts(array $added, Collection $distContacts, Collection $localContacts): Collection
|
||||
{
|
||||
@@ -61,11 +61,10 @@ class AddressBookContactsPushMissed
|
||||
->filter(function (Contact $contact) use ($distUuids, $addedUuids) {
|
||||
return ! $distUuids->contains($contact->uuid)
|
||||
&& ! $addedUuids->contains($contact->uuid);
|
||||
})->map(function (Contact $contact): ContactPushDto {
|
||||
})->map(function (Contact $contact): PushVCard {
|
||||
$card = $this->sync->backend->prepareCard($contact);
|
||||
|
||||
return new ContactPushDto($card['uri'], $card['etag'], new Request('PUT', $card['uri'], ['If-Match' => '*'], $card['carddata']));
|
||||
})
|
||||
->values();
|
||||
return new PushVCard($this->sync->subscription, new ContactPushDto($card['uri'], $card['etag'], $card['carddata'], 2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,11 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use App\Jobs\Dav\GetVCard;
|
||||
use App\Jobs\Dav\GetMultipleVCard;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
use App\Services\DavClient\Utils\Traits\HasCapability;
|
||||
use App\Services\DavClient\Utils\Model\ContactUpdateDto;
|
||||
|
||||
class AddressBookContactsUpdater
|
||||
{
|
||||
@@ -27,9 +22,9 @@ class AddressBookContactsUpdater
|
||||
*
|
||||
* @param SyncDto $sync
|
||||
* @param Collection<array-key, \App\Services\DavClient\Utils\Model\ContactDto> $refresh
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
public function execute(SyncDto $sync, Collection $refresh): PromiseInterface
|
||||
public function execute(SyncDto $sync, Collection $refresh): Collection
|
||||
{
|
||||
$this->sync = $sync;
|
||||
|
||||
@@ -42,98 +37,27 @@ class AddressBookContactsUpdater
|
||||
* Get contacts data with addressbook-multiget request.
|
||||
*
|
||||
* @param Collection<array-key, \App\Services\DavClient\Utils\Model\ContactDto> $refresh
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
private function refreshMultigetContacts(Collection $refresh): PromiseInterface
|
||||
private function refreshMultigetContacts(Collection $refresh): Collection
|
||||
{
|
||||
$hrefs = $refresh->pluck('uri');
|
||||
$hrefs = $refresh->pluck('uri')->toArray();
|
||||
|
||||
return $this->sync->client->addressbookMultigetAsync('', [
|
||||
'{DAV:}getetag',
|
||||
$this->getAddressDataProperty(),
|
||||
], $hrefs)
|
||||
->then(function ($datas) {
|
||||
return collect($datas)
|
||||
->filter(function (array $contact): bool {
|
||||
return isset($contact[200]);
|
||||
})
|
||||
->map(function (array $contact, $href): ContactUpdateDto {
|
||||
return new ContactUpdateDto(
|
||||
$href,
|
||||
Arr::get($contact, '200.{DAV:}getetag'),
|
||||
Arr::get($contact, '200.{'.CardDAVPlugin::NS_CARDDAV.'}address-data')
|
||||
);
|
||||
});
|
||||
})->then(function (Collection $contacts) {
|
||||
$contacts->each(function (ContactUpdateDto $contact) {
|
||||
$this->syncLocalContact($contact);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for address-data property.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAddressDataProperty(): array
|
||||
{
|
||||
$addressDataAttributes = Arr::get($this->sync->subscription->capabilities, 'addressData', [
|
||||
'content-type' => 'text/vcard',
|
||||
'version' => '4.0',
|
||||
return collect([
|
||||
new GetMultipleVCard($this->sync->subscription, $hrefs),
|
||||
]);
|
||||
|
||||
return [
|
||||
'name' => '{'.CardDAVPlugin::NS_CARDDAV.'}address-data',
|
||||
'value' => null,
|
||||
'attributes' => $addressDataAttributes,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contacts data with request.
|
||||
*
|
||||
* @param Collection<array-key, \App\Services\DavClient\Utils\Model\ContactDto> $requests
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
private function refreshSimpleGetContacts(Collection $requests): PromiseInterface
|
||||
private function refreshSimpleGetContacts(Collection $requests): Collection
|
||||
{
|
||||
$inputs = $requests->map(function ($contact) {
|
||||
return new Request('GET', $contact->uri);
|
||||
})->toArray();
|
||||
|
||||
return $this->sync->client->requestPool($inputs, [
|
||||
'concurrency' => 25,
|
||||
'fulfilled' => function (ResponseInterface $response, $index) use ($requests) {
|
||||
if ($response->getStatusCode() === 200) {
|
||||
/** @var \App\Services\DavClient\Utils\Model\ContactDto $request */
|
||||
$request = $requests[$index];
|
||||
|
||||
$this->syncLocalContact(new ContactUpdateDto(
|
||||
$request->uri,
|
||||
$request->etag,
|
||||
$response->getBody()->detach(),
|
||||
));
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save contact to local storage.
|
||||
*
|
||||
* @param ContactUpdateDto $contact
|
||||
*/
|
||||
private function syncLocalContact(ContactUpdateDto $contact): void
|
||||
{
|
||||
if ($contact->card !== null) {
|
||||
Log::info(__CLASS__.' syncLocalContact: update '.$contact->uri);
|
||||
|
||||
$newtag = $this->sync->backend->updateCard($this->sync->addressBookName(), $contact->uri, $contact->card);
|
||||
|
||||
if ($newtag !== $contact->etag) {
|
||||
Log::warning(__CLASS__.' syncLocalContact: wrong etag when updating contact. Expected '.$contact->etag.', get '.$newtag);
|
||||
}
|
||||
}
|
||||
return $requests->map(function ($contact): GetVCard {
|
||||
return new GetVCard($this->sync->subscription, $contact);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Services\DavClient\Utils;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
use App\Services\DavClient\Utils\Model\ContactDto;
|
||||
|
||||
@@ -20,9 +19,9 @@ class AddressBookContactsUpdaterMissed
|
||||
* @param SyncDto $sync
|
||||
* @param Collection<array-key, \App\Models\Contact\Contact> $localContacts
|
||||
* @param Collection<array-key, \App\Services\DavClient\Utils\Model\ContactDto> $distContacts
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
public function execute(SyncDto $sync, Collection $localContacts, Collection $distContacts): PromiseInterface
|
||||
public function execute(SyncDto $sync, Collection $localContacts, Collection $distContacts): Collection
|
||||
{
|
||||
$this->sync = $sync;
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils;
|
||||
|
||||
use Illuminate\Bus\Batch;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use GuzzleHttp\Promise\Each;
|
||||
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;
|
||||
@@ -44,27 +45,28 @@ class AddressBookSynchronizer
|
||||
$localChanges = $this->sync->backend->getChangesForAddressBook($this->sync->addressBookName(), (string) $this->sync->subscription->localSyncToken, 1);
|
||||
|
||||
// Get distant changes to sync
|
||||
$promise = $this->getDistantChanges();
|
||||
$changes = $this->getDistantChanges();
|
||||
|
||||
// Get distant contacts
|
||||
$batch = app(AddressBookContactsUpdater::class)
|
||||
->execute($this->sync, $changes);
|
||||
|
||||
$chain = [];
|
||||
$chain[] = $promise->then(function (Collection $changes) {
|
||||
// Get distant contacts
|
||||
return app(AddressBookContactsUpdater::class)
|
||||
->execute($this->sync, $changes);
|
||||
});
|
||||
if (! $this->sync->subscription->readonly) {
|
||||
$chain[] = $promise->then(function (Collection $changes) use ($localChanges) {
|
||||
return app(AddressBookContactsPush::class)
|
||||
->execute($this->sync, $changes, $localChanges);
|
||||
});
|
||||
$batch->union(
|
||||
app(AddressBookContactsPush::class)
|
||||
->execute($this->sync, $changes, $localChanges)
|
||||
);
|
||||
}
|
||||
|
||||
Each::of($chain)->wait();
|
||||
Bus::batch($batch)
|
||||
->then(function (Batch $batch) {
|
||||
$token = $this->sync->backend->getCurrentSyncToken($this->sync->addressBookName());
|
||||
|
||||
$token = $this->sync->backend->getCurrentSyncToken($this->sync->addressBookName());
|
||||
|
||||
$this->sync->subscription->localSyncToken = $token->id;
|
||||
$this->sync->subscription->save();
|
||||
$this->sync->subscription->localSyncToken = $token->id;
|
||||
$this->sync->subscription->save();
|
||||
})
|
||||
->allowFailures()
|
||||
->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,31 +81,30 @@ class AddressBookSynchronizer
|
||||
$localContacts = $this->sync->backend->getObjects($this->sync->addressBookName());
|
||||
|
||||
// Get distant changes to sync
|
||||
$promise = $this->getAllContactsEtag();
|
||||
$distContacts = $this->getAllContactsEtag();
|
||||
|
||||
$chain = [];
|
||||
$chain[] = $promise->then(function (Collection $distContacts) use ($localContacts) {
|
||||
// Get missed contacts
|
||||
return app(AddressBookContactsUpdaterMissed::class)
|
||||
->execute($this->sync, $localContacts, $distContacts);
|
||||
});
|
||||
// Get missed contacts
|
||||
$batch = app(AddressBookContactsUpdaterMissed::class)
|
||||
->execute($this->sync, $localContacts, $distContacts);
|
||||
|
||||
if (! $this->sync->subscription->readonly) {
|
||||
$chain[] = $promise->then(function ($distContacts) use ($localChanges, $localContacts) {
|
||||
return app(AddressBookContactsPushMissed::class)
|
||||
->execute($this->sync, $localChanges, $distContacts, $localContacts);
|
||||
});
|
||||
$batch->union(
|
||||
app(AddressBookContactsPushMissed::class)
|
||||
->execute($this->sync, $localChanges, $distContacts, $localContacts)
|
||||
);
|
||||
}
|
||||
|
||||
Each::of($chain)->wait();
|
||||
Bus::batch($batch)
|
||||
->allowFailures()
|
||||
->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distant changes to sync.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
private function getDistantChanges(): PromiseInterface
|
||||
private function getDistantChanges(): Collection
|
||||
{
|
||||
return $this->getDistantEtags()
|
||||
->then(function ($collection) {
|
||||
@@ -114,7 +115,8 @@ class AddressBookSynchronizer
|
||||
->map(function ($contact, $href): ContactDto {
|
||||
return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag'));
|
||||
});
|
||||
});
|
||||
})
|
||||
->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,12 +206,12 @@ class AddressBookSynchronizer
|
||||
/**
|
||||
* Get all contacts etag.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @return Collection
|
||||
*/
|
||||
private function getAllContactsEtag(): PromiseInterface
|
||||
private function getAllContactsEtag(): Collection
|
||||
{
|
||||
if (! $this->hasCapability('addressbookQuery')) {
|
||||
return $this->emptyPromise();
|
||||
return collect();
|
||||
}
|
||||
|
||||
return $this->sync->client->addressbookQueryAsync('', '{DAV:}getetag')
|
||||
@@ -221,7 +223,8 @@ class AddressBookSynchronizer
|
||||
->map(function ($contact, $href): ContactDto {
|
||||
return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag'));
|
||||
});
|
||||
});
|
||||
})
|
||||
->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,44 +13,43 @@ use GuzzleHttp\Client as GuzzleClient;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||
|
||||
class DavClient
|
||||
{
|
||||
/**
|
||||
* The xml service.
|
||||
*
|
||||
* @var Service
|
||||
*/
|
||||
public $xml;
|
||||
|
||||
/**
|
||||
* @var GuzzleClient
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Create a new client.
|
||||
* Initialize the client.
|
||||
*
|
||||
* @param array $settings
|
||||
* @param GuzzleClient $client
|
||||
* @return DavClient
|
||||
*/
|
||||
public function __construct(array $settings, GuzzleClient $client = null)
|
||||
public static function init(array $settings, GuzzleClient $client = null): DavClient
|
||||
{
|
||||
if (is_null($client) && ! isset($settings['base_uri'])) {
|
||||
throw new \InvalidArgumentException('A baseUri must be provided');
|
||||
}
|
||||
|
||||
$this->client = is_null($client) ? new GuzzleClient([
|
||||
'base_uri' => $settings['base_uri'],
|
||||
'auth' => [
|
||||
$settings['username'],
|
||||
$settings['password'],
|
||||
],
|
||||
'verify' => ! App::environment('local'),
|
||||
]) : $client;
|
||||
$me = new self();
|
||||
|
||||
$this->xml = new Service();
|
||||
$me->client = is_null($client)
|
||||
? new GuzzleClient([
|
||||
'base_uri' => $settings['base_uri'],
|
||||
'auth' => [
|
||||
$settings['username'],
|
||||
$settings['password'],
|
||||
],
|
||||
'verify' => ! App::environment('local'),
|
||||
])
|
||||
: $client;
|
||||
|
||||
return $me;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +137,7 @@ class DavClient
|
||||
{
|
||||
$baseUri = $this->client->getConfig('base_uri');
|
||||
|
||||
return is_null($path) ? $baseUri : $baseUri->withPath($path);
|
||||
return (string) (is_null($path) ? $baseUri : $baseUri->withPath($path));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,14 +209,14 @@ class DavClient
|
||||
public function propFindAsync(string $url, $properties, int $depth = 0, array $options = []): PromiseInterface
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$root = $this->addElementNS($dom, 'DAV:', 'd:propfind');
|
||||
$prop = $this->addElement($dom, $root, 'd:prop');
|
||||
$root = self::addElementNS($dom, 'DAV:', 'd:propfind');
|
||||
$prop = self::addElement($dom, $root, 'd:prop');
|
||||
|
||||
$namespaces = [
|
||||
'DAV:' => 'd',
|
||||
];
|
||||
|
||||
$this->fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
self::fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
|
||||
$body = $dom->saveXML();
|
||||
|
||||
@@ -225,7 +224,7 @@ class DavClient
|
||||
'Depth' => $depth,
|
||||
'Content-Type' => 'application/xml; charset=utf-8',
|
||||
], $body, $options)->then(function (ResponseInterface $response) use ($depth): array {
|
||||
$result = $this->parseMultiStatus((string) $response->getBody());
|
||||
$result = self::parseMultiStatus((string) $response->getBody());
|
||||
|
||||
// If depth was 0, we only return the top item value
|
||||
if ($depth === 0) {
|
||||
@@ -254,18 +253,18 @@ class DavClient
|
||||
public function syncCollectionAsync(string $url, $properties, string $syncToken, array $options = []): PromiseInterface
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$root = $this->addElementNS($dom, 'DAV:', 'd:sync-collection');
|
||||
$root = self::addElementNS($dom, 'DAV:', 'd:sync-collection');
|
||||
|
||||
$this->addElement($dom, $root, 'd:sync-token', $syncToken);
|
||||
$this->addElement($dom, $root, 'd:sync-level', '1');
|
||||
self::addElement($dom, $root, 'd:sync-token', $syncToken);
|
||||
self::addElement($dom, $root, 'd:sync-level', '1');
|
||||
|
||||
$prop = $this->addElement($dom, $root, 'd:prop');
|
||||
$prop = self::addElement($dom, $root, 'd:prop');
|
||||
|
||||
$namespaces = [
|
||||
'DAV:' => 'd',
|
||||
];
|
||||
|
||||
$this->fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
self::fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
|
||||
$body = $dom->saveXML();
|
||||
|
||||
@@ -273,7 +272,7 @@ class DavClient
|
||||
'Depth' => '0',
|
||||
'Content-Type' => 'application/xml; charset=utf-8',
|
||||
], $body, $options)->then(function (ResponseInterface $response) {
|
||||
return $this->parseMultiStatus((string) $response->getBody());
|
||||
return self::parseMultiStatus((string) $response->getBody());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -283,37 +282,38 @@ class DavClient
|
||||
* @param string $url
|
||||
* @param array|string $properties
|
||||
* @param iterable $contacts
|
||||
* @return PromiseInterface
|
||||
* @return array
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc6352#section-8.7
|
||||
*/
|
||||
public function addressbookMultigetAsync(string $url, $properties, iterable $contacts, array $options = []): PromiseInterface
|
||||
public static function addressbookMultiget(PendingRequest $request, $properties, iterable $contacts, string $url = '', array $options = []): array
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$root = $this->addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-multiget');
|
||||
$root = self::addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-multiget');
|
||||
$dom->createAttributeNS('DAV:', 'd:e');
|
||||
|
||||
$prop = $this->addElement($dom, $root, 'd:prop');
|
||||
$prop = self::addElement($dom, $root, 'd:prop');
|
||||
|
||||
$namespaces = [
|
||||
'DAV:' => 'd',
|
||||
CardDAVPlugin::NS_CARDDAV => 'card',
|
||||
];
|
||||
|
||||
$this->fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
self::fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
$this->addElement($dom, $root, 'd:href', $contact);
|
||||
self::addElement($dom, $root, 'd:href', $contact);
|
||||
}
|
||||
|
||||
$body = $dom->saveXML();
|
||||
|
||||
return $this->requestAsync('REPORT', $url, [
|
||||
'Depth' => '1',
|
||||
'Content-Type' => 'application/xml; charset=utf-8',
|
||||
], $body, $options)->then(function (ResponseInterface $response) {
|
||||
return $this->parseMultiStatus((string) $response->getBody());
|
||||
});
|
||||
$response = $request->withHeaders(['Depth' => '1'])
|
||||
->withBody($body, 'application/xml; charset=utf-8')
|
||||
->send('REPORT', $url, $options);
|
||||
|
||||
$response->throw();
|
||||
|
||||
return self::parseMultiStatus($response->body());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,17 +328,17 @@ class DavClient
|
||||
public function addressbookQueryAsync(string $url, $properties, array $options = []): PromiseInterface
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$root = $this->addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-query');
|
||||
$root = self::addElementNS($dom, CardDAVPlugin::NS_CARDDAV, 'card:addressbook-query');
|
||||
$dom->createAttributeNS('DAV:', 'd:e');
|
||||
|
||||
$prop = $this->addElement($dom, $root, 'd:prop');
|
||||
$prop = self::addElement($dom, $root, 'd:prop');
|
||||
|
||||
$namespaces = [
|
||||
'DAV:' => 'd',
|
||||
CardDAVPlugin::NS_CARDDAV => 'card',
|
||||
];
|
||||
|
||||
$this->fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
self::fetchProperties($dom, $prop, $properties, $namespaces);
|
||||
|
||||
$body = $dom->saveXML();
|
||||
|
||||
@@ -346,7 +346,7 @@ class DavClient
|
||||
'Depth' => '1',
|
||||
'Content-Type' => 'application/xml; charset=utf-8',
|
||||
], $body, $options)->then(function (ResponseInterface $response) {
|
||||
return $this->parseMultiStatus((string) $response->getBody());
|
||||
return self::parseMultiStatus((string) $response->getBody());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ class DavClient
|
||||
* @param array $namespaces
|
||||
* @return void
|
||||
*/
|
||||
private function fetchProperties(\DOMDocument $dom, \DOMNode $prop, $properties, array $namespaces)
|
||||
private static function fetchProperties(\DOMDocument $dom, \DOMNode $prop, $properties, array $namespaces)
|
||||
{
|
||||
if (is_string($properties)) {
|
||||
$properties = [$properties];
|
||||
@@ -522,7 +522,7 @@ class DavClient
|
||||
{
|
||||
$propPatch = new PropPatch();
|
||||
$propPatch->properties = $properties;
|
||||
$body = $this->xml->write(
|
||||
$body = (new Service())->write(
|
||||
'{DAV:}propertyupdate',
|
||||
$propPatch
|
||||
);
|
||||
@@ -533,7 +533,7 @@ class DavClient
|
||||
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 = $this->parseMultiStatus((string) $response->getBody());
|
||||
$result = self::parseMultiStatus((string) $response->getBody());
|
||||
|
||||
$errorProperties = [];
|
||||
foreach ($result as $statusList) {
|
||||
@@ -649,9 +649,10 @@ class DavClient
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc4918#section-9.2.1
|
||||
*/
|
||||
private function parseMultiStatus(string $body): array
|
||||
private static function parseMultiStatus(string $body): array
|
||||
{
|
||||
$multistatus = $this->xml->expect('{DAV:}multistatus', $body);
|
||||
$multistatus = (new Service())
|
||||
->expect('{DAV:}multistatus', $body);
|
||||
|
||||
$result = [];
|
||||
|
||||
@@ -675,7 +676,7 @@ class DavClient
|
||||
* @param string $qualifiedName
|
||||
* @return \DOMNode
|
||||
*/
|
||||
private function addElementNS(\DOMDocument $dom, ?string $namespace, string $qualifiedName): \DOMNode
|
||||
private static function addElementNS(\DOMDocument $dom, ?string $namespace, string $qualifiedName): \DOMNode
|
||||
{
|
||||
return $dom->appendChild($dom->createElementNS($namespace, $qualifiedName));
|
||||
}
|
||||
@@ -689,7 +690,7 @@ class DavClient
|
||||
* @param string|null $value
|
||||
* @return \DOMNode
|
||||
*/
|
||||
private function addElement(\DOMDocument $dom, \DOMNode $root, string $name, ?string $value = null): \DOMNode
|
||||
private static function addElement(\DOMDocument $dom, \DOMNode $root, string $name, ?string $value = null): \DOMNode
|
||||
{
|
||||
return $root->appendChild($dom->createElement($name, $value));
|
||||
}
|
||||
|
||||
@@ -2,26 +2,24 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils\Model;
|
||||
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
|
||||
class ContactPushDto extends ContactDto
|
||||
class ContactPushDto extends ContactUpdateDto
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
* @var int
|
||||
*/
|
||||
public $request;
|
||||
public $mode;
|
||||
|
||||
/**
|
||||
* Create a new ContactPushDto.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param string $etag
|
||||
* @param Request $request
|
||||
* @param string|resource $card
|
||||
* @param int $mode
|
||||
*/
|
||||
public function __construct(string $uri, string $etag, Request $request)
|
||||
public function __construct(string $uri, string $etag, $card, int $mode)
|
||||
{
|
||||
$this->uri = $uri;
|
||||
$this->etag = $etag;
|
||||
$this->request = $request;
|
||||
parent::__construct($uri, $etag, $card);
|
||||
$this->mode = $mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
namespace App\Services\DavClient\Utils\Model;
|
||||
|
||||
use function Safe\fclose;
|
||||
use function Safe\stream_get_contents;
|
||||
|
||||
class ContactUpdateDto extends ContactDto
|
||||
{
|
||||
/**
|
||||
* @var string|resource
|
||||
* @var string
|
||||
*/
|
||||
public $card;
|
||||
|
||||
@@ -18,8 +21,24 @@ class ContactUpdateDto extends ContactDto
|
||||
*/
|
||||
public function __construct(string $uri, string $etag, $card)
|
||||
{
|
||||
$this->uri = $uri;
|
||||
$this->etag = $etag;
|
||||
$this->card = $card;
|
||||
parent::__construct($uri, $etag);
|
||||
$this->card = self::transformCard($card);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform card.
|
||||
*
|
||||
* @param string|resource $card
|
||||
* @return string
|
||||
*/
|
||||
protected static function transformCard($card): string
|
||||
{
|
||||
if (is_resource($card)) {
|
||||
$card = tap(stream_get_contents($card), function () use ($card) {
|
||||
fclose($card);
|
||||
});
|
||||
}
|
||||
|
||||
return $card;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateJobBatchesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->text('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('job_batches');
|
||||
}
|
||||
}
|
||||
@@ -359,7 +359,7 @@ class DavTester extends TestCase
|
||||
"</card:addressbook-multiget>\n", 'REPORT');
|
||||
}
|
||||
|
||||
public function multistatusHeader()
|
||||
public static function multistatusHeader()
|
||||
{
|
||||
return '<d:multistatus xmlns:d="DAV:" xmlns:card="urn:ietf:params:xml:ns:carddav">';
|
||||
}
|
||||
|
||||
@@ -44,6 +44,22 @@ abstract class TestCase extends BaseTestCase
|
||||
$property->setValue($object, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get protected/private property of a class.
|
||||
*
|
||||
* @param object &$object
|
||||
* @param string $propertyName
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPrivateValue(&$object, string $propertyName)
|
||||
{
|
||||
$reflection = new \ReflectionClass(get_class($object));
|
||||
$property = $reflection->getProperty($propertyName);
|
||||
$property->setAccessible(true);
|
||||
|
||||
return $property->getValue($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the response contains an ObjectDeleted response.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Jobs\Dav;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User\User;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use App\Jobs\Dav\UpdateVCard;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Bus\PendingBatch;
|
||||
use App\Jobs\Dav\GetMultipleVCard;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||
use Illuminate\Bus\DatabaseBatchRepository;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Dav\DavClient;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class GetMultipleVCardTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
use CardEtag;
|
||||
|
||||
/** @test */
|
||||
public function it_get_cards()
|
||||
{
|
||||
$fake = Bus::fake();
|
||||
|
||||
$user = factory(User::class)->create();
|
||||
$addressBookSubscription = AddressBookSubscription::factory()->create([
|
||||
'account_id' => $user->account_id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact->forceFill([
|
||||
'first_name' => 'Test',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$card = $this->getCard($contact);
|
||||
$etag = $this->getEtag($contact, true);
|
||||
|
||||
$this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) {
|
||||
$mock->shouldReceive('addressbookMultiget')
|
||||
->once()
|
||||
->withArgs(function ($request, $properties, $contacts) {
|
||||
$this->assertEquals([
|
||||
'{DAV:}getetag',
|
||||
[
|
||||
'name' => '{'.CardDAVPlugin::NS_CARDDAV.'}address-data',
|
||||
'value' => null,
|
||||
'attributes' => [
|
||||
'content-type' => 'text/vcard',
|
||||
'version' => '4.0',
|
||||
],
|
||||
],
|
||||
], $properties);
|
||||
$this->assertEquals(['https://test/dav/uri'], $contacts);
|
||||
|
||||
return true;
|
||||
})
|
||||
->andReturn([
|
||||
'https://test/dav/uri' => [
|
||||
200 => [
|
||||
'{'.CardDAVPlugin::NS_CARDDAV.'}address-data' => $card,
|
||||
'{DAV:}getetag' => $etag,
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
$pendingBatch = $fake->batch([
|
||||
$job = new GetMultipleVCard($addressBookSubscription, ['https://test/dav/uri']),
|
||||
]);
|
||||
$batch = $pendingBatch->dispatch();
|
||||
|
||||
$fake->assertBatched(function (PendingBatch $pendingBatch) {
|
||||
$this->assertCount(1, $pendingBatch->jobs);
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $pendingBatch->jobs->first());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$batch = app(DatabaseBatchRepository::class)->store($pendingBatch);
|
||||
$job->withBatchId($batch->id)->handle();
|
||||
|
||||
$fake->assertDispatched(function (UpdateVCard $updateVCard) use ($etag, $card) {
|
||||
$dto = $this->getPrivateValue($updateVCard, 'contact');
|
||||
$this->assertEquals('https://test/dav/uri', $dto->uri);
|
||||
$this->assertEquals($etag, $dto->etag);
|
||||
$this->assertEquals($card, $dto->card);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_get_cards_mock_http()
|
||||
{
|
||||
$fake = Bus::fake();
|
||||
|
||||
$user = factory(User::class)->create();
|
||||
$addressBookSubscription = AddressBookSubscription::factory()->create([
|
||||
'account_id' => $user->account_id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact->forceFill([
|
||||
'first_name' => 'Test',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$card = $this->getCard($contact);
|
||||
$etag = $this->getEtag($contact, true);
|
||||
|
||||
$this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) {
|
||||
$mock->shouldReceive('addressbookMultiget')
|
||||
->once()
|
||||
->withArgs(function ($request, $properties, $contacts) {
|
||||
$this->assertEquals([
|
||||
'{DAV:}getetag',
|
||||
[
|
||||
'name' => '{'.CardDAVPlugin::NS_CARDDAV.'}address-data',
|
||||
'value' => null,
|
||||
'attributes' => [
|
||||
'content-type' => 'text/vcard',
|
||||
'version' => '4.0',
|
||||
],
|
||||
],
|
||||
], $properties);
|
||||
$this->assertEquals(['https://test/dav/uri'], $contacts);
|
||||
|
||||
return true;
|
||||
})
|
||||
->andReturn([
|
||||
'https://test/dav/uri' => [
|
||||
200 => [
|
||||
'{'.CardDAVPlugin::NS_CARDDAV.'}address-data' => $card,
|
||||
'{DAV:}getetag' => $etag,
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
$pendingBatch = $fake->batch([
|
||||
$job = new GetMultipleVCard($addressBookSubscription, ['https://test/dav/uri']),
|
||||
]);
|
||||
$batch = $pendingBatch->dispatch();
|
||||
|
||||
$fake->assertBatched(function (PendingBatch $pendingBatch) {
|
||||
$this->assertCount(1, $pendingBatch->jobs);
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $pendingBatch->jobs->first());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$batch = app(DatabaseBatchRepository::class)->store($pendingBatch);
|
||||
$job->withBatchId($batch->id)->handle();
|
||||
|
||||
$fake->assertDispatched(function (UpdateVCard $updateVCard) use ($etag, $card) {
|
||||
$dto = $this->getPrivateValue($updateVCard, 'contact');
|
||||
$this->assertEquals('https://test/dav/uri', $dto->uri);
|
||||
$this->assertEquals($etag, $dto->etag);
|
||||
$this->assertEquals($card, $dto->card);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Jobs\Dav;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User\User;
|
||||
use App\Jobs\Dav\GetVCard;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use App\Jobs\Dav\UpdateVCard;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Bus\PendingBatch;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Bus\DatabaseBatchRepository;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Model\ContactDto;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class GetVCardTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
use CardEtag;
|
||||
|
||||
/** @test */
|
||||
public function it_get_card()
|
||||
{
|
||||
$fake = Bus::fake();
|
||||
|
||||
$user = factory(User::class)->create();
|
||||
$addressBookSubscription = AddressBookSubscription::factory()->create([
|
||||
'account_id' => $user->account_id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact->forceFill([
|
||||
'first_name' => 'Test',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$card = $this->getCard($contact);
|
||||
$etag = $this->getEtag($contact, true);
|
||||
|
||||
Http::fake([
|
||||
'https://test/dav/uri' => Http::response($card, 200),
|
||||
]);
|
||||
|
||||
$pendingBatch = $fake->batch([
|
||||
$job = new GetVCard($addressBookSubscription, new ContactDto('https://test/dav/uri', $etag)),
|
||||
]);
|
||||
$batch = $pendingBatch->dispatch();
|
||||
|
||||
$fake->assertBatched(function (PendingBatch $pendingBatch) {
|
||||
$this->assertCount(1, $pendingBatch->jobs);
|
||||
$this->assertInstanceOf(GetVCard::class, $pendingBatch->jobs->first());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$batch = app(DatabaseBatchRepository::class)->store($pendingBatch);
|
||||
$job->withBatchId($batch->id)->handle();
|
||||
|
||||
$fake->assertDispatched(function (UpdateVCard $updateVCard) use ($etag, $card) {
|
||||
$dto = $this->getPrivateValue($updateVCard, 'contact');
|
||||
$this->assertEquals('https://test/dav/uri', $dto->uri);
|
||||
$this->assertEquals($etag, $dto->etag);
|
||||
$this->assertEquals($card, $dto->card);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Jobs\Dav;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User\User;
|
||||
use App\Jobs\Dav\PushVCard;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Bus\PendingBatch;
|
||||
use Illuminate\Http\Client\Request;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Bus\DatabaseBatchRepository;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Model\ContactPushDto;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class PushVCardTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
use CardEtag;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider modes
|
||||
*/
|
||||
public function it_push_card($mode, $ifmatch)
|
||||
{
|
||||
$fake = Bus::fake();
|
||||
|
||||
$user = factory(User::class)->create();
|
||||
$addressBookSubscription = AddressBookSubscription::factory()->create([
|
||||
'account_id' => $user->account_id,
|
||||
'user_id' => $user->id,
|
||||
'uri' => 'https://test/dav',
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact->forceFill([
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
'updated_at' => '2021-09-01',
|
||||
]);
|
||||
$card = $this->getCard($contact);
|
||||
$etag = $this->getEtag($contact, true);
|
||||
|
||||
if ($ifmatch == ['etag']) {
|
||||
$ifmatch = [$etag];
|
||||
}
|
||||
|
||||
Http::fake(function (Request $request, $options) use ($card, $ifmatch) {
|
||||
$this->assertEquals('https://test/dav/uri', $request->url());
|
||||
$this->assertEquals('PUT', $request->method());
|
||||
$this->assertEquals($ifmatch, $request->header('If-Match'));
|
||||
|
||||
return Http::response($card, 200);
|
||||
});
|
||||
|
||||
$pendingBatch = $fake->batch([
|
||||
$job = new PushVCard($addressBookSubscription, new ContactPushDto('uri', $etag, $card, $mode)),
|
||||
]);
|
||||
$batch = $pendingBatch->dispatch();
|
||||
|
||||
$fake->assertBatched(function (PendingBatch $pendingBatch) {
|
||||
$this->assertCount(1, $pendingBatch->jobs);
|
||||
$this->assertInstanceOf(PushVCard::class, $pendingBatch->jobs->first());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$batch = app(DatabaseBatchRepository::class)->store($pendingBatch);
|
||||
$job->withBatchId($batch->id)->handle();
|
||||
}
|
||||
|
||||
public function modes(): array
|
||||
{
|
||||
return [
|
||||
[0, []],
|
||||
[1, ['etag']],
|
||||
[2, ['*']],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Jobs\Dav;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User\User;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use App\Jobs\Dav\UpdateVCard;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Bus\PendingBatch;
|
||||
use App\Models\Account\AddressBook;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Bus\DatabaseBatchRepository;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use App\Services\DavClient\Utils\Model\ContactUpdateDto;
|
||||
|
||||
class UpdateVCardTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
use CardEtag;
|
||||
|
||||
/** @test */
|
||||
public function it_create_a_contact()
|
||||
{
|
||||
$fake = Bus::fake();
|
||||
|
||||
$user = factory(User::class)->create();
|
||||
$addressBook = AddressBook::factory()->create([
|
||||
'account_id' => $user->account_id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$contact = new Contact();
|
||||
$contact->forceFill([
|
||||
'first_name' => 'Test',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$card = $this->getCard($contact);
|
||||
$etag = $this->getEtag($contact, true);
|
||||
|
||||
$pendingBatch = $fake->batch([
|
||||
$job = new UpdateVCard($user, $addressBook->name, new ContactUpdateDto('https://test/dav/uricontact1', $etag, $card)),
|
||||
]);
|
||||
$batch = $pendingBatch->dispatch();
|
||||
|
||||
$fake->assertBatched(function (PendingBatch $pendingBatch) {
|
||||
$this->assertCount(1, $pendingBatch->jobs);
|
||||
$this->assertInstanceOf(UpdateVCard::class, $pendingBatch->jobs->first());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$batch = app(DatabaseBatchRepository::class)->store($pendingBatch);
|
||||
$job->withBatchId($batch->id)->handle();
|
||||
|
||||
$this->assertDatabaseHas('contacts', [
|
||||
'first_name' => 'Test',
|
||||
'uuid' => 'affacde9-b2fe-4371-9acb-6612aaee6971',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,16 @@ namespace Tests\Unit\Services\DavClient\Utils;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Mockery\MockInterface;
|
||||
use App\Jobs\Dav\PushVCard;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use Tests\Helpers\DavTester;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
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;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use App\Http\Controllers\DAV\Backend\CardDAV\CardDAVBackend;
|
||||
use App\Services\DavClient\Utils\AddressBookContactsPushMissed;
|
||||
@@ -48,7 +49,7 @@ class AddressBookContactsPushMissedTest extends TestCase
|
||||
$mock->shouldReceive('getUuid')
|
||||
->once()
|
||||
->withArgs(function ($uri) {
|
||||
$this->assertEquals('https://test/dav/uuid6', $uri);
|
||||
$this->assertEquals('uuid6', $uri);
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -62,22 +63,25 @@ class AddressBookContactsPushMissedTest extends TestCase
|
||||
})
|
||||
->andReturn([
|
||||
'carddata' => $card,
|
||||
'uri' => 'https://test/dav/uuid3',
|
||||
'uri' => 'uuid3',
|
||||
'etag' => $etag,
|
||||
]);
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addResponse('https://test/dav/uuid3', new Response(200, ['Etag' => $etag]), $card, 'PUT', ['If-Match' => '*']);
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsPushMissed())
|
||||
$batchs = (new AddressBookContactsPushMissed())
|
||||
->execute(new SyncDto($subscription, $client, $backend), [], collect([
|
||||
'https://test/dav/uuid6' => new ContactDto('https://test/dav/uuid6', $etag),
|
||||
]), collect([$contact]))
|
||||
->wait();
|
||||
'uuid6' => new ContactDto('uuid6', $etag),
|
||||
]), collect([$contact]));
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(PushVCard::class, $batch);
|
||||
$dto = $this->getPrivateValue($batch, 'contact');
|
||||
$this->assertInstanceOf(ContactPushDto::class, $dto);
|
||||
$this->assertEquals('uuid3', $dto->uri);
|
||||
$this->assertEquals(2, $dto->mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@ namespace Tests\Unit\Services\DavClient\Utils;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Mockery\MockInterface;
|
||||
use App\Jobs\Dav\PushVCard;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\Api\DAV\CardEtag;
|
||||
use Tests\Helpers\DavTester;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
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;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use App\Services\DavClient\Utils\AddressBookContactsPush;
|
||||
use App\Http\Controllers\DAV\Backend\CardDAV\CardDAVBackend;
|
||||
@@ -48,7 +49,7 @@ class AddressBookContactsPushTest extends TestCase
|
||||
$backend = $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) {
|
||||
$mock->shouldReceive('getCard')
|
||||
->withArgs(function ($name, $uri) {
|
||||
$this->assertEquals($uri, 'https://test/dav/uricontact2');
|
||||
$this->assertEquals($uri, 'uricontact2');
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -65,20 +66,23 @@ class AddressBookContactsPushTest extends TestCase
|
||||
->andReturn('uricontact1');
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addResponse('https://test/dav/uricontact2', new Response(200, ['Etag' => $etag]), $card, 'PUT');
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsPush())
|
||||
$batchs = (new AddressBookContactsPush())
|
||||
->execute(new SyncDto($subscription, $client, $backend), collect([
|
||||
'https://test/dav/uricontact1' => new ContactDto('https://test/dav/uricontact1', $etag),
|
||||
]), [
|
||||
'added' => ['https://test/dav/uricontact2'],
|
||||
])
|
||||
->wait();
|
||||
'added' => ['uricontact2'],
|
||||
]);
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(PushVCard::class, $batch);
|
||||
$dto = $this->getPrivateValue($batch, 'contact');
|
||||
$this->assertInstanceOf(ContactPushDto::class, $dto);
|
||||
$this->assertEquals('uricontact2', $dto->uri);
|
||||
$this->assertEquals(0, $dto->mode);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
@@ -106,7 +110,7 @@ class AddressBookContactsPushTest extends TestCase
|
||||
$backend = $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) {
|
||||
$mock->shouldReceive('getUuid')
|
||||
->withArgs(function ($uri) {
|
||||
$this->assertStringStartsWith('https://test/dav/uricontact', $uri);
|
||||
$this->assertStringContainsString('uricontact', $uri);
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -115,7 +119,7 @@ class AddressBookContactsPushTest extends TestCase
|
||||
});
|
||||
$mock->shouldReceive('getCard')
|
||||
->withArgs(function ($name, $uri) {
|
||||
$this->assertEquals($uri, 'https://test/dav/uricontact2');
|
||||
$this->assertEquals($uri, 'uricontact2');
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -125,19 +129,22 @@ class AddressBookContactsPushTest extends TestCase
|
||||
]);
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addResponse('https://test/dav/uricontact2', new Response(200, ['Etag' => $etag]), $card, 'PUT', ['If-Match' => $etag]);
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsPush())
|
||||
$batchs = (new AddressBookContactsPush())
|
||||
->execute(new SyncDto($subscription, $client, $backend), collect([
|
||||
'https://test/dav/uricontact1' => new ContactDto('https://test/dav/uricontact1', $etag),
|
||||
]), [
|
||||
'modified' => ['https://test/dav/uricontact2'],
|
||||
])
|
||||
->wait();
|
||||
'modified' => ['uricontact2'],
|
||||
]);
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(PushVCard::class, $batch);
|
||||
$dto = $this->getPrivateValue($batch, 'contact');
|
||||
$this->assertInstanceOf(ContactPushDto::class, $dto);
|
||||
$this->assertEquals('uricontact2', $dto->uri);
|
||||
$this->assertEquals(1, $dto->mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use Tests\Api\DAV\CardEtag;
|
||||
use Tests\Helpers\DavTester;
|
||||
use App\Models\User\SyncToken;
|
||||
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;
|
||||
@@ -61,21 +62,22 @@ class AddressBookContactsUpdaterMissedTest extends TestCase
|
||||
->andReturn($etag);
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addressMultiGet($etag, $card, 'https://test/dav/uuid2');
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsUpdaterMissed())
|
||||
$batchs = (new AddressBookContactsUpdaterMissed())
|
||||
->execute(new SyncDto($subscription, $client, $backend), collect([
|
||||
[
|
||||
'uuid' => 'uuid1',
|
||||
],
|
||||
]), collect([
|
||||
'https://test/dav/uuid2' => new ContactDto('https://test/dav/uuid2', $etag),
|
||||
]))
|
||||
->wait();
|
||||
]));
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $batch);
|
||||
$hrefs = $this->getPrivateValue($batch, 'hrefs');
|
||||
$this->assertEquals(['https://test/dav/uuid2'], $hrefs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
namespace Tests\Unit\Services\DavClient\Utils;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Jobs\Dav\GetVCard;
|
||||
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 App\Jobs\Dav\GetMultipleVCard;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Dav\DavClient;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
@@ -55,18 +56,19 @@ class AddressBookContactsUpdaterTest extends TestCase
|
||||
->andReturn($etag);
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addressMultiGet($etag, $card, 'https://test/dav/uuid2');
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsUpdater())
|
||||
$batchs = (new AddressBookContactsUpdater())
|
||||
->execute(new SyncDto($subscription, $client, $backend), collect([
|
||||
'https://test/dav/uuid2' => new ContactDto('https://test/dav/uuid2', $etag),
|
||||
]))
|
||||
->wait();
|
||||
]));
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $batch);
|
||||
$hrefs = $this->getPrivateValue($batch, 'hrefs');
|
||||
$this->assertEquals(['https://test/dav/uuid2'], $hrefs);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
@@ -121,17 +123,19 @@ class AddressBookContactsUpdaterTest extends TestCase
|
||||
->andReturn($etag);
|
||||
});
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->addResponse('https://test/dav/uuid2', new Response(200, [], $card), null, 'GET');
|
||||
$tester = new DavTester();
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
(new AddressBookContactsUpdater())
|
||||
$batchs = (new AddressBookContactsUpdater())
|
||||
->execute(new SyncDto($subscription, $client, $backend), collect([
|
||||
'https://test/dav/uuid2' => new ContactDto('https://test/dav/uuid2', $etag),
|
||||
]))
|
||||
->wait();
|
||||
]));
|
||||
|
||||
$tester->assert();
|
||||
$this->assertCount(1, $batchs);
|
||||
$batch = $batchs->first();
|
||||
$this->assertInstanceOf(GetVCard::class, $batch);
|
||||
$dto = $this->getPrivateValue($batch, 'contact');
|
||||
$this->assertInstanceOf(ContactDto::class, $dto);
|
||||
$this->assertEquals('https://test/dav/uuid2', $dto->uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class AddressBookGetterTest extends TestCase
|
||||
->addressBookBaseUri()
|
||||
->capabilities()
|
||||
->displayName();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
$result = (new AddressBookGetter())
|
||||
->execute($client);
|
||||
|
||||
@@ -47,7 +47,7 @@ class AddressBookGetterTest extends TestCase
|
||||
$tester = (new DavTester())
|
||||
->serviceUrl()
|
||||
->optionsFail();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(DavServerNotCompliantException::class);
|
||||
(new AddressBookGetter())
|
||||
@@ -62,7 +62,7 @@ class AddressBookGetterTest extends TestCase
|
||||
->optionsOk()
|
||||
->userPrincipalEmpty();
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(DavServerNotCompliantException::class);
|
||||
(new AddressBookGetter())
|
||||
@@ -78,7 +78,7 @@ class AddressBookGetterTest extends TestCase
|
||||
->userPrincipal()
|
||||
->addressbookEmpty();
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(DavServerNotCompliantException::class);
|
||||
(new AddressBookGetter())
|
||||
@@ -94,7 +94,7 @@ class AddressBookGetterTest extends TestCase
|
||||
->userPrincipal()
|
||||
->addressbookHome()
|
||||
->resourceTypeHomeOnly();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(DavClientException::class);
|
||||
(new AddressBookGetter())
|
||||
|
||||
@@ -9,7 +9,9 @@ use Tests\Helpers\DavTester;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use App\Models\User\SyncToken;
|
||||
use App\Models\Contact\Contact;
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use Illuminate\Bus\PendingBatch;
|
||||
use App\Jobs\Dav\GetMultipleVCard;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use App\Models\Account\AddressBookSubscription;
|
||||
use App\Services\DavClient\Utils\Dav\DavClient;
|
||||
use App\Services\DavClient\Utils\Model\SyncDto;
|
||||
@@ -28,6 +30,8 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
/** @test */
|
||||
public function it_sync_empty_changes()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$this->mock(AddressBookContactsUpdater::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('execute')
|
||||
->once()
|
||||
@@ -39,7 +43,7 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'))
|
||||
->getSynctoken($subscription->syncToken);
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
->execute(new SyncDto($subscription, $client, $backend));
|
||||
@@ -50,6 +54,8 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
/** @test */
|
||||
public function it_sync_no_changes()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$this->mock(AddressBookContactsUpdater::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('execute')
|
||||
->once()
|
||||
@@ -63,7 +69,7 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
$tester->getSynctoken('"test21"')
|
||||
->getSyncCollection('test20');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
->execute(new SyncDto($subscription, $client, $backend));
|
||||
@@ -74,6 +80,8 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
/** @test */
|
||||
public function it_sync_changes_added_local_contact()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$subscription = $this->getSubscription();
|
||||
$backend = new CardDAVBackend($subscription->user);
|
||||
|
||||
@@ -87,7 +95,7 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
$tester->getSynctoken('"token"')
|
||||
->getSyncCollection('token', '"test2"');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$sync = new SyncDto($subscription, $client, $backend);
|
||||
$this->mock(AddressBookContactsUpdater::class, function (MockInterface $mock) use ($sync) {
|
||||
@@ -100,9 +108,7 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
|
||||
return true;
|
||||
})
|
||||
->andReturn(new Promise(function () {
|
||||
return true;
|
||||
}));
|
||||
->andReturn(collect());
|
||||
});
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
@@ -111,9 +117,48 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
$tester->assert();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_sync_changes_added_local_contact_batched()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$subscription = $this->getSubscription();
|
||||
$backend = new CardDAVBackend($subscription->user);
|
||||
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $subscription->account_id,
|
||||
'address_book_id' => $subscription->address_book_id,
|
||||
'uuid' => 'd403af1c-8492-4e9b-9833-cf18c795dfa9',
|
||||
]);
|
||||
|
||||
$tester = (new DavTester('https://test/dav/addressbooks/user@test.com/contacts/'));
|
||||
$tester->getSynctoken('"token"')
|
||||
->getSyncCollection('token', '"test2"');
|
||||
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$sync = new SyncDto($subscription, $client, $backend);
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
->execute($sync);
|
||||
|
||||
$tester->assert();
|
||||
|
||||
Bus::assertBatched(function (PendingBatch $batch) {
|
||||
$this->assertCount(1, $batch->jobs);
|
||||
$job = $batch->jobs[0];
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $job);
|
||||
$this->assertEquals(['https://test/dav/addressbooks/user@test.com/contacts/uuid'], $this->getPrivateValue($job, 'hrefs'));
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_forcesync_changes_added_local_contact()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$subscription = $this->getSubscription();
|
||||
$backend = new CardDAVBackend($subscription->user);
|
||||
|
||||
@@ -142,7 +187,7 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</card:addressbook-query>\n", 'REPORT');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$sync = new SyncDto($subscription, $client, $backend);
|
||||
$this->mock(AddressBookContactsUpdaterMissed::class, function (MockInterface $mock) use ($sync, $contact, $etag) {
|
||||
@@ -156,16 +201,12 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
|
||||
return true;
|
||||
})
|
||||
->andReturn(new Promise(function () {
|
||||
return true;
|
||||
}));
|
||||
->andReturn(collect());
|
||||
});
|
||||
$this->mock(AddressBookContactsPushMissed::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('execute')
|
||||
->once()
|
||||
->andReturn(new Promise(function () {
|
||||
return true;
|
||||
}));
|
||||
->andReturn(collect());
|
||||
});
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
@@ -174,6 +215,58 @@ class AddressBookSynchronizerTest extends TestCase
|
||||
$tester->assert();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_forcesync_changes_added_local_contact_batched()
|
||||
{
|
||||
Bus::fake();
|
||||
|
||||
$subscription = $this->getSubscription();
|
||||
$backend = new CardDAVBackend($subscription->user);
|
||||
|
||||
$contact = factory(Contact::class)->create([
|
||||
'account_id' => $subscription->account_id,
|
||||
'address_book_id' => $subscription->address_book_id,
|
||||
'uuid' => 'd403af1c-8492-4e9b-9833-cf18c795dfa9',
|
||||
]);
|
||||
$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().
|
||||
'<d:response>'.
|
||||
'<d:href>https://test/dav/uuid1</d:href>'.
|
||||
'<d:propstat>'.
|
||||
'<d:prop>'.
|
||||
"<d:getetag>$etag</d:getetag>".
|
||||
'</d:prop>'.
|
||||
'<d:status>HTTP/1.1 200 OK</d:status>'.
|
||||
'</d:propstat>'.
|
||||
'</d:response>'.
|
||||
'</d:multistatus>'), '<?xml version="1.0" encoding="UTF-8"?>'."\n".
|
||||
'<card:addressbook-query xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">'.
|
||||
'<d:prop>'.
|
||||
'<d:getetag/>'.
|
||||
'</d:prop>'.
|
||||
"</card:addressbook-query>\n", 'REPORT');
|
||||
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$sync = new SyncDto($subscription, $client, $backend);
|
||||
|
||||
(new AddressBookSynchronizer())
|
||||
->execute($sync, true);
|
||||
|
||||
$tester->assert();
|
||||
|
||||
Bus::assertBatched(function (PendingBatch $batch) {
|
||||
$this->assertCount(1, $batch->jobs);
|
||||
$job = $batch->jobs[0];
|
||||
$this->assertInstanceOf(GetMultipleVCard::class, $job);
|
||||
$this->assertEquals(['https://test/dav/uuid1'], $this->getPrivateValue($job, 'hrefs'));
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private function getSubscription()
|
||||
{
|
||||
$subscription = AddressBookSubscription::factory()->create();
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Tests\Unit\Services\DavClient\Utils\Dav;
|
||||
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\Foundation\Testing\DatabaseTransactions;
|
||||
@@ -14,29 +16,17 @@ class DavClientTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_creates_client()
|
||||
{
|
||||
$client = new DavClient([
|
||||
'base_uri' => 'test',
|
||||
'username' => 'user',
|
||||
'password' => 'pass',
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(\Sabre\DAV\Xml\Service::class, $client->xml);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_no_baseuri()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
new DavClient([]);
|
||||
app(DavClient::class)->init([]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_accept_guzzle_client()
|
||||
{
|
||||
$client = new DavClient([], new \GuzzleHttp\Client());
|
||||
$client = app(DavClient::class)->init([], new \GuzzleHttp\Client());
|
||||
$this->assertInstanceOf(DavClient::class, $client);
|
||||
}
|
||||
|
||||
@@ -47,7 +37,7 @@ class DavClientTest extends TestCase
|
||||
->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 = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->options();
|
||||
$this->assertEquals([], $result);
|
||||
@@ -66,7 +56,7 @@ class DavClientTest extends TestCase
|
||||
{
|
||||
$tester = (new DavTester())
|
||||
->serviceUrl();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getServiceUrl();
|
||||
|
||||
@@ -80,7 +70,7 @@ class DavClientTest extends TestCase
|
||||
$tester = (new DavTester())
|
||||
->addResponse('https://test/.well-known/carddav', new Response(200), null, 'GET')
|
||||
->nonStandardServiceUrl();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getServiceUrl();
|
||||
|
||||
@@ -94,7 +84,7 @@ class DavClientTest extends TestCase
|
||||
$tester = (new DavTester())
|
||||
->addResponse('https://test/.well-known/carddav', new Response(404), null, 'GET')
|
||||
->nonStandardServiceUrl();
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getServiceUrl();
|
||||
|
||||
@@ -107,7 +97,7 @@ class DavClientTest extends TestCase
|
||||
{
|
||||
$tester = (new DavTester())
|
||||
->addResponse('https://test/.well-known/carddav', new Response(500), null, 'GET');
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$client->getServiceUrl();
|
||||
@@ -117,7 +107,7 @@ class DavClientTest extends TestCase
|
||||
public function it_get_base_uri()
|
||||
{
|
||||
$tester = (new DavTester());
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getBaseUri();
|
||||
|
||||
@@ -132,7 +122,7 @@ class DavClientTest extends TestCase
|
||||
public function it_set_base_uri()
|
||||
{
|
||||
$tester = (new DavTester());
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->setBaseUri('https://new')
|
||||
->getBaseUri();
|
||||
@@ -162,7 +152,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</d:propfind>\n", 'PROPFIND');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->propFind('https://test/test', ['{DAV:}test']);
|
||||
|
||||
@@ -194,7 +184,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</d:propfind>\n", 'PROPFIND');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getProperty('{DAV:}test', 'https://test/test');
|
||||
|
||||
@@ -231,7 +221,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</d:propfind>\n", 'PROPFIND');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->getSupportedReportSet();
|
||||
|
||||
@@ -265,7 +255,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</d:sync-collection>\n", 'REPORT');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->syncCollectionAsync('https://test/test', ['{DAV:}test'], '')
|
||||
->wait();
|
||||
@@ -308,7 +298,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</d:sync-collection>\n", 'REPORT');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->syncCollectionAsync('https://test/test', ['{DAV:}test'], '"00000-abcd0"')
|
||||
->wait();
|
||||
@@ -328,9 +318,8 @@ class DavClientTest extends TestCase
|
||||
/** @test */
|
||||
public function it_run_addressbook_multiget_report()
|
||||
{
|
||||
$tester = (new DavTester());
|
||||
|
||||
$tester->addResponse('https://test/test', new Response(200, [], $tester->multistatusHeader().
|
||||
Http::fake([
|
||||
'https://test/*' => Http::response(DavTester::multistatusHeader().
|
||||
'<d:response>'.
|
||||
'<d:href>href</d:href>'.
|
||||
'<d:propstat>'.
|
||||
@@ -341,22 +330,27 @@ class DavClientTest extends TestCase
|
||||
'<d:status>HTTP/1.1 200 OK</d:status>'.
|
||||
'</d:propstat>'.
|
||||
'</d:response>'.
|
||||
'</d:multistatus>'), '<?xml version="1.0" encoding="UTF-8"?>'."\n".
|
||||
'<card:addressbook-multiget xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">'.
|
||||
'<d:prop>'.
|
||||
'<d:test/>'.
|
||||
'</d:prop>'.
|
||||
'<d:href>https://test/contacts/1</d:href>'.
|
||||
"</card:addressbook-multiget>\n", 'REPORT');
|
||||
'</d:multistatus>'),
|
||||
]);
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
|
||||
$result = $client->addressbookMultigetAsync('https://test/test', ['{DAV:}test'], [
|
||||
$result = app(DavClient::class)->addressbookMultiget(Http::baseUrl('https://test/test'), ['{DAV:}test'], [
|
||||
'https://test/contacts/1',
|
||||
])
|
||||
->wait();
|
||||
]);
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
$this->assertEquals('https://test/test/', $request->url());
|
||||
$this->assertEquals('REPORT', $request->method());
|
||||
$this->assertEquals('<?xml version="1.0" encoding="UTF-8"?>'."\n".
|
||||
'<card:addressbook-multiget xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">'.
|
||||
'<d:prop>'.
|
||||
'<d:test/>'.
|
||||
'</d:prop>'.
|
||||
'<d:href>https://test/contacts/1</d:href>'.
|
||||
"</card:addressbook-multiget>\n", $request->body());
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$tester->assert();
|
||||
$this->assertEquals([
|
||||
'href' => [
|
||||
200 => [
|
||||
@@ -390,7 +384,7 @@ class DavClientTest extends TestCase
|
||||
'</d:prop>'.
|
||||
"</card:addressbook-query>\n", 'REPORT');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->addressbookQueryAsync('https://test/test', ['{DAV:}test'])
|
||||
->wait();
|
||||
@@ -430,7 +424,7 @@ class DavClientTest extends TestCase
|
||||
' </d:set>'."\n".
|
||||
"</d:propertyupdate>\n", 'PROPPATCH');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$result = $client->propPatchAsync('https://test/test', ['{DAV:}test' => 'value'])
|
||||
->wait();
|
||||
@@ -470,11 +464,11 @@ class DavClientTest extends TestCase
|
||||
' </d:set>'."\n".
|
||||
"</d:propertyupdate>\n", 'PROPPATCH');
|
||||
|
||||
$client = new DavClient([], $tester->getClient());
|
||||
$client = app(DavClient::class)->init([], $tester->getClient());
|
||||
|
||||
$this->expectException(DavClientException::class);
|
||||
$this->expectExceptionMessage('PROPPATCH failed. The following properties errored: {DAV:}test (405), {DAV:}excerpt (500)');
|
||||
$result = $client->propPatchAsync('https://test/test', [
|
||||
$client->propPatchAsync('https://test/test', [
|
||||
'{DAV:}test' => 'value',
|
||||
'{DAV:}excerpt' => 'value',
|
||||
])
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\DavClient\Utils\Model;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use App\Services\DavClient\Utils\Model\ContactUpdateDto;
|
||||
|
||||
class ContactUpdateDtoTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_create_dto_string()
|
||||
{
|
||||
$dto = new ContactUpdateDto('uri', 'etag', 'card');
|
||||
$this->assertEquals('uri', $dto->uri);
|
||||
$this->assertEquals('etag', $dto->etag);
|
||||
$this->assertEquals('card', $dto->card);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_create_dto_resource()
|
||||
{
|
||||
$resource = fopen(__DIR__.'/stub.vcf', 'r');
|
||||
$dto = new ContactUpdateDto('uri', 'etag', $resource);
|
||||
$this->assertEquals('uri', $dto->uri);
|
||||
$this->assertEquals('etag', $dto->etag);
|
||||
$this->assertEquals('card', $dto->card);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
card
|
||||
Reference in New Issue
Block a user