Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PLA-1872] Beam pack feature #80

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions database/factories/BeamPackFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Enjin\Platform\Beam\Database\Factories;

use Enjin\Platform\Beam\Models\BeamPack;
use Illuminate\Database\Eloquent\Factories\Factory;

class BeamPackFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var BeamPack
*/
protected $model = BeamPack::class;

/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'is_claimed' => fake()->boolean(),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('beams', function (Blueprint $table) {
$table->boolean('is_pack')->default(false);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('beams', function (Blueprint $table) {
$table->dropColumn('is_pack');
});
}
};
28 changes: 28 additions & 0 deletions database/migrations/2024_07_01_020155_create_beam_packs_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('beam_packs', function (Blueprint $table) {
$table->id();
$table->foreignId('beam_id')->constrained()->cascadeOnDelete();
$table->boolean('is_claimed')->default(false);
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('beam_packs');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('beam_claims', function (Blueprint $table) {
$table->foreignId('beam_pack_id')->index()->nullable()->constrained()->cascadeOnDelete();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('beam_claims', function (Blueprint $table) {
$table->dropColumn('beam_pack_id');
});
}
};
1 change: 1 addition & 0 deletions lang/en/input_type.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
'beam_flag.field.flag' => 'The beam flag.',
'beam_flag.field.enabled' => 'The flag enabled status.',
'claim_token.description' => 'The claimable tokens.',
'claim_token_pack.description' => 'The claimable tokens in a beam pack.',
'claim_token.field.tokenId' => 'The token chain IDs available to claim.',
'claim_token.field.tokenIdDataUpload' => 'You can use this to upload a txt file that contains a list of token ID ranges, one per line.',
'claim_token.field.claimQuantity' => 'The total amount of times each token ID can be claimed. This is mainly relevant for fungible tokens, where you can specify that there are a certain amount of claims for a token ID, e.g. 10 individual claims to receive 1 token with ID 123 per claim.',
Expand Down
2 changes: 2 additions & 0 deletions lang/en/mutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@
'remove_tokens.description' => 'Removes tokens from a beam.',
'remove_tokens.args.tokenIds' => 'The token IDs to remove.',
'add_tokens.description' => 'Add tokens to a beam.',
'create_beam_pack.description' => 'Create a beam pack.',
'create_beam_pack.args.quantity' => 'The quantity of beam packs to create.',
];
1 change: 1 addition & 0 deletions lang/en/type.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@
'beam_scan.field.walletPublicKey' => 'The wallet public key.',
'integer_range.description' => "A string value that can be used to represent a range of integer numbers. Use a double full stop to supply a range between 2 integers. \n\nExample \[\"1\",\"2\",\"3..8\"\]",
'attribute.description' => 'An initial attribute to set for the token when minting on demand.',
'create_beam_pack.description' => 'The beam bunldle tokens.',
];
1 change: 1 addition & 0 deletions src/BeamServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function configurePackage(Package $package): void
->hasMigration('update_beams_table')
->hasMigration('add_collection_chain_id_to_beam_batches_table')
->hasMigration('add_idempotency_key_to_beam_claims_table')
->hasMigration('add_is_pack_column_to_beams_table')
->hasRoute('enjin-platform-beam')
->hasTranslations();
}
Expand Down
165 changes: 165 additions & 0 deletions src/GraphQL/Mutations/CreateBeamPackMutation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php

namespace Enjin\Platform\Beam\GraphQL\Mutations;

use Closure;
use Enjin\Platform\Beam\Enums\BeamType;
use Enjin\Platform\Beam\GraphQL\Traits\HasBeamCommonFields;
use Enjin\Platform\Beam\Rules\MaxTokenSupply;
use Enjin\Platform\Beam\Rules\TokensDoNotExistInBeam;
use Enjin\Platform\Beam\Rules\TokensDoNotExistInCollection;
use Enjin\Platform\Beam\Rules\TokensExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadExistInCollection;
use Enjin\Platform\Beam\Rules\TokenUploadNotExistInBeam;
use Enjin\Platform\Beam\Rules\TokenUploadNotExistInCollection;
use Enjin\Platform\Beam\Rules\UniqueTokenIds;
use Enjin\Platform\Beam\Services\BeamService;
use Enjin\Platform\Models\Collection;
use Enjin\Platform\Rules\DistinctAttributes;
use Enjin\Platform\Rules\IsCollectionOwnerOrApproved;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Rebing\GraphQL\Support\Facades\GraphQL;

