Skip to content

Commit

Permalink
format fields can now be attributed with request details
Browse files Browse the repository at this point in the history
  • Loading branch information
eceltov committed Dec 18, 2024
1 parent aaf4ea6 commit 0372e5b
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 40 deletions.
41 changes: 31 additions & 10 deletions app/V1Module/presenters/base/BasePresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use App\Helpers\MetaFormats\FormatCache;
use App\Helpers\MetaFormats\MetaFormat;
use App\Helpers\MetaFormats\MetaRequest;
use App\Helpers\MetaFormats\RequestParamType;
use App\Responses\StorageFileResponse;
use App\Responses\ZipFilesResponse;
use Nette\Application\Application;
Expand Down Expand Up @@ -206,26 +207,43 @@ public function getMetaRequest(): MetaRequest|null

private function processParams(ReflectionMethod $reflection)
{
// $annotations = AnnotationsParser::getAll($reflection);
// $requiredFields = Arrays::get($annotations, "Param", []);

///TODO: add support for post/query type distinction
$format = MetaFormatHelper::extractFormatFromAttribute($reflection);

// ignore request that do not yet have the attribute
if ($format === null) {
return;
}

// get the parsed attribute data from the format fields
$formatToFieldDefinitionsMap = FormatCache::getFormatToFieldDefinitionsMap();
if (!array_key_exists($format, $formatToFieldDefinitionsMap)) {
throw new InternalServerException("The format $format is not defined.");
}

// maps field names to their attribute data
$nameToFieldDefinitionsMap = $formatToFieldDefinitionsMap[$format];

///TODO: handle nested MetaFormat creation
$fieldNames = FormatCache::getFormatFieldNames($format);
$formatInstance = MetaFormatHelper::createFormatInstance($format);
foreach ($fieldNames as $field) {
///TODO: check if required
$value = $this->getPostField($field, false);
if (!$formatInstance->checkedAssign($field, $value)) {
foreach ($nameToFieldDefinitionsMap as $fieldName => $fieldData) {
$requestParamData = $fieldData->requestData;
$this->logger->log(var_export($requestParamData, true), ILogger::DEBUG);

$value = null;
switch ($requestParamData->type) {
case RequestParamType::Post:
$value = $this->getPostField($fieldName, required: $requestParamData->required);
break;
case RequestParamType::Query:
$value = $this->getQueryField($fieldName, required: $requestParamData->required);
break;
default:
throw new InternalServerException("Unknown parameter type: {$requestParamData->type}");
}

if (!$formatInstance->checkedAssign($fieldName, $value)) {
///TODO: it would be nice to give a more detailed error message here
throw new InvalidArgumentException($field);
throw new InvalidArgumentException($fieldName);
}
}

Expand All @@ -236,6 +254,9 @@ private function processParams(ReflectionMethod $reflection)

$this->requestFormatInstance = $formatInstance;

// $annotations = AnnotationsParser::getAll($reflection);
// $requiredFields = Arrays::get($annotations, "Param", []);

// $this->logger->log(var_export($annotations, true), ILogger::DEBUG);
// $this->logger->log(var_export($requiredFields, true), ILogger::DEBUG);

Expand Down
2 changes: 1 addition & 1 deletion app/commands/MetaTester.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ public function test(string $arg)
// var_dump($format->checkIfAssignable("primaryAdminsIds", [ "10000000-2000-4000-8000-160000000000", "10000000-2000-4000-8000-160000000000" ]));

$format = new UserFormat();
$format->checkedAssign("titlesBeforeName", null);
var_dump($format->checkedAssign("email", "[email protected]"));
}
}
5 changes: 4 additions & 1 deletion app/helpers/MetaFormats/FieldFormatDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class FieldFormatDefinition
// A string name of the field type yielded by 'ReflectionProperty::getType()'.
public ?string $type;
public bool $nullable;
public RequestParamData $requestData;

///TODO: double check this
private static array $gettypeToReflectiveMap = [
Expand All @@ -29,9 +30,10 @@ class FieldFormatDefinition
* @param ?string $format The format of the field.
* @param ?string $type The PHP type of the field yielded by a 'ReflectionProperty::getType()' call.
* @param bool $nullable Whether the type is nullable.
* @param RequestParamData $requestData Request data such as param type and description.
* @throws \App\Exceptions\InternalServerException Thrown when both @format and @type were null.
*/
public function __construct(?string $format, ?string $type, bool $nullable)
public function __construct(?string $format, ?string $type, bool $nullable, RequestParamData $requestData)
{
// if both are null, there is no way to validate an assigned value
if ($format === null && $type === null) {
Expand All @@ -41,6 +43,7 @@ public function __construct(?string $format, ?string $type, bool $nullable)
$this->format = $format;
$this->type = $type;
$this->nullable = $nullable;
$this->requestData = $requestData;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/MetaFormats/FormatAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#[Attribute]
class FormatAttribute
{
public function __construct($format)
public function __construct(string $format)

Check failure on line 13 in app/helpers/MetaFormats/FormatAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Constructor of class App\Helpers\MetaFormats\FormatAttribute has an unused parameter $format.

Check failure on line 13 in app/helpers/MetaFormats/FormatAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Constructor of class App\Helpers\MetaFormats\FormatAttribute has an unused parameter $format.
{
}
}
46 changes: 23 additions & 23 deletions app/helpers/MetaFormats/FormatDefinitions/GroupFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@
#[FormatAttribute("group")]
class GroupFormat extends MetaFormat
{
#[FormatAttribute("uuid")]
public string $id;
#[FormatAttribute("uuid")]
public string $externalId;
public bool $organizational;
public bool $exam;
public bool $archived;
public bool $public;
public bool $directlyArchived;
#[FormatAttribute("localizedText[]")]
public array $localizedTexts;
#[FormatAttribute("uuid[]")]
public array $primaryAdminsIds;
#[FormatAttribute("uuid?")]
public string $parentGroupId;
#[FormatAttribute("uuid[]")]
public array $parentGroupsIds;
#[FormatAttribute("uuid[]")]
public array $childGroups;
#[FormatAttribute("groupPrivateData")]
public $privateData;
#[FormatAttribute("acl[]")]
public array $permissionHints;
// #[FormatAttribute("uuid")]
// public string $id;
// #[FormatAttribute("uuid")]
// public string $externalId;
// public bool $organizational;
// public bool $exam;
// public bool $archived;
// public bool $public;
// public bool $directlyArchived;
// #[FormatAttribute("localizedText[]")]
// public array $localizedTexts;
// #[FormatAttribute("uuid[]")]
// public array $primaryAdminsIds;
// #[FormatAttribute("uuid?")]
// public string $parentGroupId;
// #[FormatAttribute("uuid[]")]
// public array $parentGroupsIds;
// #[FormatAttribute("uuid[]")]
// public array $childGroups;
// #[FormatAttribute("groupPrivateData")]
// public $privateData;
// #[FormatAttribute("acl[]")]
// public array $permissionHints;
}
27 changes: 26 additions & 1 deletion app/helpers/MetaFormats/FormatDefinitions/UserFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,42 @@

use App\Helpers\MetaFormats\FormatAttribute;
use App\Helpers\MetaFormats\MetaFormat;
use App\Helpers\MetaFormats\RequestAttribute;
use App\Helpers\MetaFormats\RequestParamType;

#[FormatAttribute("userRegistration")]
class UserFormat extends MetaFormat
{
//#[FormatAttribute("email")]
#[FormatAttribute("email")]
#[RequestAttribute(type: RequestParamType::Post, description: "An email that will serve as a login name")]
public string $email;

#[RequestAttribute(type: RequestParamType::Post, description: "First name")]
public string $firstName;

#[RequestAttribute(type: RequestParamType::Post, description: "Last name")]
public string $lastName;

#[RequestAttribute(type: RequestParamType::Post, description: "A password for authentication")]
public string $password;

#[RequestAttribute(type: RequestParamType::Post, description: "A password confirmation")]
public string $passwordConfirm;

#[RequestAttribute(type: RequestParamType::Post, description: "Identifier of the instance to register in")]
public string $instanceId;

#[RequestAttribute(
type: RequestParamType::Post,
description: "Titles that are placed before user name",
required: false
)]
public ?string $titlesBeforeName;

#[RequestAttribute(
type: RequestParamType::Post,
description: "Titles that are placed after user name",
required: false
)]
public ?string $titlesAfterName;
}
23 changes: 22 additions & 1 deletion app/helpers/MetaFormats/MetaFormatHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ public static function extractFormatFromAttribute(
return $formatArguments[0];
}

