Add repositories and exceptions

This commit is contained in:
Eric Wang
2022-08-01 16:22:05 +00:00
parent 43b679c120
commit 5c46df42d9
9 changed files with 515 additions and 3 deletions

View File

@@ -0,0 +1,169 @@
<?php
namespace App\Contracts\Repository;
use Illuminate\Support\Collection;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
interface RepositoryInterface
{
/**
* Return an identifier or Model object to be used by the repository.
*
* @return string|\Closure|object
*/
public function model();
/**
* Return the model being used for this repository instance.
*
* @return mixed
*/
public function getModel();
/**
* Returns an instance of a query builder.
*
* @return mixed
*/
public function getBuilder();
/**
* Returns the columns to be selected or returned by the query.
*
* @return mixed
*/
public function getColumns();
/**
* An array of columns to filter the response by.
*
* @param array|string $columns
*
* @return $this
*/
public function setColumns($columns = ['*']);
/**
* Stop repository update functions from returning a fresh
* model when changes are committed.
*
* @return $this
*/
public function withoutFreshModel();
/**
* Return a fresh model with a repository updates a model.
*
* @return $this
*/
public function withFreshModel();
/**
* Set whether or not the repository should return a fresh model
* when changes are committed.
*
* @return $this
*/
public function setFreshModel(bool $fresh = true);
/**
* Create a new model instance and persist it to the database.
*
* @return mixed
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function create(array $fields, bool $validate = true, bool $force = false);
/**
* Find a model that has the specific ID passed.
*
* @return mixed
*
* @throws \App\Exceptions\Repository\RecordNotFoundException
*/
public function find(int $id);
/**
* Find a model matching an array of where clauses.
*/
public function findWhere(array $fields): Collection;
/**
* Find and return the first matching instance for the given fields.
*
* @return mixed
*
* @throws \App\Exceptions\Repository\RecordNotFoundException
*/
public function findFirstWhere(array $fields);
/**
* Return a count of records matching the passed arguments.
*/
public function findCountWhere(array $fields): int;
/**
* Delete a given record from the database.
*/
public function delete(int $id): int;
/**
* Delete records matching the given attributes.
*/
public function deleteWhere(array $attributes): int;
/**
* Update a given ID with the passed array of fields.
*
* @param int $id
*
* @return mixed
*
* @throws \App\Exceptions\Model\DataValidationException
* @throws \App\Exceptions\Repository\RecordNotFoundException
*/
public function update($id, array $fields, bool $validate = true, bool $force = false);
/**
* Perform a mass update where matching records are updated using whereIn.
* This does not perform any model data validation.
*/
public function updateWhereIn(string $column, array $values, array $fields): int;
/**
* Update a record if it exists in the database, otherwise create it.
*
* @return mixed
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false);
/**
* Return all records associated with the given model.
*/
public function all(): Collection;
/**
* Return a paginated result set using a search term if set on the repository.
*/
public function paginated(int $perPage): LengthAwarePaginator;
/**
* Insert a single or multiple records into the database at once skipping
* validation and mass assignment checking.
*/
public function insert(array $data): bool;
/**
* Insert multiple records into the database and ignore duplicates.
*/
public function insertIgnore(array $values): bool;
/**
* Get the amount of entries in the database.
*/
public function count(): int;
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
use Exception;
class ConvoyException extends Exception
{
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Exceptions\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Validation\Validator;
use App\Exceptions\ConvoyException;
use Illuminate\Contracts\Support\MessageProvider;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class DataValidationException extends ConvoyException implements HttpExceptionInterface, MessageProvider
{
/**
* The validator instance.
*/
protected Validator $validator;
/**
* The underlying model instance that triggered this exception.
*/
protected Model $model;
/**
* DataValidationException constructor.
*/
public function __construct(Validator $validator, Model $model)
{
$message = sprintf(
'Could not save %s[%s]: failed to validate data: %s',
get_class($model),
$model->getKey(),
$validator->errors()->toJson()
);
parent::__construct($message);
$this->validator = $validator;
$this->model = $model;
}
/**
* Return the validator message bag.
*
* @return \Illuminate\Support\MessageBag
*/
public function getMessageBag()
{
return $this->validator->errors();
}
/**
* Return the status code for this request.
*
* @return int
*/
public function getStatusCode(): int
{
return 500;
}
/**
* @return array
*/
public function getHeaders(): array
{
return [];
}
public function getValidator(): Validator
{
return $this->validator;
}
public function getModel(): Model
{
return $this->model;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Exceptions\Repository;
use Illuminate\Http\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class RecordNotFoundException extends RepositoryException implements HttpExceptionInterface
{
/**
* Returns the status code.
*
* @return int
*/
public function getStatusCode(): int
{
return Response::HTTP_NOT_FOUND;
}
/**
* Returns response headers.
*
* @return array
*/
public function getHeaders(): array
{
return [];
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Exceptions\Repository;
use App\Exceptions\ConvoyException;
class RepositoryException extends ConvoyException
{
}

View File

@@ -14,17 +14,18 @@ use Spatie\QueryBuilder\QueryBuilder;
class AddressController extends ApplicationApiController
{
public function index(Request $request)
public function index(Node $node, Request $request)
{
$addresses = QueryBuilder::for(IPAddress::query())
->allowedFilters(['server_id', 'node_id', 'address', 'cidr', 'gateway', 'type'])
->allowedSorts(['id', 'server_id', 'node_id'])
->where('node_id', $node->id)
->paginate($request->query('per_page') ?? 50);
return $addresses;
}
public function show(IPAddress $address)
public function show(Node $node, IPAddress $address)
{
return $this->returnContent([
'data' => $address,

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Repositories\Proxmox;
use GuzzleHttp\Client;
use App\Models\Node;
use Webmozart\Assert\Assert;
use App\Models\Server;
use Illuminate\Contracts\Foundation\Application;
abstract class DaemonRepository
{
/**
* @var Application
*/
protected $app;
/**
* @var Server|null
*/
protected $server;
/**
* @var Node|null
*/
protected $node;
/**
* BaseWingsRepository constructor.
*/
public function __construct(Application $application)
{
$this->app = $application;
}
/**
* Set the server model this request is stemming from.
*
* @return $this
*/
public function setServer(Server $server): static
{
$this->server = $server;
$this->setNode($this->server->node);
return $this;
}
/**
* Set the node model this request is stemming from.
*
* @return $this
*/
public function setNode(Node $node): static
{
$this->node = $node;
return $this;
}
/**
* Return an instance of the Guzzle HTTP Client to be used for requests.
*/
public function getHttpClient(array $headers = []): Client
{
Assert::isInstanceOf($this->node, Node::class);
return new Client([
'base_uri' => $this->node->getConnectionAddress(),
'timeout' => config('pterodactyl.guzzle.timeout'),
'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'),
'headers' => array_merge($headers, [
'Authorization' => 'Bearer ' . $this->node->getDecryptedKey(),
'Accept' => 'application/json',
'Content-Type' => 'application/json',
]),
]);
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace App\Repositories;
use Closure;
use InvalidArgumentException;
use Illuminate\Foundation\Application;
use App\Contracts\Repository\RepositoryInterface;
abstract class Repository implements RepositoryInterface
{
/**
* @var Application
*/
protected Application $app;
/**
* @var array
*/
protected array $columns = ['*'];
/**
* @var mixed
*/
protected mixed $model;
/**
* @var bool
*/
protected bool $withFresh = true;
/**
* Repository constructor.
*/
public function __construct(Application $application)
{
$this->app = $application;
$this->initializeModel($this->model());
}
/**
* Return the model backing this repository.
*
* @return string|Closure|object
*/
abstract public function model();
/**
* Return the model being used for this repository.
*
* @return mixed
*/
public function getModel(): mixed
{
return $this->model;
}
/**
* Setup column selection functionality.
*
* @param array|string $columns
*
* @return $this
*/
public function setColumns($columns = ['*']): Repository|static
{
$clone = clone $this;
$clone->columns = is_array($columns) ? $columns : func_get_args();
return $clone;
}
/**
* Return the columns to be selected in the repository call.
*
* @return array
*/
public function getColumns(): array
{
return $this->columns;
}
/**
* Stop repository update functions from returning a fresh
* model when changes are committed.
*
* @return $this
*/
public function withoutFreshModel(): Repository|static
{
return $this->setFreshModel(false);
}
/**
* Return a fresh model with a repository updates a model.
*
* @return $this
*/
public function withFreshModel()
{
return $this->setFreshModel(true);
}
/**
* Set whether or not the repository should return a fresh model
* when changes are committed.
*
* @return $this
*/
public function setFreshModel(bool $fresh = true)
{
$clone = clone $this;
$clone->withFresh = $fresh;
return $clone;
}
/**
* Take the provided model and make it accessible to the rest of the repository.
*
* @param array $model
*
* @return mixed
*/
protected function initializeModel(...$model): mixed
{
switch (count($model)) {
case 1:
return $this->model = $this->app->make($model[0]);
case 2:
return $this->model = call_user_func([$this->app->make($model[0]), $model[1]]);
default:
throw new InvalidArgumentException('Model must be a FQDN or an array with a count of two.');
}
}
}

View File

@@ -52,7 +52,7 @@ Route::group(['prefix' => '/servers'], function () {
/*
|--------------------------------------------------------------------------
| Server Controller Routes
| Node Controller Routes
|--------------------------------------------------------------------------
|
| Endpoint: /api/application/servers