Fix member resync
continuous-integration/drone/push Build is failing Details

This commit is contained in:
philipp lang 2023-02-23 00:38:17 +01:00
parent de7ee9cda2
commit 211f93ec17
10 changed files with 132 additions and 154 deletions

View File

@ -1,34 +0,0 @@
<?php
namespace App\Initialize\Actions;
use App\Actions\InsertCoursesAction;
use App\Actions\InsertMemberAction;
use App\Actions\InsertMembershipsAction;
use Lorisleiva\Actions\Concerns\AsAction;
use Zoomyboy\LaravelNami\Data\Course as NamiCourse;
use Zoomyboy\LaravelNami\Data\Member as NamiMember;
use Zoomyboy\LaravelNami\Data\MembershipEntry as NamiMembershipEntry;
class ProcessRedisAction
{
use AsAction;
public string $jobQueue = 'single';
/**
* @param array{member: array<string, mixed>, memberships: array<string, mixed>, courses: array<string, mixed>} $data
*/
public function handle(array $data): void
{
$localMember = InsertMemberAction::run(NamiMember::from($data['member']));
InsertMembershipsAction::run(
$localMember,
collect($data['memberships'])->map(fn ($membership) => NamiMembershipEntry::from($membership)),
);
InsertCoursesAction::run(
$localMember,
collect($data['courses'])->map(fn ($course) => NamiCourse::from($course)),
);
}
}

View File

@ -2,8 +2,9 @@
namespace App\Initialize;
use App\Initialize\Actions\ProcessRedisAction;
use App\Nami\Api\CompleteMemberToRedisJob;
use App\Member\Actions\InsertFullMemberAction;
use App\Member\Data\FullMember;
use App\Nami\Api\FullMemberAction;
use App\Setting\NamiSettings;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Redis;
@ -23,13 +24,16 @@ class InitializeMembers
Redis::delete('members');
$jobs = $api->search([])->map(function (NamiMemberEntry $member) use ($api) {
return new CompleteMemberToRedisJob($api, $member->groupId, $member->id);
return FullMemberAction::makeJob($api, $member->groupId, $member->id, 'members');
})->toArray();
Bus::batch($jobs)
->finally(function () {
foreach (Redis::lrange('members', 0, -1) as $data) {
ProcessRedisAction::dispatch(json_decode($data, true));
/** @var array<int, FullMember> */
$members = array_map(fn ($member) => FullMember::from(json_decode($member, true)), Redis::lrange('members', 0, -1));
foreach ($members as $data) {
InsertFullMemberAction::dispatch($data);
}
})
->onQueue('long')

View File

@ -0,0 +1,23 @@
<?php
namespace App\Member\Actions;
use App\Actions\InsertCoursesAction;
use App\Actions\InsertMemberAction;
use App\Actions\InsertMembershipsAction;
use App\Member\Data\FullMember;
use Lorisleiva\Actions\Concerns\AsAction;
class InsertFullMemberAction
{
use AsAction;
public string $jobQueue = 'single';
public function handle(FullMember $member): void
{
$localMember = InsertMemberAction::run($member->member);
InsertMembershipsAction::run($localMember, $member->memberships->toCollection());
InsertCoursesAction::run($localMember, $member->courses->toCollection());
}
}

View File

@ -2,15 +2,13 @@
namespace App\Member\Actions;
use App\Actions\PullMemberAction;
use App\Actions\PullMembershipsAction;
use App\Member\Member;
use App\Nami\Api\FullMemberAction;
use App\Setting\NamiSettings;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;
use Zoomyboy\LaravelNami\Exceptions\Skippable;
class MemberResyncAction
{
@ -24,13 +22,9 @@ class MemberResyncAction
return;
}
try {
$localMember = app(PullMemberAction::class)->handle($member->group->nami_id, $member->nami_id);
} catch (Skippable $e) {
return;
}
$fullMember = FullMemberAction::run($api, $member->group->nami_id, $member->nami_id);
app(PullMembershipsAction::class)->handle($localMember);
InsertFullMemberAction::dispatch($fullMember);
}
public function asController(ActionRequest $request, Member $member): RedirectResponse|Response

View File