class CreateBeamPackMutation extends Mutation
{
use HasBeamCommonFields;

/**
* Get the mutation's attributes.
*/
public function attributes(): array
{
return [
'name' => 'CreateBeamPack',
'description' => __('enjin-platform-beam::mutation.create_beam_pack.description'),
];
}

/**
* Get the mutation's return type.
*/
public function type(): Type
{
return GraphQL::type('String!');
}

/**
* Get the mutation's arguments definition.
*/
public function args(): array
{
return [
...$this->getCommonFields(),
'flags' => [
'type' => GraphQL::type('[BeamFlagInputType!]'),
'description' => __('enjin-platform-beam::mutation.update_beam.args.flags'),
],
'collectionId' => [
'alias' => 'collection_chain_id',
'type' => GraphQL::type('BigInt!'),
'description' => __('enjin-platform-beam::mutation.create_beam.args.collectionId'),
],
'tokens' => [
'type' => GraphQL::type('[ClaimTokenPack!]!'),
'description' => __('enjin-platform-beam::input_type.claim_token_pack.description'),
],
'quantity' => [
'type' => GraphQL::type('Int'),
'description' => __('enjin-platform-beam::mutation.create_beam_pack.args.quantity'),
'defaultValue' => 1,
],
];
}

/**
* Resolve the mutation's request.
*/
public function resolve(
$root,
array $args,
$context,
ResolveInfo $resolveInfo,
Closure $getSelectFields,
BeamService $beam
) {
return DB::transaction(fn () => $beam->createPack($args)->code, 3);
}

/**
* Get the mutation's request validation rules.
*/
protected function rules(array $args = []): array
{
return [
'name' => ['filled', 'max:255'],
'description' => ['filled', 'max:1024'],
'image' => ['filled', 'url', 'max:1024'],
'start' => ['filled', 'date', 'before:end'],
'end' => ['filled', 'date', 'after:start'],
'quantity' => ['integer', 'min:1', 'max:1000'],
'collectionId' => [
'bail',
'filled',
function (string $attribute, mixed $value, Closure $fail) {
if (! Collection::where('collection_chain_id', $value)->exists()) {
$fail('validation.exists')->translate();
}
},
new IsCollectionOwnerOrApproved(),
],
'tokens' => ['bail', 'array', 'min:1', 'max:1000', new UniqueTokenIds()],
'tokens.*.attributes' => Rule::forEach(function ($value, $attribute) use ($args) {
if (empty($value)) {
return [];
}

return [
'nullable',
'bail',
'array',
'min:1',
'max:10',
new DistinctAttributes(),
Rule::prohibitedIf(BeamType::getEnumCase(Arr::get($args, str_replace('attributes', 'type', $attribute))) == BeamType::TRANSFER_TOKEN),
];
}),
'tokens.*.attributes.*.key' => 'max:255',
'tokens.*.attributes.*.value' => 'max:1000',
'tokens.*.tokenIds' => Rule::forEach(function ($value, $attribute) use ($args) {
return [
'bail',
'required_without:tokens.*.tokenIdDataUpload',
'prohibits:tokens.*.tokenIdDataUpload',
'distinct',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIds', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokensExistInCollection($args['collectionId'])
: new TokensDoNotExistInCollection($args['collectionId']),
new TokensDoNotExistInBeam(),
];
}),
'tokens.*.tokenIdDataUpload' => Rule::forEach(function ($value, $attribute) use ($args) {
return [
'bail',
'required_without:tokens.*.tokenIds',
'prohibits:tokens.*.tokenIds',
BeamType::getEnumCase(Arr::get($args, str_replace('tokenIdDataUpload', 'type', $attribute))) == BeamType::TRANSFER_TOKEN
? new TokenUploadExistInCollection($args['collectionId'])
: new TokenUploadNotExistInCollection($args['collectionId']),
new TokenUploadNotExistInBeam(),
];
}),
'tokens.*.tokenQuantityPerClaim' => [
'bail',
'filled',
'integer',
'min:1',
new MaxTokenSupply($args['collectionId'], $args['quantity']),
],
'flags.*.flag' => ['required', 'distinct'],
];
}
}
59 changes: 59 additions & 0 deletions src/GraphQL/Types/Input/ClaimTokenPackInputType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Enjin\Platform\Beam\GraphQL\Types\Input;

use Enjin\Platform\Beam\Enums\BeamType;
use Enjin\Platform\Beam\Rules\MaxBigIntIntegerRange;
use Enjin\Platform\Beam\Rules\MinBigIntIntegerRange;
use Enjin\Platform\Support\Hex;
use Rebing\GraphQL\Support\Facades\GraphQL;

class ClaimTokenPackInputType extends InputType
{
/**
* Get the input type's attributes.
*/
public function attributes(): array
{
return [
'name' => 'ClaimTokenPack',
'description' => __('enjin-platform-beam::input_type.claim_token_pack.description'),
];
}

/**
* Get the input type's fields.
*/
public function fields(): array
{
return [
'tokenIds' => [
'type' => GraphQL::type('[IntegerRangeString!]'),
'description' => __('enjin-platform-beam::input_type.claim_token.field.tokenId'),
'rules' => [new MinBigIntIntegerRange(), new MaxBigIntIntegerRange(Hex::MAX_UINT128)],
],
'tokenIdDataUpload' => [
'type' => GraphQL::type('Upload'),
'description' => __('enjin-platform-beam::input_type.claim_token.field.tokenIdDataUpload'),
'rules' => ['file', 'mimes:json,txt'],
],
'attributes' => [
'type' => GraphQL::type('[AttributeInput]'),
'description' => __('enjin-platform::input_type.create_token_params.field.attributes'),
'defaultValue' => [],
],
'tokenQuantityPerClaim' => [
'alias' => 'quantity',
'type' => GraphQL::type('Int'),
'description' => __('enjin-platform-beam::input_type.claim_token.field.tokenQuantityPerClaim'),
'rules' => ['integer'],
'defaultValue' => 1,
],
'type' => [
'type' => GraphQL::type('BeamType'),
'description' => __('enjin-platform-beam::mutation.common.args.type'),
'defaultValue' => BeamType::TRANSFER_TOKEN->name,
],
];
}
}
Loading
Loading