Skip to content

Commit

Permalink
Support collect swagger validation rules and attribute for mediaType …
Browse files Browse the repository at this point in the history
…request body. (#6380)
  • Loading branch information
xuanyanwow authored Dec 14, 2023
1 parent e3e7f2c commit 2776915
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 112 deletions.
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"Hyperf\\Swagger\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"HyperfTest\\Swagger\\": "tests/"
}
},
"suggest": {
"hyperf/validation": "Required to use SwaggerRequest."
},
Expand Down
59 changes: 0 additions & 59 deletions src/Request/AttributeCollector.php

This file was deleted.

44 changes: 4 additions & 40 deletions src/Request/RuleCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,13 @@
*/
namespace Hyperf\Swagger\Request;

use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Swagger\Annotation\JsonContent;
use Hyperf\Swagger\Annotation\Property;
use Hyperf\Swagger\Annotation\QueryParameter;
use Hyperf\Swagger\Annotation\RequestBody;
use Hyperf\Swagger\Util;

/**
* @deprecated will remove, please use Hyperf\Swagger\Request\ValidationCollector instead
*/
class RuleCollector
{
protected static $rules = [];

public static function get(string $class, string $method): array
{
if (! empty(static::$rules[$class][$method])) {
return static::$rules[$class][$method];
}
$methodAnnotations = AnnotationCollector::getClassMethodAnnotation($class, $method);
/** @var QueryParameter[] $queryParameters */
$queryParameters = Util::findAnnotations($methodAnnotations, QueryParameter::class);

$rules = [];
foreach ($queryParameters as $parameter) {
if ($parameter->rules) {
$rules[$parameter->name] = $parameter->rules;
}
}

/** @var RequestBody $body */
$body = Util::findAnnotations($methodAnnotations, RequestBody::class)[0] ?? null;
if ($body) {
if ($body->_content instanceof JsonContent) {
if (is_array($body->_content->properties)) {
foreach ($body->_content->properties as $property) {
if ($property instanceof Property) {
if ($property->rules) {
$rules[$property->property] = $property->rules;
}
}
}
}
}
}

return static::$rules[$class][$method] = $rules;
return ValidationCollector::get($class, $method, 'rules');
}
}
45 changes: 32 additions & 13 deletions src/Request/SwaggerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,19 @@ public function authorize(): bool
*/
public function rules(): array
{
/** @var Dispatched $dispatched */
$dispatched = RequestContext::getOrNull()?->getAttribute(Dispatched::class);
if (! $dispatched) {
throw new RuntimeException('The request is invalid.');
}
$callback = $this->getCallbackByContext();

$callback = $dispatched->handler?->callback;
if (! $callback || ! is_array($callback)) {
throw new RuntimeException('The SwaggerRequest is only used by swagger annotations.');
}

return RuleCollector::get($callback[0], $callback[1]);
return ValidationCollector::get($callback[0], $callback[1], 'rules');
}

public function attributes(): array
{
$callback = $this->getCallbackByContext();

return ValidationCollector::get($callback[0], $callback[1], 'attribute');
}

protected function getCallbackByContext(): array
{
/** @var Dispatched $dispatched */
$dispatched = RequestContext::getOrNull()?->getAttribute(Dispatched::class);
Expand All @@ -54,10 +52,31 @@ public function attributes(): array
}

$callback = $dispatched->handler?->callback;
if (! $callback || ! is_array($callback)) {
if (! $callback) {
throw new RuntimeException('The SwaggerRequest is only used by swagger annotations.');
}

return AttributeCollector::get($callback[0], $callback[1]);
return $this->prepareHandler($callback);
}

/**
* @see \Hyperf\HttpServer\CoreMiddleware::prepareHandler()
*/
protected function prepareHandler(array|string $handler): array
{
if (is_string($handler)) {
if (str_contains($handler, '@')) {
return explode('@', $handler);
}
$array = explode('::', $handler);
if (! isset($array[1]) && class_exists($handler) && method_exists($handler, '__invoke')) {
$array[1] = '__invoke';
}
return [$array[0], $array[1] ?? null];
}
if (is_array($handler) && isset($handler[0], $handler[1])) {
return $handler;
}
throw new RuntimeException('Handler not exist.');
}
}
111 changes: 111 additions & 0 deletions src/Request/ValidationCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact [email protected]
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Swagger\Request;

