Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TASK: Allow simple types string, int, bool` as method arguments #2

Closed
wants to merge 8 commits into from
Closed
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
46 changes: 46 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: build

on:
push:
branches:
- 'main'
pull_request:
branches:
- 'main'

jobs:
test:
name: "Test (PHP ${{ matrix.php-versions }}, Flow ${{ matrix.flow-versions }})"

strategy:
fail-fast: false
matrix:
php-versions: ["8.2"]
flow-versions: ["8.3"]
include:
- php-versions: '8.3'
flow-versions: '8.3'

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: ${{ env.FLOW_FOLDER }}

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite
ini-values: date.timezone="Africa/Tunis", opcache.fast_shutdown=0, apc.enable_cli=on

- name: Set Neos Version
run: composer require neos/flow ^${{ matrix.flow-versions }} --no-progress --no-interaction

- name: Run Linter
run: composer lint

- name: Run Test
run: composer test
6 changes: 4 additions & 2 deletions Classes/Application/OpenApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Controller\ControllerInterface;
use Neos\Flow\Mvc\Routing\UriBuilder;
use Sitegeist\SchemeOnYou\Domain\Schema\SchemaSerializer;

#[Flow\Scope('singleton')]
abstract class OpenApiController implements ControllerInterface
Expand All @@ -27,6 +28,7 @@ final public function processRequest(ActionRequest $request, ActionResponse $res
$this->request->setDispatched(true);
$this->response = $response;
$this->response->setContentType('application/json');
$this->response->addHttpHeader('Access-Control-Allow-Origin', '*');
$uriBuilder = new UriBuilder();
$uriBuilder->setRequest($this->request);
$this->controllerContext = new ControllerContext(
Expand All @@ -36,7 +38,7 @@ final public function processRequest(ActionRequest $request, ActionResponse $res
$uriBuilder
);

$actionName = $request->getControllerActionName() . 'Endpoint';
$actionName = $request->getControllerActionName() . 'Action';
if (!method_exists($this, $actionName)) {
throw new \DomainException(
'Missing endpoint "' . $request->getControllerActionName() . '" in ' . static::class,
Expand All @@ -48,6 +50,6 @@ final public function processRequest(ActionRequest $request, ActionResponse $res

$result = $this->$actionName(...$parameters);

$this->response->setContent(\json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
$this->response->setContent(SchemaSerializer::serializeValue($result));
}
}
68 changes: 39 additions & 29 deletions Classes/Application/ParameterFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
{
/**
* @param class-string $className
* @return array<string,RequestParameterContract>
* @return array<string,RequestParameterContract|bool|int|string|float>
*/
public static function resolveParameters(string $className, string $methodName, ActionRequest $request): array
{
Expand All @@ -37,36 +37,46 @@ public static function resolveParameters(string $className, string $methodName,
if (!$type instanceof \ReflectionNamedType) {
throw new \DomainException('Can only resolve named parameters with single type', 1709721743);
}
$parameterClassName = $type->getName();
if (!class_exists($parameterClassName)) {
throw new \DomainException('Can only resolve parameters of type class', 1709721783);
if ($type->allowsNull()) {
throw new \DomainException('Nullable types are not supported yet', 1709721755);
}
$parameterReflectionClass = new \ReflectionClass($parameterClassName);
if (!$parameterReflectionClass->implementsInterface(RequestParameterContract::class)) {
throw new \DomainException(
'Can only resolve parameters of type ' . RequestParameterContract::class,
1709722058
);
}
/** @var class-string<RequestParameterContract> $parameterClassName */
$parameters[$parameter->name] = $parameterClassName::fromRequestParameter(
match (ParameterAttribute::tryFromReflectionParameter($parameter)?->in) {
ParameterLocation::LOCATION_PATH => $request->getArgument($parameter->name),
ParameterLocation::LOCATION_QUERY => $request->getHttpRequest()->getQueryParams()[$parameter->name],
ParameterLocation::LOCATION_HEADER => $request->getHttpRequest()->getHeader($parameter->name),
ParameterLocation::LOCATION_COOKIE
=> $request->getHttpRequest()->getCookieParams()[$parameter->name],
null => match (RequestBody::fromReflectionParameter($parameter)->contentType) {
RequestBodyContentType::CONTENT_TYPE_JSON => \json_decode(
(string)$request->getHttpRequest()->getBody(),
true,
512,
JSON_THROW_ON_ERROR
),
RequestBodyContentType::CONTENT_TYPE_FORM => $request->getArgument($parameter->name)
}
$parameterTypeName = $type->getName();
$parameterValueFromRequest = match (ParameterAttribute::tryFromReflectionParameter($parameter)?->in) {
ParameterLocation::LOCATION_PATH => $request->getArgument($parameter->name),
ParameterLocation::LOCATION_QUERY => $request->getHttpRequest()->getQueryParams()[$parameter->name],
ParameterLocation::LOCATION_HEADER => $request->getHttpRequest()->getHeader($parameter->name),
ParameterLocation::LOCATION_COOKIE
=> $request->getHttpRequest()->getCookieParams()[$parameter->name],
null => match (RequestBody::fromReflectionParameter($parameter)->contentType) {
RequestBodyContentType::CONTENT_TYPE_JSON => \json_decode(
(string)$request->getHttpRequest()->getBody(),
true,
512,
JSON_THROW_ON_ERROR
),
RequestBodyContentType::CONTENT_TYPE_FORM => $request->getArgument($parameter->name)
}
};

if (class_exists($parameterTypeName)) {
$parameterReflectionClass = new \ReflectionClass($parameterTypeName);
if (!$parameterReflectionClass->implementsInterface(RequestParameterContract::class)) {
throw new \DomainException(
'Can only resolve parameters of type ' . RequestParameterContract::class,
1709722058
);
}
);
/** @var class-string<RequestParameterContract> $parameterTypeName */
$parameters[$parameter->name] = $parameterTypeName::fromRequestParameter($parameterValueFromRequest);
} else {
$parameters[$parameter->name] = match ($parameterTypeName) {
'string' => (string) $parameterValueFromRequest,
'int' => (int) $parameterValueFromRequest,
'float' => (float) $parameterValueFromRequest,
'bool' => (bool) $parameterValueFromRequest,
default => throw new \DomainException(sprintf('Cannot resolve parameters of type %s', $parameterTypeName), 1709721783)
};
}
}

return $parameters;
Expand Down
18 changes: 8 additions & 10 deletions Classes/Command/OpenApiCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,20 @@
#[Flow\Scope('singleton')]
final class OpenApiCommandController extends CommandController
{
#[Flow\InjectConfiguration(path: 'schemaTargetFilePath')]
protected string $schemaTargetFilePath;

#[Flow\Inject]
protected OpenApiDocumentRepository $documentRepository;

public function renderDocumentCommand(): void
/**
* @param string $name the name of the api document to render
*/
public function documentCommand(string $name): void
{
$schema = $this->documentRepository->findDocument();
file_put_contents(
/** @phpstan-ignore-next-line known constant */
FLOW_PATH_ROOT . $this->schemaTargetFilePath,
$schema = $this->documentRepository->findDocumentByName($name);
$this->output->output(
\json_encode(
$schema,
JSON_THROW_ON_ERROR + JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE
)
JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
) . PHP_EOL
);
}
}
29 changes: 29 additions & 0 deletions Classes/Controller/OpenApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Sitegeist\SchemeOnYou\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Mvc\View\JsonView;
use Sitegeist\SchemeOnYou\Domain\OpenApiDocumentRepository;

class OpenApiController extends ActionController
{
protected $defaultViewObjectName = JsonView::class;

#[Flow\Inject]
protected OpenApiDocumentRepository $documentRepository;

public function documentAction(string $name): string
{
$schema = $this->documentRepository->findDocumentByName($name);
$this->response->setContentType('application\json');
$this->response->addHttpHeader('Access-Control-Allow-Origin', '*');
return \json_encode(
$schema,
JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
);
}
}
24 changes: 14 additions & 10 deletions Classes/Domain/Metadata/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ public function __construct(
public static function fromReflectionClass(\ReflectionClass $reflection): self
{
$definitionReflections = $reflection->getAttributes(Schema::class);
if (count($definitionReflections) !== 1) {
throw new \DomainException(
'There must be exactly one schema attribute declared in class ' . $reflection->name . ', '
. count($definitionReflections) . ' given',
1709537723
if (count($definitionReflections) === 0) {
return new self(
'',
$reflection->getShortName(),
);
} elseif (count($definitionReflections) === 1) {
$arguments = $definitionReflections[0]->getArguments();
return new self(
$arguments['description'] ?? $arguments[0],
$arguments['name'] ?? $arguments[1] ?? $reflection->getShortName(),
);
}
$arguments = $definitionReflections[0]->getArguments();

return new self(
$arguments['description'] ?? $arguments[0],
$arguments['name'] ?? $arguments[1] ?? $reflection->getShortName(),
throw new \DomainException(
'There must be exactly one schema attribute declared in class ' . $reflection->name . ', '
. count($definitionReflections) . ' given',
1709537723
);
}
}
Loading
Loading