Something went wrong. Try again.
Laravel AT Protocol Client (alpha & unstable)
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233<?php
namespace SocialDept\AtpClient\Client\Requests\Atproto;
use BackedEnum;use Illuminate\Http\UploadedFile;use InvalidArgumentException;use SocialDept\AtpClient\Attributes\PublicEndpoint;use SocialDept\AtpClient\Attributes\ScopedEndpoint;use SocialDept\AtpClient\Auth\ScopeChecker;use SocialDept\AtpClient\Client\Requests\Request;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\CreateRecordResponse;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DeleteRecordResponse;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\DescribeRepoResponse;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\GetRecordResponse;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\ListRecordsResponse;use SocialDept\AtpClient\Data\Responses\Atproto\Repo\PutRecordResponse;use SocialDept\AtpClient\Enums\Nsid\AtprotoRepo;use SocialDept\AtpClient\Enums\Scope;use SocialDept\AtpSchema\Data\BlobReference;use SplFileInfo;use Throwable;
class RepoRequestClient extends Request{ /** * Create a record * * @requires transition:generic OR repo:[collection]?action=create * * @see https://docs.bsky.app/docs/api/com-atproto-repo-create-record */ #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Create records in repository')] public function createRecord( string|BackedEnum $collection, array $record, ?string $rkey = null, bool $validate = true, ?string $swapCommit = null ): CreateRecordResponse { $repo = $this->atp->client->session()->did(); $collection = $collection instanceof BackedEnum ? $collection->value : $collection; $this->checkCollectionScope($collection, 'create');
$response = $this->atp->client->post( endpoint: AtprotoRepo::CreateRecord, body: array_filter( compact('repo', 'collection', 'record', 'rkey', 'validate', 'swapCommit'), fn ($v) => ! is_null($v) ) );
return CreateRecordResponse::fromArray($response->json()); }
/** * Delete a record * * @requires transition:generic OR repo:[collection]?action=delete * * @see https://docs.bsky.app/docs/api/com-atproto-repo-delete-record */ #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Delete records from repository')] public function deleteRecord( string|BackedEnum $collection, string $rkey, ?string $swapRecord = null, ?string $swapCommit = null ): DeleteRecordResponse { $repo = $this->atp->client->session()->did(); $collection = $collection instanceof BackedEnum ? $collection->value : $collection; $this->checkCollectionScope($collection, 'delete');
$response = $this->atp->client->post( endpoint: AtprotoRepo::DeleteRecord, body: array_filter( compact('repo', 'collection', 'rkey', 'swapRecord', 'swapCommit'), fn ($v) => ! is_null($v) ) );
return DeleteRecordResponse::fromArray($response->json()); }
/** * Put (upsert) a record * * @requires transition:generic OR repo:[collection]?action=update * * @see https://docs.bsky.app/docs/api/com-atproto-repo-put-record */ #[ScopedEndpoint(Scope::TransitionGeneric, description: 'Update records in repository')] public function putRecord( string|BackedEnum $collection, string $rkey, array $record, bool $validate = true, ?string $swapRecord = null, ?string $swapCommit = null ): PutRecordResponse { $repo = $this->atp->client->session()->did(); $collection = $collection instanceof BackedEnum ? $collection->value : $collection; $this->checkCollectionScope($collection, 'update');
$response = $this->atp->client->post( endpoint: AtprotoRepo::PutRecord, body: array_filter( compact('repo', 'collection', 'rkey', 'record', 'validate', 'swapRecord', 'swapCommit'), fn ($v) => ! is_null($v) ) );
return PutRecordResponse::fromArray($response->json()); }
/** * Get a record * * @see https://docs.bsky.app/docs/api/com-atproto-repo-get-record */ #[PublicEndpoint] public function getRecord( string $repo, string|BackedEnum $collection, string $rkey, ?string $cid = null ): GetRecordResponse { $collection = $collection instanceof BackedEnum ? $collection->value : $collection; $response = $this->atp->client->get( endpoint: AtprotoRepo::GetRecord, params: compact('repo', 'collection', 'rkey', 'cid') );
return GetRecordResponse::fromArray($response->json()); }
/** * List records in a collection * * @see https://docs.bsky.app/docs/api/com-atproto-repo-list-records */ #[PublicEndpoint] public function listRecords( string $repo, string|BackedEnum $collection, int $limit = 50, ?string $cursor = null, bool $reverse = false ): ListRecordsResponse { $collection = $collection instanceof BackedEnum ? $collection->value : $collection; $response = $this->atp->client->get( endpoint: AtprotoRepo::ListRecords, params: compact('repo', 'collection', 'limit', 'cursor', 'reverse') );
return ListRecordsResponse::fromArray($response->json()); }
/** * Upload a new blob, to be referenced from a repository record * * The blob will be deleted if it is not referenced within a time window. * * @requires transition:generic (blob:*\/*\) * * @param UploadedFile|SplFileInfo|string $file The file to upload * @param string|null $mimeType MIME type (required for string input, auto-detected for file objects) * * @throws InvalidArgumentException|Throwable When $file is a string and $mimeType is not provided * * @see https://docs.bsky.app/docs/api/com-atproto-repo-upload-blob */ #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'blob:*/*')] public function uploadBlob(UploadedFile|SplFileInfo|string $file, ?string $mimeType = null): BlobReference { // Handle different input types if ($file instanceof UploadedFile) { $data = $file->getContent(); $mimeType ??= $file->getMimeType(); } elseif ($file instanceof SplFileInfo) { $data = file_get_contents($file->getRealPath()); $mimeType ??= mime_content_type($file->getRealPath()) ?: 'application/octet-stream'; } else { throw_if($mimeType === null, new InvalidArgumentException('The $mimeType parameter is required when $file is a string.')); $data = $file; }
$response = $this->atp->client->postBlob( endpoint: AtprotoRepo::UploadBlob, data: $data, mimeType: $mimeType );
return BlobReference::fromArray($response->json()['blob']); }
/** * Describe the repository * * @see https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo */ #[PublicEndpoint] public function describeRepo(string $repo): DescribeRepoResponse { $response = $this->atp->client->get( endpoint: AtprotoRepo::DescribeRepo, params: compact('repo') );
return DescribeRepoResponse::fromArray($response->json()); }
/** * Check if the session has repo access for a specific collection and action. * * This check is in addition to the transition:generic scope check. * Users need either transition:generic OR the specific repo scope. */ protected function checkCollectionScope(string $collection, string $action): void { $session = $this->atp->client->session(); $checker = app(ScopeChecker::class);
// If user has transition:generic, they have broad access if ($checker->hasScope($session, Scope::TransitionGeneric)) { return; }
// Otherwise, check for specific repo scope $checker->checkRepoScopeOrFail($session, $collection, $action); }}