Fix member resync
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
de7ee9cda2
commit
211f93ec17
|
@ -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)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace App\Initialize;
|
namespace App\Initialize;
|
||||||
|
|
||||||
use App\Initialize\Actions\ProcessRedisAction;
|
use App\Member\Actions\InsertFullMemberAction;
|
||||||
use App\Nami\Api\CompleteMemberToRedisJob;
|
use App\Member\Data\FullMember;
|
||||||
|
use App\Nami\Api\FullMemberAction;
|
||||||
use App\Setting\NamiSettings;
|
use App\Setting\NamiSettings;
|
||||||
use Illuminate\Support\Facades\Bus;
|
use Illuminate\Support\Facades\Bus;
|
||||||
use Illuminate\Support\Facades\Redis;
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
@ -23,13 +24,16 @@ class InitializeMembers
|
||||||
Redis::delete('members');
|
Redis::delete('members');
|
||||||
|
|
||||||
$jobs = $api->search([])->map(function (NamiMemberEntry $member) use ($api) {
|
$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();
|
})->toArray();
|
||||||
|
|
||||||
Bus::batch($jobs)
|
Bus::batch($jobs)
|
||||||
->finally(function () {
|
->finally(function () {
|
||||||
foreach (Redis::lrange('members', 0, -1) as $data) {
|
/** @var array<int, FullMember> */
|
||||||
ProcessRedisAction::dispatch(json_decode($data, true));
|
$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')
|
->onQueue('long')
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,13 @@
|
||||||
|
|
||||||
namespace App\Member\Actions;
|
namespace App\Member\Actions;
|
||||||
|
|
||||||
use App\Actions\PullMemberAction;
|
|
||||||
use App\Actions\PullMembershipsAction;
|
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
|
use App\Nami\Api\FullMemberAction;
|
||||||
use App\Setting\NamiSettings;
|
use App\Setting\NamiSettings;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
use Lorisleiva\Actions\ActionRequest;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Zoomyboy\LaravelNami\Exceptions\Skippable;
|
|
||||||
|
|
||||||
class MemberResyncAction
|
class MemberResyncAction
|
||||||
{
|
{
|
||||||
|
@ -24,13 +22,9 @@ class MemberResyncAction
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$fullMember = FullMemberAction::run($api, $member->group->nami_id, $member->nami_id);
|
||||||
$localMember = app(PullMemberAction::class)->handle($member->group->nami_id, $member->nami_id);
|
|
||||||
} catch (Skippable $e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
app(PullMembershipsAction::class)->handle($localMember);
|
InsertFullMemberAction::dispatch($fullMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function asController(ActionRequest $request, Member $member): RedirectResponse|Response
|
public function asController(ActionRequest $request, Member $member): RedirectResponse|Response
|
||||||
|
|
|
@ -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,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
35
phpstan.neon
35
phpstan.neon
|
@ -599,13 +599,38 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: tests/Feature/Member/NamiPutMemberActionTest.php
|
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\\(\\)\\.$#"
|
message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:once\\(\\)\\.$#"
|
||||||
count: 2
|
count: 2
|
||||||
path: tests/Feature/Member/ResyncTest.php
|
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
|
||||||
|
|
||||||
|
|
|
@ -2,42 +2,35 @@
|
||||||
|
|
||||||
namespace Tests\Feature\Member;
|
namespace Tests\Feature\Member;
|
||||||
|
|
||||||
use App\Actions\PullCoursesAction;
|
|
||||||
use App\Actions\PullMemberAction;
|
|
||||||
use App\Actions\PullMembershipsAction;
|
|
||||||
use App\Group;
|
use App\Group;
|
||||||
|
use App\Member\Actions\InsertFullMemberAction;
|
||||||
|
use App\Member\Data\FullMember;
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
|
use App\Nami\Api\FullMemberAction;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
use Zoomyboy\LaravelNami\Api;
|
||||||
|
use Zoomyboy\LaravelNami\Fakes\MemberFake;
|
||||||
|
|
||||||
class ResyncTest extends TestCase
|
class ResyncTest extends TestCase
|
||||||
{
|
{
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
private Api $api;
|
||||||
|
|
||||||
public function testItCanResyncAMember(): void
|
public function testItCanResyncAMember(): void
|
||||||
{
|
{
|
||||||
|
$this->api = $this->createStub(Api::class);
|
||||||
$this->login()->loginNami();
|
$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();
|
$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 = $this->from('/member')->get(route('member.resync', ['member' => $member]));
|
||||||
|
|
||||||
$response->assertRedirect('/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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue