Skip to content

Commit

Permalink
Update claims beam pack
Browse files Browse the repository at this point in the history
  • Loading branch information
enjinabner committed Aug 5, 2024
1 parent 952be3d commit 069a53e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 33 deletions.
43 changes: 32 additions & 11 deletions src/Jobs/ClaimBeam.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

use Enjin\Platform\Beam\Enums\BeamType;
use Enjin\Platform\Beam\Enums\PlatformBeamCache;
use Enjin\Platform\Beam\Exceptions\BeamException;
use Enjin\Platform\Beam\Models\BeamClaim;
use Enjin\Platform\Beam\Models\BeamPack;
use Enjin\Platform\Beam\Models\BeamScan;
use Enjin\Platform\Beam\Services\BatchService;
use Enjin\Platform\Beam\Services\BeamService;
Expand All @@ -13,6 +15,7 @@
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
Expand Down Expand Up @@ -45,17 +48,23 @@ public function handle(BatchService $batch, WalletService $wallet): void

try {
$lock->block(5);
if ($claim = $this->claimQuery($data)->first()) {
DB::beginTransaction();
$claims = $this->claims($data);
if (count($claims)) {
$wallet->firstOrStore(['public_key' => $data['wallet_public_key']]);
$claim->forceFill($this->buildBeamClaimAttributes($batch, $claim))->save();
$isPackUpdated = false;
DB::beginTransaction();
foreach ($claims as $claim) {
$claim->forceFill($this->buildBeamClaimAttributes($batch, $claim))->save();
Log::info('ClaimBeamJob: Claim assigned.', $claim->toArray());

if ($claim->beamPack && Arr::get($this->data, 'is_pack') && ! $isPackUpdated) {
$claim->beamPack->update(['is_claimed' => true]);
$isPackUpdated = true;
}
}
// Delete scan after claim is set up so the signed data can't be used to claim again.
BeamScan::firstWhere(['wallet_public_key' => $data['wallet_public_key'], 'beam_id' => $data['beam']['id']])?->delete();

DB::commit();

Log::info('ClaimBeamJob: Claim assigned.', $claim->toArray());
} else {
Cache::put(BeamService::key(Arr::get($data, 'beam.code')), 0);
Log::info('ClaimBeamJob: No claim available, setting remaining count to 0', $data);
Expand All @@ -66,7 +75,7 @@ public function handle(BatchService $batch, WalletService $wallet): void
} catch (Throwable $e) {
DB::rollBack();

Log::error('ClaimBeamJob: Claim error, message:' . $e->getMessage(), $data);
Log::error('ClaimBeamJob: Claim error, message: ' . $e->getMessage(), $data);

throw $e;
} finally {
Expand All @@ -81,7 +90,7 @@ public function handle(BatchService $batch, WalletService $wallet): void
public function failed(Throwable $exception): void
{
if ($data = $this->data) {
if ($this->claimQuery($data)->count() > 0) {
if (count($this->claims($data)) > 0) {
// Idempotency key prevents incrementing cache on same claim request even with manual retry on horizon
$key = Arr::get($data, 'idempotency_key');
if (! Cache::get(PlatformBeamCache::IDEMPOTENCY_KEY->key($key))) {
Expand All @@ -99,12 +108,24 @@ public function failed(Throwable $exception): void
/**
* Get the claim query.
*/
protected function claimQuery(array $data): Builder
protected function claims(array $data): Collection
{
return BeamClaim::where('beam_id', $data['beam']['id'])
->with(['beam:id,collection_chain_id', 'beamPack:id,beam_id'])
->claimable()
->when($data['code'], fn ($query) => $query->withSingleUseCode($data['code']))
->unless($data['code'], fn ($query) => $query->inRandomOrder());
->when($isPack = Arr::get($data, 'is_pack'), function (Builder $query) use ($data) {
if (!($pack = BeamPack::where('is_claimed', false)
->where('beam_id', $data['beam']['id'])
->when($data['code'], fn ($subquery) => $subquery->where('code', $data['code']))
->inRandomOrder()
->first())) {
throw new BeamException('No available packs to claim.');
}
$query->where('beam_pack_id', $pack->id);
})
->when(!$isPack && $data['code'], fn ($query) => $query->withSingleUseCode($data['code']))
->when(!$isPack, fn ($query) => $query->inRandomOrder())
->get(['id', 'beam_id', 'type', 'beam_pack_id']);
}

/**
Expand Down
44 changes: 23 additions & 21 deletions src/Services/BeamService.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,16 @@ public function findByCode(string $code): ?Model
*/
public function scanByCode(string $code, ?string $wallet = null): ?Model
{
$isSingleUse = static::isSingleUse($code);
$beamCode = static::getSingleUseCodeData($code)?->beamCode;
$beam = Beam::whereCode($beamCode ?? $code)->firstOrFail();

if ($beamCode) {
($beam->is_pack ? new BeamPack() : new BeamClaim())
->withSingleUseCode($code)
->firstOrFail();
}


$beam = $isSingleUse
? BeamClaim::withSingleUseCode($code)
->with('beam')
->first()
->beam
: $this->findByCode($code);
if ($wallet) {
// Pushing this to the queue for performance
CreateClaim::dispatch($claim = [
Expand All @@ -254,7 +256,7 @@ public function scanByCode(string $code, ?string $wallet = null): ?Model
$beam->setRelation('scans', collect(json_decode(json_encode([$claim]))));
}

if ($isSingleUse) {
if ($beamCode) {
$beam['code'] = $code;
}

Expand All @@ -267,23 +269,22 @@ public function scanByCode(string $code, ?string $wallet = null): ?Model
public function claim(string $code, string $wallet, ?string $idempotencyKey = null): bool
{
$singleUseCode = null;
$singleUse = static::isSingleUse($code);

if ($singleUse) {
$singleUseCode = $code;
$beam = BeamClaim::withSingleUseCode($singleUseCode)
->with('beam')
->first()
->beam;
$code = $beam?->code;
} else {
$beam = $this->findByCode($code);
}

$singleUse = static::getSingleUseCodeData($code);
$beam = $this->findByCode($singleUse ? $singleUse->beamCode : $code);
if (! $beam) {
throw new BeamException(__('enjin-platform-beam::error.beam_not_found', ['code' => $code]));
}

if ($singleUse) {
if (!($beam->is_pack ? new BeamPack() : new BeamClaim())
->withSingleUseCode($code)
->first()) {
throw new BeamException(__('enjin-platform-beam::error.beam_not_found', ['code' => $code]));
}
$singleUseCode = $singleUse->claimCode;
$code = $singleUse->beamCode;
}

$lock = Cache::lock(self::key($code, 'claim-lock'), 5);

try {
Expand Down Expand Up @@ -614,6 +615,7 @@ protected function buildRequiredClaimBeamData(
'state' => ClaimStatus::PENDING->name,
'beam' => $beam->toArray(),
'beam_id' => $beam->id,
'is_pack' => $beam->is_pack,
'ip_address' => request()->getClientIp(),
'code' => $singleUseCode,
'idempotency_key' => $idempotencyKey ?: Str::uuid()->toString(),
Expand Down
29 changes: 28 additions & 1 deletion tests/Feature/GraphQL/Mutations/ClaimBeamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Enjin\Platform\Support\BitMask;
use Enjin\Platform\Support\SS58Address;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue;

Expand Down Expand Up @@ -98,6 +99,20 @@ public function test_it_can_claim_beam_with_sr25519_single_use_codes(): void
$this->genericClaimTest(CryptoSignatureType::SR25519, Arr::get($response, 'edges.0.node.code'));
}

public function test_it_can_claim_beam_pack_with_sr25519_single_use_codes(): void
{
$code = $this->graphql('CreateBeam', $this->generateBeamPackData(
BeamType::MINT_ON_DEMAND,
1,
[],
[['flag' => 'SINGLE_USE']],
));
$response = $this->graphql('GetSingleUseCodes', ['code' => $code]);
$this->assertNotEmpty($response['totalCount']);

$this->genericClaimTest(CryptoSignatureType::SR25519, Arr::get($response, 'edges.0.node.code'));
}

/**
* Test claiming beam with ed25519.
*/
Expand All @@ -106,6 +121,18 @@ public function test_it_can_claim_beam_with_ed25519(): void
$this->genericClaimTest(CryptoSignatureType::ED25519);
}

public function test_it_can_claim_beam_pack_with_ed25519(): void
{
$this->seedBeamPack();
$this->genericClaimTest(CryptoSignatureType::ED25519);
}

public function test_it_can_claim_beam_pack_with_sr25519(): void
{
$this->seedBeamPack();
$this->genericClaimTest(CryptoSignatureType::SR25519);
}

public function test_it_can_claim_beam_job_with_idempotency_key(): void
{
$data = [
Expand Down Expand Up @@ -488,7 +515,7 @@ protected function genericClaimTest(CryptoSignatureType $type = CryptoSignatureT
]);
$this->assertNotEmpty($response['message']);
if (! $singleUseCode) {
$this->assertEquals(1, $this->beam->scans()->count());
$this->assertEquals(1, DB::table('beam_scans')->where('beam_id', $this->beam->id)->count());
}

$message = $response['message']['message'];
Expand Down

0 comments on commit 069a53e

Please sign in to comment.