Skip to content

Commit

Permalink
feat: add update command
Browse files Browse the repository at this point in the history
  • Loading branch information
lindyhopchris committed Jul 31, 2023
1 parent 22e2d8b commit cd26c1a
Show file tree
Hide file tree
Showing 29 changed files with 2,092 additions and 55 deletions.
45 changes: 45 additions & 0 deletions src/Contracts/Http/Controllers/Hooks/UpdateImplementation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Http\Controllers\Hooks;

use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Request;
use LaravelJsonApi\Contracts\Query\QueryParameters;

interface UpdateImplementation extends SaveImplementation
{
/**
* @param object $model
* @param Request $request
* @param QueryParameters $query
* @return void
* @throws HttpResponseException
*/
public function updating(object $model, Request $request, QueryParameters $query): void;

/**
* @param object $model
* @param Request $request
* @param QueryParameters $query
* @return void
* @throws HttpResponseException
*/
public function updated(object $model, Request $request, QueryParameters $query): void;
}
4 changes: 2 additions & 2 deletions src/Contracts/Store/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ public function create(ResourceType|string $resourceType): ResourceBuilder;
/**
* Update an existing resource.
*
* @param string $resourceType
* @param ResourceType|string $resourceType
* @param object|string $modelOrResourceId
* @return ResourceBuilder
*/
public function update(string $resourceType, $modelOrResourceId): ResourceBuilder;
public function update(ResourceType|string $resourceType, $modelOrResourceId): ResourceBuilder;

/**
* Delete an existing resource.
Expand Down
5 changes: 5 additions & 0 deletions src/Contracts/Validation/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ public function queryOne(): QueryOneValidator;
* @return StoreValidator
*/
public function store(): StoreValidator;

/**
* @return UpdateValidator
*/
public function update(): UpdateValidator;
}
46 changes: 46 additions & 0 deletions src/Contracts/Validation/UpdateValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Validation;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Extensions\Atomic\Operations\Update;

interface UpdateValidator
{
/**
* Extract validation data from the update operation.
*
* @param object $model
* @param Update $operation
* @return array
*/
public function extract(object $model, Update $operation): array;

/**
* Make a validator for the store operation.
*
* @param Request|null $request
* @param object $model
* @param Update $operation
* @return Validator
*/
public function make(?Request $request, object $model, Update $operation): Validator;
}
37 changes: 37 additions & 0 deletions src/Core/Auth/ResourceAuthorizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,43 @@ public function showOrFail(?Request $request, object $model): void
}
}

/**
* Authorize a JSON:API update command.
*
* @param Request|null $request
* @param object $model
* @return ErrorList|null
* @throws AuthorizationException
* @throws AuthenticationException
* @throws HttpExceptionInterface
*/
public function update(?Request $request, object $model): ?ErrorList
{
$passes = $this->authorizer->update(
$request,
$model,
);

return $passes ? null : $this->failed();
}

/**
* Authorize a JSON:API update command, or fail.
*
* @param Request|null $request
* @param object $model
* @return void
* @throws AuthorizationException
* @throws AuthenticationException
* @throws HttpExceptionInterface
*/
public function updateOrFail(?Request $request, object $model): void
{
if ($errors = $this->update($request, $model)) {
throw new JsonApiException($errors);
}
}

/**
* Authorize a JSON:API show related query.
*
Expand Down
7 changes: 7 additions & 0 deletions src/Core/Bus/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ abstract public function type(): ResourceType;
*/
abstract public function operation(): Operation;

/**
* Get the hooks implementation.
*
* @return object|null
*/
abstract public function hooks(): ?object;

/**
* Command constructor
*
Expand Down
68 changes: 68 additions & 0 deletions src/Core/Bus/Commands/Concerns/Identifiable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands\Concerns;

use RuntimeException;

trait Identifiable
{
/**
* @var object|null
*/
private ?object $model = null;

/**
* Return a new instance with the model set, if known.
*
* @param object|null $model
* @return static
*/
public function withModel(?object $model): static
{
$copy = clone $this;
$copy->model = $model;

return $copy;
}

/**
* Get the model for the query.
*
* @return object|null
*/
public function model(): ?object
{
return $this->model;
}

/**
* Get the model for the query.
*
* @return object
*/
public function modelOrFail(): object
{
if ($this->model !== null) {
return $this->model;
}

throw new RuntimeException('Expecting a model to be set on the query.');
}
}
3 changes: 3 additions & 0 deletions src/Core/Bus/Commands/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use LaravelJsonApi\Contracts\Bus\Commands\Dispatcher as DispatcherContract;
use LaravelJsonApi\Core\Bus\Commands\Store\StoreCommand;
use LaravelJsonApi\Core\Bus\Commands\Store\StoreCommandHandler;
use LaravelJsonApi\Core\Bus\Commands\Update\UpdateCommand;
use LaravelJsonApi\Core\Bus\Commands\Update\UpdateCommandHandler;
use RuntimeException;

class Dispatcher implements DispatcherContract
Expand Down Expand Up @@ -65,6 +67,7 @@ private function handlerFor(string $commandClass): string
{
return match ($commandClass) {
StoreCommand::class => StoreCommandHandler::class,
UpdateCommand::class => UpdateCommandHandler::class,
default => throw new RuntimeException('Unexpected command class: ' . $commandClass),
};
}
Expand Down
54 changes: 54 additions & 0 deletions src/Core/Bus/Commands/IsIdentifiable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands;

use LaravelJsonApi\Core\Document\Input\Values\ResourceId;

interface IsIdentifiable
{
/**
* Get the resource id for the command.
*
* @return ResourceId
*/
public function id(): ResourceId;

/**
* Get the model for the command, if there is one.
*
* @return object|null
*/
public function model(): ?object;

/**
* Get the model for the command, or fail if there isn't one.
*
* @return object
*/
public function modelOrFail(): object;

/**
* Return a new instance with the model set.
*
* @param object|null $model
* @return static
*/
public function withModel(?object $model): static;
}
67 changes: 67 additions & 0 deletions src/Core/Bus/Commands/Middleware/LookupModelIfMissing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands\Middleware;

use Closure;
use LaravelJsonApi\Contracts\Store\Store;
use LaravelJsonApi\Core\Bus\Commands\Command;
use LaravelJsonApi\Core\Bus\Commands\IsIdentifiable;
use LaravelJsonApi\Core\Bus\Commands\Result;
use LaravelJsonApi\Core\Document\Error;
use Symfony\Component\HttpFoundation\Response;

class LookupModelIfMissing
{
/**
* LookupModelIfMissing constructor
*
* @param Store $store
*/
public function __construct(private readonly Store $store)
{
}

/**
* Handle an identifiable command.
*
* @param IsIdentifiable&Command $command
* @param Closure $next
* @return Result
*/
public function handle(Command&IsIdentifiable $command, Closure $next): Result
{
if ($command->model() === null) {
$model = $this->store->find(
$command->type(),
$command->id(),
);

if ($model === null) {
return Result::failed(
Error::make()->setStatus(Response::HTTP_NOT_FOUND)
);
}

$command = $command->withModel($model);
}

return $next($command);
}
}
Loading

0 comments on commit cd26c1a

Please sign in to comment.