use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Swagger\Annotation\JsonContent;
use Hyperf\Swagger\Annotation\MediaType;
use Hyperf\Swagger\Annotation\Property;
use Hyperf\Swagger\Annotation\QueryParameter;
use Hyperf\Swagger\Annotation\RequestBody;
use Hyperf\Swagger\Util;

class ValidationCollector
{
protected static array $data = [];

/**
* @param string $field rules or attribute
*/
public static function get(string $class, string $method, string $field): array
{
if (! empty(static::$data[$class][$method][$field])) {
return static::$data[$class][$method][$field];
}

$methodAnnotations = AnnotationCollector::getClassMethodAnnotation($class, $method);

$data = [];
$data = self::collectQueryParameter($methodAnnotations, $data, $field);
$data = self::collectJsonContentRequestBody($methodAnnotations, $data, $field);
$data = self::collectMediaTypeRequestBody($methodAnnotations, $data, $field);

return static::$data[$class][$method][$field] = $data;
}

protected static function collectQueryParameter($methodAnnotations, array $data, string $field): array
{
/** @var QueryParameter[] $queryParameters */
$queryParameters = Util::findAnnotations($methodAnnotations, QueryParameter::class);

foreach ($queryParameters as $parameter) {
if (isset($property->{$field}) && $property->{$field}) {
$data[$parameter->name] = $parameter->{$field};
}
}
return $data;
}

protected static function collectJsonContentRequestBody($methodAnnotations, array $data, string $field): array
{
/** @var RequestBody $body */
$body = Util::findAnnotations($methodAnnotations, RequestBody::class)[0] ?? null;
if (! $body) {
return $data;
}

if (! $body->_content instanceof JsonContent) {
return $data;
}

if (! is_array($body->_content->properties)) {
return $data;
}

foreach ($body->_content->properties as $property) {
if ($property instanceof Property) {
if (isset($property->{$field}) && $property->{$field}) {
$data[$property->property] = $property->{$field};
}
}
}

return $data;
}

protected static function collectMediaTypeRequestBody($methodAnnotations, array $data, string $field): array
{
/** @var RequestBody $body */
$body = Util::findAnnotations($methodAnnotations, RequestBody::class)[0] ?? null;
if (! $body || ! is_array($body->content)) {
return $data;
}

foreach ($body->content as $content) {
if (! $content instanceof MediaType) {
continue;
}
if (! $content->schema || ! is_array($content->schema->properties)) {
continue;
}
foreach ($content->schema->properties as $property) {
if (! $property instanceof Property) {
continue;
}
if (isset($property->{$field}) && $property->{$field}) {
$data[$property->property] = $property->{$field};
}
}
}
return $data;
}
}
61 changes: 61 additions & 0 deletions tests/Stub/ExampleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact [email protected]
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace HyperfTest\Swagger\Stub;

use Hyperf\Swagger\Annotation as SA;
use Hyperf\Swagger\Request\SwaggerRequest;

class ExampleController
{
#[SA\Post('/hyperf/example/index', summary: '单测 index', tags: ['hyperf'])]
#[SA\QueryParameter(
name: 'token',
description: 'token',
example: 'token',
rules: 'required|string|max:25',
)]
#[SA\RequestBody(
description: '请求参数',
content: [
new SA\MediaType(
mediaType: 'application/x-www-form-urlencoded',
schema: new SA\Schema(
properties: [
new SA\Property(property: 'name', description: '昵称', type: 'string', example: 'user-2', rules: 'required|string|max:3', attribute: 'nickname'),
]
),
),
],
)]
public function index(SwaggerRequest $request): void
{
}

#[SA\Post('/hyperf/example/json', summary: '单测 json', tags: ['hyperf'])]
#[SA\QueryParameter(
name: 'token',
description: 'token',
example: 'token',
rules: 'required|string|max:25',
)]
#[SA\RequestBody(
description: '请求参数',
content: new SA\JsonContent(
properties: [
new SA\Property(property: 'name', description: '昵称', type: 'string', example: 'user-2', rules: 'required|int', attribute: 'json-name'),
]
),
)]
public function json(SwaggerRequest $request): void
{
}
}
Loading

0 comments on commit 2776915

Please sign in to comment.