Compare commits

...

1 Commits

Author SHA1 Message Date
philipp lang af848e51c6 --wip-- [skip ci] 2024-10-23 21:02:42 +02:00
11 changed files with 53 additions and 116 deletions

View File

@ -1,42 +0,0 @@
<?php
namespace App\Mailgateway\Casts;
use App\Mailgateway\Types\Type;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
/**
* @implements CastsAttributes<Type, Type>
*/
class TypeCast implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param mixed $value
* @param array<string, mixed> $attributes
*
* @return mixed
*/
public function get($model, string $key, $value, array $attributes)
{
$value = json_decode($value, true);
return app($value['cls'])->setParams($value['params']);
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param mixed $value
* @param array<string, mixed> $attributes
*
* @return mixed
*/
public function set($model, string $key, $value, array $attributes)
{
return json_encode($value);
}
}

View File

@ -2,11 +2,12 @@
namespace App\Mailgateway\Models; namespace App\Mailgateway\Models;
use App\Mailgateway\Casts\TypeCast; use App\Mailgateway\Types\Type as TypesType;
use Database\Factories\Mailgateway\Models\MailgatewayFactory; use Database\Factories\Mailgateway\Models\MailgatewayFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Nette\Utils\Type;
class Mailgateway extends Model class Mailgateway extends Model
{ {
@ -14,6 +15,6 @@ class Mailgateway extends Model
use HasFactory; use HasFactory;
use HasUuids; use HasUuids;
public $casts = ['type' => TypeCast::class]; public $casts = ['type' => TypesType::class];
public $guarded = []; public $guarded = [];
} }

View File