public static function extractRequestAttributeData(
ReflectionClass|ReflectionProperty|ReflectionMethod $reflectionObject
): ?RequestParamData {
$requestAttribute = $reflectionObject->getAttributes(RequestAttribute::class);
if (count($requestAttribute) === 0) {
return null;
}

$requestArguments = $requestAttribute[0]->getArguments();
$type = $requestArguments["type"];
$description = array_key_exists("description", $requestArguments) ? $requestArguments["description"] : "";
$required = array_key_exists("required", $requestArguments) ? $requestArguments["required"] : true;

return new RequestParamData($type, $description, $required);
}

/**
* Parses the format attributes of class fields and returns their metadata.
* @param string $className The name of the class.
Expand All @@ -103,7 +119,12 @@ public static function createNameToFieldDefinitionsMap(string $className)
$fieldType = $reflectionType?->getName();

Check failure on line 119 in app/helpers/MetaFormats/MetaFormatHelper.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Call to an undefined method ReflectionType::getName().

Check failure on line 119 in app/helpers/MetaFormats/MetaFormatHelper.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Call to an undefined method ReflectionType::getName().
$nullable = $reflectionType?->allowsNull() ?? false;

$formats[$fieldName] = new FieldFormatDefinition($format, $fieldType, $nullable);
$requestParamData = self::extractRequestAttributeData($field);
if ($requestParamData === null) {
throw new InternalServerException("The field $fieldName of class $className does not have a RequestAttribute.");
}

$formats[$fieldName] = new FieldFormatDefinition($format, $fieldType, $nullable, $requestParamData);
}

return $formats;

Check failure on line 130 in app/helpers/MetaFormats/MetaFormatHelper.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Method App\Helpers\MetaFormats\MetaFormatHelper::createNameToFieldDefinitionsMap() should return array{format: string|null, type: string|null} but returns array<App\Helpers\MetaFormats\FieldFormatDefinition>.

Check failure on line 130 in app/helpers/MetaFormats/MetaFormatHelper.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Method App\Helpers\MetaFormats\MetaFormatHelper::createNameToFieldDefinitionsMap() should return array{format: string|null, type: string|null} but returns array<App\Helpers\MetaFormats\FieldFormatDefinition>.
Expand Down
3 changes: 2 additions & 1 deletion app/helpers/MetaFormats/PhpTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

// the string values have to match the return string of gettype()
// @codingStandardsIgnoreStart
enum PhpTypes: string {
enum PhpTypes: string
{
case String = "string";
case Int = "integer";
case Double = "double";
Expand Down
18 changes: 17 additions & 1 deletion app/helpers/MetaFormats/PrimitiveFormatValidators.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class PrimitiveFormatValidators
/**
* @format uuid
*/
public function validateUuid($uuid): bool
public function validateUuid(string $uuid): bool
{
if (!self::checkType($uuid, PhpTypes::String)) {
return false;
Expand All @@ -16,6 +16,22 @@ public function validateUuid($uuid): bool
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/', $uuid) === 1;
}

/**
* @format email
*/
public function validateEmail(string $email): bool
{
if (!self::checkType($email, PhpTypes::String)) {
return false;
}

if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
return false;
}

return true;
}

