Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Data Transfer Object classes generation from swagger spec
- Route info in the request attribute
- CacheableDispatcherFactoryProxy
- JsonApiResponseFactory::createRelationshipResponse()
- RateLimitMiddleware and related packages
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
<?php

namespace FreeElephants\JsonApiToolkit\JsonApi\Request;

use FreeElephants\JsonApiToolkit\JsonApi\Request\Route\RelationshipRoute;
use FreeElephants\JsonApiToolkit\JsonApi\Request\Route\RouteFactory;
use FreeElephants\JsonApiToolkit\JsonApi\Request\Route\RouteInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
use stdClass;

class JsonApiServerRequestDecorator implements JsonApiServerRequestInterface
{
private ServerRequestInterface $request;
private ?array $decodedBody;

public function __construct(ServerRequestInterface $request, RouteFactory $routeFactory)
{
$this->request = $request;
}

public function getProtocolVersion()
{
return $this->request->getProtocolVersion();
}

public function withProtocolVersion($version)
{
return $this->request->withProtocolVersion($version);
}

public function getHeaders()
{
return $this->request->getHeaders();
}

public function hasHeader($name)
{
return $this->request->hasHeader($name);
}

public function getHeader($name)
{
return $this->request->getHeader($name);
}

public function getHeaderLine($name)
{
return $this->request->getHeaderLine($name);
}

public function withHeader($name, $value)
{
return $this->request->withHeader($name, $value);
}

public function withAddedHeader($name, $value)
{
return $this->request->withAddedHeader($name, $value);
}

public function withoutHeader($name)
{
return $this->request->withoutHeader($name);
}

public function getBody()
{
return $this->request->getBody();
}

public function withBody(StreamInterface $body)
{
return $this->request->withBody($body);
}

public function getRequestTarget()
{
return $this->request->getRequestTarget();
}

public function withRequestTarget($requestTarget)
{
return $this->request->withRequestTarget($requestTarget);
}

public function getMethod()
{
return $this->request->getMethod();
}

public function withMethod($method)
{
return $this->request->withMethod($method);
}

public function getUri()
{
return $this->request->getUri();
}

public function withUri(UriInterface $uri, $preserveHost = false)
{
return $this->request->withUri($uri, $preserveHost);
}

public function getServerParams()
{
return $this->request->getServerParams();
}

public function getCookieParams()
{
return $this->request->getCookieParams();
}

public function withCookieParams(array $cookies)
{
return $this->request->withCookieParams($cookies);
}

public function getQueryParams()
{
return $this->request->getQueryParams();
}

public function withQueryParams(array $query)
{
return $this->request->withQueryParams($query);
}

public function getUploadedFiles()
{
return $this->request->getUploadedFiles();
}

public function withUploadedFiles(array $uploadedFiles)
{
return $this->request->withUploadedFiles($uploadedFiles);
}

public function getParsedBody()
{
return $this->request->getParsedBody();
}

public function withParsedBody($data)
{
return $this->request->withParsedBody($data);
}

public function getAttributes()
{
return $this->request->getAttributes();
}

public function getAttribute($name, $default = null)
{
return $this->request->getAttribute($name, $default);
}

public function withAttribute($name, $value)
{
return $this->request->withAttribute($name, $value);
}

public function withoutAttribute($name)
{
return $this->request->withoutAttribute($name);
}

private function getRoute(): RouteInterface
{
return $this->request->getAttribute(JsonApiServerRequestInterface::ATTRIBUTE_ROUTE_NAME);
}

private function getDecodedBody(): ?array
{
if (isset($this->decodedBody)) {
return $this->decodedBody;
}

$this->request->getBody()->rewind();
$this->decodedBody = json_decode($this->request->getBody()->getContents(), true);

return $this->decodedBody;
}

public function getDocumentId(): ?string
{
return $this->getDecodedBody()['data']['id'] ?? null;
}

public function getDocumentType(): ?string
{
return $this->getDecodedBody()['data']['type'] ?? null;
}

public function getPrimeAttributeName(): ?string
{
return $this->getRoute()->getRouteParamName();
}

/**
* @return mixed
*/
public function getPrimeAttributeValue()
{
return $this->getAttribute($this->getPrimeAttributeName());
}

public function getEndpointTypeName(): string
{
return $this->getRoute()->getEndpointTypeName();
}

/**
* @see MemberTypeEnum
*/
public function getRelationshipOriginName(): string
{
return $this->getRoute()->getRelationshipOriginName();
}

public function getRequestType(): int
{
return $this->getRoute()->getType();
}

public function getDocumentAttributes(): ?stdClass
{
$this->request->getBody()->rewind();

$decodedBody = json_decode($this->request->getBody()->getContents());

return $decodedBody->data->attributes ?? null;
}

/**
* @return array<string, array>
*/
public function getRelationships(): array
{
$body = $this->getDecodedBody();

switch ($this->getRoute()->getType()) {
case RouteInterface::TYPE_RESOURCES_COLLECTION:
case RouteInterface::TYPE_RESOURCE:
case RouteInterface::TYPE_MEMBER:
$relationships = $body['data']['relationships'] ?? [];
break;
case RouteInterface::TYPE_RELATIONSHIP:
/** @var RelationshipRoute $route */
$route = $this->getRoute();
$relationships = [$route->getRelationshipName() => $body];
break;
default:
$relationships = [];
}

return $relationships;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace FreeElephants\JsonApiToolkit\JsonApi\Request;

use Psr\Http\Message\ServerRequestInterface;

interface JsonApiServerRequestInterface extends ServerRequestInterface
{
const ATTRIBUTE_ROUTE_NAME = 'route';

public function getDocumentId(): ?string;

public function getDocumentType(): ?string;

public function getPrimeAttributeName(): ?string;

/**
* @return mixed
*/
public function getPrimeAttributeValue();

public function getEndpointTypeName(): string;

/**
* @see MemberTypeEnum
*/
public function getRelationshipOriginName(): string;

public function getRequestType(): int;

public function getDocumentAttributes(): ?object;

/**
* @return array<string, array>
*/
public function getRelationships(): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace FreeElephants\JsonApiToolkit\JsonApi\Request;

class MappingConfigProvider
{
public const ENDPOINT_TYPE_TO_DOCUMENT_TYPE = 0;
public const RESOURCE_TYPE_TO_ROUTE_PARAM_KEY = 1;
public const TYPE_TO_CLASSNAME = 2;

protected array $endpointTypeToDocumentTypeMapping;
protected array $resourceTypeToRouteParamKeyTypeMapping;
protected array $typenameToClassnameMapping;

/**
* @example
* [
* ENDPOINT_TYPE_TO_DOCUMENT_TYPE => [
* 'foo' => 'bar',
* ],
* ]
*
* @param array<int, array<string, string>> $mapping
*/
public function __construct(array $mapping)
{
$this->endpointTypeToDocumentTypeMapping = $mapping[self::ENDPOINT_TYPE_TO_DOCUMENT_TYPE] ?? [];
$this->resourceTypeToRouteParamKeyTypeMapping = $mapping[self::RESOURCE_TYPE_TO_ROUTE_PARAM_KEY] ?? [];
$this->typenameToClassnameMapping = $mapping[self::TYPE_TO_CLASSNAME] ?? [];
}

public function isEndpointToDocumentMapped(string $endpointType): bool
{
return array_key_exists($endpointType, $this->endpointTypeToDocumentTypeMapping);
}

public function isResourceToRouteParamKeyMapped(string $resourceType): bool
{
return array_key_exists($resourceType, $this->resourceTypeToRouteParamKeyTypeMapping);
}

public function isTypenameToClassnameMapped(string $typename): bool
{
return array_key_exists($typename, $this->typenameToClassnameMapping);
}

public function getDocumentTypeByEndpointType(string $endpointType): ?string
{
return $this->endpointTypeToDocumentTypeMapping[$endpointType] ?? null;
}

public function getRouteParamKeyByResourceType(string $resourceType): ?string
{
return $this->resourceTypeToRouteParamKeyTypeMapping[$resourceType] ?? null;
}

public function getClassnameByTypename(?string $typename): ?string
{
return $this->typenameToClassnameMapping[$typename] ?? null;
}
}
Loading