@ -28,7 +28,6 @@ class MailgatewayResource extends JsonResource
'domain' => $this->domain, 'domain' => $this->domain,
'type_human' => $this->type::name(), 'type_human' => $this->type::name(),
'works' => $this->type->works(), 'works' => $this->type->works(),
'type' => $this->type->toResource(),
'id' => $this->id, 'id' => $this->id,
'links' => [ 'links' => [
'update' => route('mailgateway.update', ['mailgateway' => $this->getModel()]), 'update' => route('mailgateway.update', ['mailgateway' => $this->getModel()]),

View File

@ -4,9 +4,15 @@ namespace App\Mailgateway\Types;
use App\Maildispatcher\Data\MailEntry; use App\Maildispatcher\Data\MailEntry;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Livewire\Wireable;
use Spatie\LaravelData\Concerns\WireableData;
use Spatie\LaravelData\Data;
abstract class Type abstract class Type extends Data implements Wireable
{ {
use WireableData;
abstract public static function name(): string; abstract public static function name(): string;
/** /**
@ -67,17 +73,6 @@ abstract class Type
])->toArray(); ])->toArray();
} }
/**
* @return array<string, array<string, mixed>>
*/
public function toResource(): array
{
return [
'cls' => get_class($this),
'params' => get_object_vars($this),
];
}
/** /**
* @param Collection<int, MailEntry> $results * @param Collection<int, MailEntry> $results
*/ */

View File

@ -3,6 +3,7 @@
namespace Database\Factories\Mailgateway\Models; namespace Database\Factories\Mailgateway\Models;
use App\Mailgateway\Models\Mailgateway; use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\LocalType;
use App\Mailgateway\Types\Type; use App\Mailgateway\Types\Type;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
@ -22,24 +23,14 @@ class MailgatewayFactory extends Factory
{ {
return [ return [
'name' => $this->faker->words(5, true), 'name' => $this->faker->words(5, true),
'type' => [ 'type' => new LocalType(),
'cls' => app('mail-gateways')->random(),
'params' => [],
],
'domain' => $this->faker->safeEmailDomain(), 'domain' => $this->faker->safeEmailDomain(),
]; ];
} }
/** public function type(Type $type): self
* @param class-string<Type> $type
* @param array<string, mixed> $params
*/
public function type(string $type, array $params): self
{ {
return $this->state(['type' => [ return $this->state(['type' => $type]);
'cls' => $type,
'params' => $params,
]]);
} }
public function name(string $name): self public function name(string $name): self

View File

@ -3,6 +3,7 @@
namespace Modules\Mailgateway\Components; namespace Modules\Mailgateway\Components;
use App\Mailgateway\Models\Mailgateway; use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\Type;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -15,9 +16,7 @@ class Form extends Component
public string $name = ''; public string $name = '';
public string $domain = ''; public string $domain = '';
public array $params = []; public ?Type $type = null;
#[Validate('required')]
public ?string $cls = null;
public Collection $types; public Collection $types;
public ?Mailgateway $model = null; public ?Mailgateway $model = null;
@ -26,19 +25,17 @@ class Form extends Component
return [ return [
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'domain' => 'required|string|max:255', 'domain' => 'required|string|max:255',
'cls' => ['required', 'string', 'max:255', Rule::in(app('mail-gateways'))], ...$this->type ? collect($this->type::rules())->mapWithKeys(fn ($rules, $key) => ["params.{$key}" => $rules]) : [],
'params' => 'present|array',
...$this->cls ? collect($this->cls::rules())->mapWithKeys(fn ($rules, $key) => ["params.{$key}" => $rules]) : [],
]; ];
} }
public function validationAttributes(): array public function validationAttributes(): array
{ {
return [ return [
'cls' => 'Typ', 'type' => 'Typ',
'name' => 'Beschreibung', 'name' => 'Beschreibung',
'domain' => 'Domain', 'domain' => 'Domain',
...$this->cls ? collect($this->cls::fieldNames())->mapWithKeys(fn ($attribute, $key) => ["params.{$key}" => $attribute]) : [], ...$this->type ? collect($this->type::fieldNames())->mapWithKeys(fn ($attribute, $key) => ["params.{$key}" => $attribute]) : [],
]; ];
} }
@ -53,19 +50,13 @@ class Form extends Component
$this->model = Mailgateway::find($id); $this->model = Mailgateway::find($id);
$this->name = $this->model->name; $this->name = $this->model->name;
$this->domain = $this->model->domain; $this->domain = $this->model->domain;
$this->cls = get_class($this->model->type); $this->type = $this->model->type;
$this->params = (array) $this->model->type;
} }
} }
public function updatedType(string $type): void
{
$this->params = $type::defaults();
}
public function fields(): array public function fields(): array
{ {
return $this->cls ? $this->cls::fields() : []; return $this->type ? $this->type::fields() : [];
} }
#[On('onStoreFromModal')] #[On('onStoreFromModal')]
@ -73,14 +64,14 @@ class Form extends Component
{ {
$this->validate(); $this->validate();
if (!app($this->cls)->setParams($this->params)->works()) { if (!$this->type->works()) {
throw ValidationException::withMessages(['connection' => 'Verbindung fehlgeschlagen.']); throw ValidationException::withMessages(['connection' => 'Verbindung fehlgeschlagen.']);
} }
$payload = [ $payload = [
'name' => $this->name, 'name' => $this->name,
'domain' => $this->domain, 'domain' => $this->domain,
'type' => ['cls' => $this->cls, 'params' => $this->params], 'type' => $this->type,
]; ];
if ($this->model) { if ($this->model) {
$this->model->update($payload); $this->model->update($payload);
@ -99,11 +90,11 @@ class Form extends Component
<form class="grid grid-cols-2 gap-3"> <form class="grid grid-cols-2 gap-3">
<x-form::text name="name" wire:model="name" label="Beschreibung" required /> <x-form::text name="name" wire:model="name" label="Beschreibung" required />
<x-form::text name="domain" wire:model="domain" label="Domain" required /> <x-form::text name="domain" wire:model="domain" label="Domain" required />
<x-form::select name="cls" wire:model.live="cls" label="Typ" :options="$types" required /> <x-form::select name="type" wire:model.live="type" label="Typ" :options="$types" required />
@foreach($this->fields() as $index => $field) @foreach($this->fields() as $index => $field)
<x-form::text <x-form::text
wire:key="index" wire:key="index"
wire:model="params.{{$field['name']}}" wire:model="$type->{$field['name']}"
:label="$field['label']" :label="$field['label']"
:type="$field['type']" :type="$field['type']"
:name="$field['name']" :name="$field['name']"

View File

@ -4,7 +4,6 @@ namespace Tests\Feature\Mailgateway;
use App\Mailgateway\Models\Mailgateway; use App\Mailgateway\Models\Mailgateway;
use App\Mailgateway\Types\LocalType; use App\Mailgateway\Types\LocalType;
use App\Mailgateway\Types\MailmanType;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire; use Livewire\Livewire;
use Modules\Mailgateway\Components\SettingView; use Modules\Mailgateway\Components\SettingView;
@ -21,7 +20,7 @@ it('test it can view index page', function () {
it('test it displays local gateways', function () { it('test it displays local gateways', function () {
test()->withoutExceptionHandling()->login()->loginNami(); test()->withoutExceptionHandling()->login()->loginNami();
Mailgateway::factory()->type(LocalType::class, [])->name('Lore')->domain('example.com')->create(); Mailgateway::factory()->type(LocalType::from([]))->name('Lore')->domain('example.com')->create();
Livewire::test(SettingView::class) Livewire::test(SettingView::class)
->assertSeeHtml('example.com') ->assertSeeHtml('example.com')
@ -32,8 +31,8 @@ it('test it displays local gateways', function () {
it('displays mailman gateways', function () { it('displays mailman gateways', function () {
test()->withoutExceptionHandling()->login()->loginNami(); test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->succeeds()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']); $typeParams = MailmanTypeRequest::new()->succeeds()->state(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']);
Mailgateway::factory()->type(MailmanType::class, $typeParams)->create(); Mailgateway::factory()->type($typeParams->toData())->create();
Livewire::test(SettingView::class)->assertSeeHtml('Verbindung erfolgreich'); Livewire::test(SettingView::class)->assertSeeHtml('Verbindung erfolgreich');
}); });

View File

@ -19,8 +19,9 @@ it('test it saves a mail gateway', function () {
Livewire::test(Form::class) Livewire::test(Form::class)
->set('name', 'lala') ->set('name', 'lala')
->set('domain', 'example.com') ->set('domain', 'example.com')
->set('cls', LocalType::class) ->set('type', new LocalType())
->call('onSave') ->call('onSave')
->assertHasNoErrors()
->assertDispatched('closeModal') ->assertDispatched('closeModal')
->assertDispatched('refresh-page') ->assertDispatched('refresh-page')
->assertDispatched('success'); ->assertDispatched('success');
@ -29,27 +30,19 @@ it('test it saves a mail gateway', function () {
'domain' => 'example.com', 'domain' => 'example.com',
'name' => 'lala', 'name' => 'lala',
'type' => json_encode([ 'type' => json_encode([
'cls' => LocalType::class, 'type' => LocalType::class,
'params' => [], 'data' => [],
]), ]),
]); ]);
}); });
it('validates type', function () {
test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class)
->set('cls', '')
->assertHasErrors(['cls' => 'required']);
});
it('test it validates mail gateway', function (array $attributes, array $errors) { it('test it validates mail gateway', function (array $attributes, array $errors) {
test()->withoutExceptionHandling()->login()->loginNami(); test()->withoutExceptionHandling()->login()->loginNami();
Livewire::test(Form::class) Livewire::test(Form::class)
->set('name', 'lala') ->set('name', 'lala')
->set('domain', 'example.com') ->set('domain', 'example.com')
->set('cls', LocalType::class) ->set('type', new LocalType())
->setArray($attributes) ->setArray($attributes)
->call('onSave') ->call('onSave')
->assertHasErrors($errors) ->assertHasErrors($errors)
@ -59,6 +52,7 @@ it('test it validates mail gateway', function (array $attributes, array $errors)
})->with([ })->with([
[['name' => ''], ['name' => 'required']], [['name' => ''], ['name' => 'required']],
[['domain' => ''], ['domain' => 'required']], [['domain' => ''], ['domain' => 'required']],
[['type' => ''], ['type' => 'required']],
]); ]);
it('test it validates mailman type', function (array $attributes, array $errors) { it('test it validates mailman type', function (array $attributes, array $errors) {

View File

@ -17,30 +17,32 @@ uses(TestCase::class);
it('test it sets attributes for mailman', function () { it('test it sets attributes for mailman', function () {
test()->withoutExceptionHandling()->login()->loginNami(); test()->withoutExceptionHandling()->login()->loginNami();
$typeParams = MailmanTypeRequest::new()->create(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']); $typeParams = MailmanTypeRequest::new()->state(['url' => 'https://mailman.example.com', 'user' => 'user', 'password' => 'password', 'owner' => 'owner']);
$mailgateway = Mailgateway::factory()->type(MailmanType::class, $typeParams)->create(['name' => '::name::', 'domain' => 'example.com']); $mailgateway = Mailgateway::factory()->type($typeParams->toData())->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['id' => $mailgateway->id]) Livewire::test(Form::class, ['id' => $mailgateway->id])
->assertSet('model', fn ($m) => $m->is($mailgateway)) ->assertSet('model', fn ($m) => $m->is($mailgateway))
->assertSet('name', '::name::') ->assertSet('name', '::name::')
->assertSet('domain', 'example.com') ->assertSet('domain', 'example.com')
->assertSet('cls', MailmanType::class) ->assertSet(
->assertSet('params.url', 'https://mailman.example.com') 'type',
->assertSet('params.user', 'user') fn ($type) => $type instanceof MailmanType
->assertSet('params.password', 'password') && $type->url === 'https://mailman.example.com'
->assertSet('params.owner', 'owner'); && $type->user === 'user' &&
$type->password === 'password'
&& $type->owner === 'owner'
);
}); });
it('test it sets attributes for local', function () { it('test it sets attributes for local', function () {
test()->withoutExceptionHandling()->login()->loginNami(); test()->withoutExceptionHandling()->login()->loginNami();
$mailgateway = Mailgateway::factory()->type(LocalType::class, [])->create(['name' => '::name::', 'domain' => 'example.com']); $mailgateway = Mailgateway::factory()->create(['name' => '::name::', 'domain' => 'example.com']);
Livewire::test(Form::class, ['id' => $mailgateway->id]) Livewire::test(Form::class, ['id' => $mailgateway->id])
->assertSet('name', '::name::') ->assertSet('name', '::name::')
->assertSet('domain', 'example.com') ->assertSet('domain', 'example.com')
->assertSet('cls', LocalType::class) ->assertSet('type', fn ($type) => $type instanceof LocalType);
->assertSet('params', []);
}); });
it('test it validates type', function () { it('test it validates type', function () {

View File

@ -78,6 +78,7 @@ use App\Membership\Actions\MembershipStoreAction;
use App\Membership\Actions\MembershipUpdateAction; use App\Membership\Actions\MembershipUpdateAction;
use App\Payment\SubscriptionController; use App\Payment\SubscriptionController;
Route::get('/lala', fn () => auth()->login(\App\User::first()));
Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void { Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void {
Auth::routes(['register' => false]); Auth::routes(['register' => false]);
}); });

View File

@ -2,6 +2,7 @@
namespace Tests\RequestFactories; namespace Tests\RequestFactories;
use App\Mailgateway\Types\MailmanType;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Worksome\RequestFactories\RequestFactory; use Worksome\RequestFactories\RequestFactory;
@ -49,4 +50,9 @@ class MailmanTypeRequest extends RequestFactory
: Http::response([], 401); : Http::response([], 401);
}); });
} }
public function toData(): MailmanType
{
return MailmanType::from($this->create());
}
} }