private static function checkType($value, PhpTypes $type): bool
{
return gettype($value) === $type->value;
Expand Down
16 changes: 16 additions & 0 deletions app/helpers/MetaFormats/RequestAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Helpers\MetaFormats;

use Attribute;

/**
* Attribute for request parameter details.
*/
#[Attribute]
class RequestAttribute
{
public function __construct(RequestParamType $type, string $description = "", bool $required = true)

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $description.

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $required.

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.2)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $type.

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $description.

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $required.

Check failure on line 13 in app/helpers/MetaFormats/RequestAttribute.php

View workflow job for this annotation

GitHub Actions / phpstan (8.3)

Constructor of class App\Helpers\MetaFormats\RequestAttribute has an unused parameter $type.
{
}
}
17 changes: 17 additions & 0 deletions app/helpers/MetaFormats/RequestParamData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Helpers\MetaFormats;

class RequestParamData
{
public RequestParamType $type;
public string $description;
public bool $required;

public function __construct(RequestParamType $type, string $description, bool $required)
{
$this->type = $type;
$this->description = $description;
$this->required = $required;
}
}
14 changes: 14 additions & 0 deletions app/helpers/MetaFormats/RequestParamType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Helpers\MetaFormats;

// @codingStandardsIgnoreStart
/**
* An enumeration of request parameter types.
*/
enum RequestParamType
{
case Post;
case Query;
}
// @codingStandardsIgnoreEnd

0 comments on commit 0372e5b

Please sign in to comment.