Add member management for mailman

This commit is contained in:
Philipp Lang 2023-06-12 17:07:56 +02:00
parent a74d0936a2
commit 904ec829c1
3 changed files with 75 additions and 12 deletions

View File

@ -3,6 +3,7 @@
namespace App\Mailgateway\Types;
use App\Maildispatcher\Data\MailEntry;
use App\Mailman\Exceptions\MailmanServiceException;
use App\Mailman\Support\MailmanService;
use Illuminate\Support\Collection;
@ -28,7 +29,7 @@ class MailmanType extends Type
public function works(): bool
{
return app(MailmanService::class)->setCredentials($this->url, $this->user, $this->password)->check();
return $this->service()->check();
}
/**
@ -66,12 +67,18 @@ class MailmanType extends Type
public function search(string $name, string $domain, string $email): ?MailEntry
{
return null;
$list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}");
throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found");
$member = $this->service()->members($list)->first(fn ($member) => $member->email === $email);
return $member ? MailEntry::from(['email' => $member->email]) : null;
}
public function add(string $name, string $domain, string $email): void
{
return;
$list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}");
throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found");
$this->service()->addMember($list, $email);
}
/**
@ -79,10 +86,23 @@ class MailmanType extends Type
*/
public function list(string $name, string $domain): Collection
{
return collect([]);
$list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}");
throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found");
return collect($this->service()->members($list)->map(fn ($member) => MailEntry::from(['email' => $member->email]))->all());
}
public function remove(string $name, string $domain, string $email): void
{
$list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}");
throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found");
$member = $this->service()->members($list)->first(fn ($member) => $member->email === $email);
throw_if(!$member, MailmanServiceException::class, 'Member for removing not found');
$this->service()->removeMember($member);
}
private function service(): MailmanService
{
return app(MailmanService::class)->setCredentials($this->url, $this->user, $this->password);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Mailman\Data;
use Spatie\LaravelData\Attributes\MapName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
#[MapName(SnakeCaseMapper::class)]
class Member extends Data
{
public function __construct(
public string $email,
public string $listId,
public string $memberId,
) {
}
}

View File

@ -3,6 +3,7 @@
namespace App\Mailman\Support;
use App\Mailman\Data\MailingList;
use App\Mailman\Data\Member;
use App\Mailman\Exceptions\MailmanServiceException;
use App\Mailman\MailmanSettings;
use Illuminate\Http\Client\ConnectionException;
@ -43,26 +44,50 @@ class MailmanService
}
/**
* @return LazyCollection<int, string>
* @return LazyCollection<int, Member>
*/
public function members(string $listId): LazyCollection
public function members(MailingList $list): LazyCollection
{
return app(Paginator::class)->result(
fn ($page) => $this->http()->get("/lists/{$listId}/roster/member?page={$page}&count=10"),
function ($response) use ($listId) {
throw_unless($response->ok(), MailmanServiceException::class, 'Fetching members for listId '.$listId.' failed.');
/** @var array<int, array{email: string}>|null */
$entries = data_get($response->json(), 'entries');
fn ($page) => $this->http()->get("/lists/{$list->listId}/roster/member?page={$page}&count=10"),
function ($response) use ($list) {
throw_unless($response->ok(), MailmanServiceException::class, 'Fetching members for listId '.$list->listId.' failed.');
/** @var array<int, array{email: string, self_link: string}>|null */
$entries = data_get($response->json(), 'entries', []);
throw_if(is_null($entries), MailmanServiceException::class, 'Failed getting member list from response');
foreach ($entries as $entry) {
yield $entry['email'];
yield Member::from([
...$entry,
'member_id' => strrev(preg_split('/\//', strrev($entry['self_link']))[0]),
]);
}
},
fn ($response) => data_get($response->json(), 'total_size')
);
}
public function addMember(MailingList $list, string $email): void
{
$response = $this->http()->post('members', [
'list_id' => $list->listId,
'subscriber' => $email,
'pre_verified' => 'true',
'pre_approved' => 'true',
'send_welcome_message' => 'false',
'pre_confirmed' => 'true',
]);
throw_unless(201 === $response->status(), MailmanServiceException::class, 'Adding member '.$email.' to '.$list->listId.' failed');
}
public function removeMember(Member $member): void
{
$response = $this->http()->delete("members/{$member->memberId}");
throw_unless($response->ok(), MailmanServiceException::class, 'Removing member failed');
}
private function http(): PendingRequest
{
return Http::withBasicAuth($this->username, $this->password)->withOptions(['base_uri' => $this->baseUrl]);