@ -0,0 +1,25 @@
<?php
namespace App\Member\Data;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
use Zoomyboy\LaravelNami\Data\Course as NamiCourse;
use Zoomyboy\LaravelNami\Data\Member as NamiMember;
use Zoomyboy\LaravelNami\Data\MembershipEntry as NamiMembershipEntry;
use Spatie\LaravelData\Attributes\DataCollectionOf;
class FullMember extends Data {
/**
* @param DataCollection<int, NamiCourse> $courses
* @param DataCollection<int, NamiMembershipEntry> $memberships
*/
public function __construct(
public NamiMember $member,
#[DataCollectionOf(NamiCourse::class)]
public DataCollection $courses,
#[DataCollectionOf(NamiMembershipEntry::class)]
public DataCollection $memberships,
) {}
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Nami\Api;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Redis;
use Zoomyboy\LaravelNami\Api;
class CompleteMemberToRedisJob implements ShouldQueue
{
use Batchable;
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public function __construct(
private Api $api,
private int $groupId,
private int $memberId,
) {
}
public function handle(): void
{
if ($this->batch()->cancelled()) {
return;
}
Redis::rpush('members', collect([
'member' => MemberAction::run($this->api, $this->groupId, $this->memberId),
'memberships' => MembershipsOfAction::run($this->api, $this->memberId),
'courses' => CoursesOfAction::run($this->api, $this->memberId),
])->toJson());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Nami\Api;
use App\Member\Data\FullMember;
use Illuminate\Support\Facades\Redis;
use Lorisleiva\Actions\Concerns\AsAction;
use Zoomyboy\LaravelNami\Api;
class FullMemberAction
{
use AsAction;
public function handle(Api $api, int $groupId, int $memberId, ?string $redisKey = null): FullMember
{
$fullMember = FullMember::from([
'member' => MemberAction::run($api, $groupId, $memberId),
'memberships' => MembershipsOfAction::run($api, $memberId),
'courses' => CoursesOfAction::run($api, $memberId),
]);
if (!$redisKey) {
return $fullMember;
}
Redis::rpush($redisKey, $fullMember->toJson());
return $fullMember;
}
}

View File

@ -1,41 +0,0 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$callback of method Illuminate\\\\Support\\\\LazyCollection\\<int,Zoomyboy\\\\LaravelNami\\\\Data\\\\MemberEntry\\>\\:\\:each\\(\\) expects callable\\(int, int\\)\\: mixed, Closure\\(Zoomyboy\\\\LaravelNami\\\\Data\\\\MemberEntry\\)\\: void given\\.$#"
count: 1
path: app/Initialize/InitializeMembers.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:never\\(\\)\\.$#"
count: 1
path: tests/Feature/Initialize/InitializeActionTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:with\\(\\)\\.$#"
count: 1
path: tests/Feature/Initialize/InitializeActionTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:once\\(\\)\\.$#"
count: 10
path: tests/Feature/Initialize/InitializeMembersTest.php
-
message: "#^Parameter \\#1 \\$mock of static method Phake\\:\\:verify\\(\\) expects Phake\\\\IMock, App\\\\Actions\\\\PullMemberAction given\\.$#"
count: 1
path: tests/Feature/Member/NamiPutMemberActionTest.php
-
message: "#^Parameter \\#1 \\$mock of static method Phake\\:\\:verify\\(\\) expects Phake\\\\IMock, App\\\\Actions\\\\PullMembershipsAction given\\.$#"
count: 1
path: tests/Feature/Member/NamiPutMemberActionTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:never\\(\\)\\.$#"
count: 4
path: tests/Feature/Member/ResyncTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:once\\(\\)\\.$#"
count: 2
path: tests/Feature/Member/ResyncTest.php

View File

@ -599,13 +599,38 @@ parameters:
count: 1
path: tests/Feature/Member/NamiPutMemberActionTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:never\\(\\)\\.$#"
count: 4
path: tests/Feature/Member/ResyncTest.php
-
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:once\\(\\)\\.$#"
count: 2
path: tests/Feature/Member/ResyncTest.php
-
message: "#^Method App\\\\Region\\:\\:forSelect\\(\\) should return Illuminate\\\\Support\\\\Collection\\<int, array\\{id\\: int, name\\: string\\}\\> but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\<int, App\\\\Region\\>\\.$#"
count: 1
path: app/Region.php
-
message: "#^Access to an undefined property Sabre\\\\VObject\\\\Component\\\\VCard\\:\\:\\$BDAY\\.$#"
count: 1
path: tests/Feature/Member/DavTest.php
-
message: "#^Access to an undefined property Sabre\\\\VObject\\\\Component\\\\VCard\\:\\:\\$FN\\.$#"
count: 1
path: tests/Feature/Member/DavTest.php
-
message: "#^Access to an undefined property Sabre\\\\VObject\\\\Component\\\\VCard\\:\\:\\$N\\.$#"
count: 1
path: tests/Feature/Member/DavTest.php
-
message: "#^Access to an undefined property Sabre\\\\VObject\\\\Component\\\\VCard\\:\\:\\$TEL\\.$#"
count: 1
path: tests/Feature/Member/DavTest.php
-
message: "#^Access to an undefined property Sabre\\\\VObject\\\\Component\\\\VCard\\:\\:\\$UID\\.$#"
count: 1
path: tests/Feature/Member/DavTest.php

View File

@ -2,42 +2,35 @@
namespace Tests\Feature\Member;
use App\Actions\PullCoursesAction;
use App\Actions\PullMemberAction;
use App\Actions\PullMembershipsAction;
use App\Group;
use App\Member\Actions\InsertFullMemberAction;
use App\Member\Data\FullMember;
use App\Member\Member;
use App\Nami\Api\FullMemberAction;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
use Zoomyboy\LaravelNami\Api;
use Zoomyboy\LaravelNami\Fakes\MemberFake;
class ResyncTest extends TestCase
{
use DatabaseTransactions;
private Api $api;
public function testItCanResyncAMember(): void
{
$this->api = $this->createStub(Api::class);
$this->login()->loginNami();
app(MemberFake::class)->shows(32, 33);
$fullMember = FullMember::from(['courses' => [], 'memberships' => [], 'member' => $this->api->member(32, 33)]);
FullMemberAction::shouldRun()->once()->andReturn($fullMember);
InsertFullMemberAction::shouldRun()->once();
$member = Member::factory()->defaults()->for(Group::factory()->inNami(32))->inNami(33)->create();
PullMemberAction::shouldRun()->once()->with(32, 33)->andReturn($member);
PullMembershipsAction::shouldRun()->once()->with($member);
PullCoursesAction::shouldRun()->never();
$response = $this->from('/member')->get(route('member.resync', ['member' => $member]));
$response->assertRedirect('/member');
}
public function testItReturnsErrorWhenMemberIsNotInNami(): void
{
$this->login()->loginNami();
$member = Member::factory()->defaults()->create();
PullMemberAction::shouldRun()->never();
PullMembershipsAction::shouldRun()->never();
PullCoursesAction::shouldRun()->never();
$response = $this->from('/member')->get(route('member.resync', ['member' => $member]));
$response->assertRedirect('/member');
}
}