Skip to content

Commit

Permalink
add address import
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin committed Nov 2, 2023
1 parent 98e1a16 commit 85af3fb
Show file tree
Hide file tree
Showing 4 changed files with 362 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @implements ExportVCardResource<Contact>
*/
#[Order(20)]
class ExportAdr implements ExportVCardResource
class ExportAddress implements ExportVCardResource
{
public function getType(): string
{
Expand Down
160 changes: 160 additions & 0 deletions app/Domains/Contact/ManageContact/Dav/ImportAddress.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

namespace App\Domains\Contact\ManageContact\Dav;

use App\Domains\Contact\Dav\Importer;
use App\Domains\Contact\Dav\ImportVCardResource;
use App\Domains\Contact\Dav\Order;
use App\Domains\Contact\Dav\VCardResource;
use App\Domains\Contact\ManageContactAddresses\Services\AssociateAddressToContact;
use App\Domains\Contact\ManageContactAddresses\Services\RemoveAddressFromContact;
use App\Domains\Settings\ManageAddressTypes\Services\CreateAddressType;
use App\Domains\Vault\ManageAddresses\Services\CreateAddress;
use App\Domains\Vault\ManageAddresses\Services\UpdateAddress;
use App\Exceptions\NotEnoughPermissionException;
use App\Models\Address;
use App\Models\AddressType;
use App\Models\Contact;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Arr;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property;

#[Order(20)]
class ImportAddress extends Importer implements ImportVCardResource
{
/**
* Can import Contact addresses.
*/
public function can(VCard $vcard): bool
{
$kind = (string) ($vcard->KIND || $vcard->select('X-ADDRESSBOOKSERVER-KIND'));
if (! empty($kind) && $kind !== 'individual') {
return false;
}

return true;
}

/**
* Import Contact addresses.
*/
public function import(VCard $vcard, ?VCardResource $result): ?VCardResource
{
/** @var Contact $contact */
$contact = $result;

$addresses = $contact->addresses()
->wherePivot('is_past_address', false)
->get();
$adr = $vcard->select('ADR');

for ($i = 0; $i < count($adr) || $i < $addresses->count(); $i++) {
if ($i < count($adr)) {
$addressType = $this->getAddressType($adr[$i]);

if ($i < $addresses->count()) {
$this->updateAddress($adr[$i], $addresses[$i], $addressType);
} else {
$this->createAddress($contact, $adr[$i], $addressType);
}
} elseif ($i < $addresses->count()) {
$this->removeAddress($contact, $addresses[$i]);
}
}

return $contact->refresh();
}

private function getAddressType(Property $adr): ?AddressType
{
$type = Arr::get($adr->parameters(), 'TYPE');

if ($type) {
try {
return AddressType::where([
'account_id' => $this->account()->id,
'name' => $type->getValue(),
])->firstOrFail();
} catch (ModelNotFoundException) {
try {
return (new CreateAddressType)->execute([
'account_id' => $this->account()->id,
'author_id' => $this->author()->id,
'name' => $type->getValue(),
]);
} catch (NotEnoughPermissionException) {
// catch
}
}
}

return null;
}

private function updateAddress(Property $adr, Address $address, ?AddressType $addressType)
{
(new UpdateAddress)->execute([
'account_id' => $this->account()->id,
'vault_id' => $this->vault()->id,
'author_id' => $this->author()->id,
'address_id' => $address->id,
'address_type_id' => optional($addressType)->id,
'line_1' => $adr->getParts()[1],
'line_2' => $adr->getParts()[2],
'city' => $adr->getParts()[3],
'province' => $adr->getParts()[4],
'postal_code' => $adr->getParts()[5],
'country' => $adr->getParts()[6],
]);
}

private function createAddress(Contact $contact, Property $adr, ?AddressType $addressType)
{
$address = Address::where([
'vault_id' => $this->vault()->id,
'address_type_id' => optional($addressType)->id,
'line_1' => $adr->getParts()[1],
'line_2' => $adr->getParts()[2],
'city' => $adr->getParts()[3],
'province' => $adr->getParts()[4],
'postal_code' => $adr->getParts()[5],
'country' => $adr->getParts()[6],
])->first();

if ($address === null) {
$address = (new CreateAddress)->execute([
'account_id' => $this->account()->id,
'vault_id' => $this->vault()->id,
'author_id' => $this->author()->id,
'address_type_id' => optional($addressType)->id,
'line_1' => $adr->getParts()[1],
'line_2' => $adr->getParts()[2],
'city' => $adr->getParts()[3],
'province' => $adr->getParts()[4],
'postal_code' => $adr->getParts()[5],
'country' => $adr->getParts()[6],
]);
}

(new AssociateAddressToContact)->execute([
'account_id' => $this->account()->id,
'vault_id' => $this->vault()->id,
'author_id' => $this->author()->id,
'contact_id' => $contact->id,
'address_id' => $address->id,
'is_past_address' => false,
]);
}

private function removeAddress(Contact $contact, Address $address)
{
(new RemoveAddressFromContact)->execute([
'account_id' => $this->account()->id,
'vault_id' => $this->vault()->id,
'author_id' => $this->author()->id,
'contact_id' => $contact->id,
'address_id' => $address->id,
]);
}
}
48 changes: 48 additions & 0 deletions tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Tests\Unit\Domains\Settings\ManageGenders\Dav;

use App\Domains\Contact\ManageContact\Dav\ExportAddress;
use App\Models\Address;
use App\Models\Contact;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Str;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\PHPUnitAssertions;
use Tests\TestCase;
use Tests\Unit\Domains\Contact\DAV\CardEtag;

class ExportAddressTest extends TestCase
{
use CardEtag,
DatabaseTransactions,
PHPUnitAssertions;

/** @var int */
const defaultPropsCount = 3;

#[Group('dav')]
#[Test]
public function it_adds_address_to_vcard()

Check failure on line 28 in tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php

View workflow job for this annotation

GitHub Actions / JUnit Test Report

tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php.it_adds_address_to_vcard

Tests\Unit\Domains\Settings\ManageGenders\Dav\ExportAddressTest::it_adds_address_to_vcard Failed asserting that 'BEGIN:VCARD\r\n
Raw output
Tests\Unit\Domains\Settings\ManageGenders\Dav\ExportAddressTest::it_adds_address_to_vcard
Failed asserting that 'BEGIN:VCARD\r\n
VERSION:4.0\r\n
PRODID:-//Sabre//Sabre VObject 4.5.3//EN\r\n
UID:sabre-vobject-960dbb44-9b77-4b93-9d48-c38fbc115869\r\n
ADR;TYPE=Cary Douglas:;6102 Corwin Locks Apt. 481;996 Darby Underpass Apt. \r\n
 984;New Dinachester;Augusta Wiza;02996;1\r\n
END:VCARD\r\n
' [ASCII](length: 255) contains " 84;New Dinachester;Augusta Wiza;02996;1" [ASCII](length: 40).

tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php:46
vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:174
{
$user = $this->createUser();
$vault = $this->createVaultUser($user);
$contact = Contact::factory()->random()->create(['vault_id' => $vault->id]);
$address = Address::factory()->create(['vault_id' => $vault->id]);
$address->contacts()->attach($contact->id);

$vCard = new VCard();
(new ExportAddress)->export($contact, $vCard);

$this->assertCount(
self::defaultPropsCount + 1,
$vCard->children()
);

$text = "ADR;TYPE={$address->addressType->name}:;{$address->line_1};{$address->line_2};{$address->city};{$address->province};{$address->postal_code};{$address->country}";
$this->assertStringContainsString(Str::substr($text, 0, 75), $vCard->serialize());
$this->assertStringContainsString(' '.Str::substr($text, 76), $vCard->serialize());

Check failure on line 46 in tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php

View workflow job for this annotation

GitHub Actions / Testsuite Unit with PHP 8.2 + sqlite

Failed asserting that 'BEGIN:VCARD\r\n

Check failure on line 46 in tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php

View workflow job for this annotation

GitHub Actions / Testsuite Unit with PHP 8.2 + mysql

Failed asserting that 'BEGIN:VCARD\r\n

Check failure on line 46 in tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php

View workflow job for this annotation

GitHub Actions / Testsuite Unit with PHP 8.2 + pgsql

Failed asserting that 'BEGIN:VCARD\r\n
}
}
153 changes: 153 additions & 0 deletions tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace Tests\Unit\Domains\Contact\ManageContact\Dav;

use App\Domains\Contact\Dav\Services\ImportVCard;
use App\Domains\Contact\ManageContact\Dav\ImportAddress;
use App\Models\Address;
use App\Models\AddressType;
use App\Models\Contact;
use App\Models\Vault;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\PHPUnitAssertions;
use Tests\TestCase;

class ImportAddressTest extends TestCase
{
use DatabaseTransactions,
PHPUnitAssertions;

#[Group('dav')]
#[Test]
public function it_imports_new_address()
{
$user = $this->createUser();
$vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE);
$importVCard = new ImportVCard();
$importVCard->author = $user;
$importVCard->vault = $vault;
$importer = new ImportAddress();
$importer->setContext($importVCard);

$contact = Contact::factory()->create([
'vault_id' => $vault->id,
]);

$vcard = new VCard([
'ADR' => [
'',
'line 1',
'line 2',
'city',
'province',
'postal code',
'country',
],
]);

$contact = $importer->import($vcard, $contact);

$this->assertCount(1, $contact->addresses);
$address = $contact->addresses->first();
$this->assertEquals('line 1', $address->line_1);
$this->assertEquals('line 2', $address->line_2);
$this->assertEquals('city', $address->city);
$this->assertEquals('province', $address->province);
$this->assertEquals('postal code', $address->postal_code);
$this->assertEquals('country', $address->country);
}

#[Group('dav')]
#[Test]
public function it_imports_address_type()
{
$user = $this->createUser();
$vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE);
$importVCard = new ImportVCard();
$importVCard->author = $user;
$importVCard->vault = $vault;
$importer = new ImportAddress();
$importer->setContext($importVCard);

$contact = Contact::factory()->create([
'vault_id' => $vault->id,
]);

AddressType::factory()->create([
'account_id' => $user->account_id,
'name' => 'home',
]);

$vcard = new VCard();
$vcard->add('ADR', [
'',
'line 1',
'line 2',
'city',
'province',
'postal code',
'country',
], [
'TYPE' => 'home',
]);

$contact = $importer->import($vcard, $contact);

$this->assertCount(1, $contact->addresses);
$address = $contact->addresses->first();

$this->assertNotNull($address->addressType);
$this->assertEquals('home', $address->addressType->name);
}

#[Group('dav')]
#[Test]
public function it_imports_new_address_and_remove_old()
{
$user = $this->createUser();
$vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE);
$importVCard = new ImportVCard();
$importVCard->author = $user;
$importVCard->vault = $vault;
$importer = new ImportAddress();
$importer->setContext($importVCard);

$contact = Contact::factory()->create([
'vault_id' => $vault->id,
]);
$addresses = Address::factory(2)->create([
'vault_id' => $vault->id,
]);
foreach ($addresses as $address) {
$contact->addresses()->attach($address,
['is_past_address' => false]
);
}

$vcard = new VCard([
'ADR' => [
'',
'line 1',
'line 2',
'city',
'province',
'postal code',
'country',
],
]);

$contact = $importer->import($vcard, $contact);

$this->assertCount(1, $contact->addresses);
$address = $contact->addresses->first();
$this->assertEquals('line 1', $address->line_1);
$this->assertEquals('line 2', $address->line_2);
$this->assertEquals('city', $address->city);
$this->assertEquals('province', $address->province);
$this->assertEquals('postal code', $address->postal_code);
$this->assertEquals('country', $address->country);
}
}

0 comments on commit 85af3fb

Please sign in to comment.