Skip to content

Commit

Permalink
Handle tags
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Nov 9, 2023
1 parent 905ce33 commit 23ca851
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 6 deletions.
14 changes: 9 additions & 5 deletions generate-spec
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ foreach ($parsedRoutes as $key => $value) {
}
}

$routeTags = Helpers::getAttributeTagsByScope($classMethod, 'OpenAPI', $routeName, $tagName, reset($scopes));

if ($isOCS && !array_key_exists("OCSMeta", $schemas)) {
$schemas["OCSMeta"] = [
"type" => "object",
Expand Down Expand Up @@ -404,7 +406,7 @@ foreach ($parsedRoutes as $key => $value) {
$routes[$scope] ??= [];
$routes[$scope][] = new Route(
$routeName,
$tagName,
$routeTags[$scope] ?? [$tagName],
$controllerName,
$methodName,
$postfix,
Expand All @@ -427,8 +429,10 @@ $tagNames = [];
if ($useTags) {
foreach ($routes as $scope => $scopeRoutes) {
foreach ($scopeRoutes as $route) {
if (!in_array($route->tag, $tagNames)) {
$tagNames[] = $route->tag;
foreach ($route->tags as $tag) {
if (!in_array($tag, $tagNames)) {
$tagNames[] = $tag;
}
}
}
}
Expand Down Expand Up @@ -581,7 +585,7 @@ foreach ($routes as $scope => $scopeRoutes) {
);
}

$operationId = [$route->tag];
$operationId = $route->tags;
$operationId = array_merge($operationId, array_map(fn(string $s) => Helpers::mapVerb(strtolower($s)), Helpers::splitOnUppercaseFollowedByNonUppercase($route->methodName)));
if ($route->postfix != null) {
$operationId[] = $route->postfix;
Expand All @@ -606,7 +610,7 @@ foreach ($routes as $scope => $scopeRoutes) {
$route->controllerMethod->summary != null ? ["summary" => $route->controllerMethod->summary] : [],
count($route->controllerMethod->description) > 0 ? ["description" => implode("\n", $route->controllerMethod->description)] : [],
$route->controllerMethod->isDeprecated ? ["deprecated" => true] : [],
$useTags ? ["tags" => [$route->tag]] : [],
$useTags ? ["tags" => $route->tags] : [],
count($security) > 0 ? ["security" => $security] : [],
count($queryParameters) > 0 || count($pathParameters) > 0 || $route->isOCS ? [
"parameters" => array_merge(
Expand Down
59 changes: 59 additions & 0 deletions src/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Exception;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
Expand Down Expand Up @@ -190,6 +192,63 @@ static function getAttributeScopes(ClassMethod|Class_|Node $node, string $annota
return $scopes;
}

static function getAttributeTagsByScope(ClassMethod|Class_|Node $node, string $annotation, string $routeName, string $defaultTag, string $defaultScope): array {
$tags = [];

/** @var Node\AttributeGroup $attrGroup */
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->getLast() === $annotation) {
if (empty($attr->args)) {
$tags[$defaultScope] = [$defaultTag];
continue;
}

$foundsTags = [];
$foundScopeName = null;
foreach ($attr->args as $arg) {
if ($arg->name->name === 'scope') {
if ($arg->value instanceof ClassConstFetch) {
if ($arg->value->class->getLast() === 'OpenAPI') {
$foundScopeName = match ($arg->value->name->name) {
'SCOPE_DEFAULT' => 'default',
'SCOPE_ADMINISTRATION' => 'administration',
'SCOPE_FEDERATION' => 'federation',
'SCOPE_IGNORE' => 'ignore',
// Fall back for future scopes assuming we follow the pattern (cut of 'SCOPE_' and lower case)
default => strtolower(substr($arg->value->name->name, 6)),
};
}
} elseif ($arg->value instanceof String_) {
$foundScopeName = $arg->value->value;
} else {
Logger::panic($routeName, 'Can not interpret value of scope provided in OpenAPI(scope: …) attribute. Please use string or OpenAPI::SCOPE_* constants');
}
}

if ($arg->name->name === 'tags') {
if ($arg->value instanceof Array_) {
foreach ($arg->value->items as $item) {
if ($item instanceof ArrayItem) {
if ($item->value instanceof String_) {
$foundsTags[] = $item->value->value;
}
}
}
}
}
}

if (!empty($foundsTags)) {
$tags[$foundScopeName ?: $defaultScope] = $foundsTags;
}
}
}
}

return $tags;
}

static function collectUsedRefs(array $data): array {
$refs = [];
if (isset($data['$ref'])) {
Expand Down
2 changes: 1 addition & 1 deletion src/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class Route {
public function __construct(
public string $name,
public string $tag,
public array $tags,
public string $controllerName,
public string $methodName,
public ?string $postfix,
Expand Down
1 change: 1 addition & 0 deletions tests/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@

['name' => 'Settings2#defaultAdminScopeOverwritten', 'url' => '/api/{apiVersion}/default-admin-overwritten', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
['name' => 'Settings2#defaultAdminScope', 'url' => '/api/{apiVersion}/default-admin', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
['name' => 'Settings2#movedToSettingsTag', 'url' => '/api/{apiVersion}/moved-with-tag', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
],
];
12 changes: 12 additions & 0 deletions tests/lib/Controller/Settings2Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,16 @@ public function defaultAdminScope(): DataResponse {
public function defaultAdminScopeOverwritten(): DataResponse {
return new DataResponse();
}

/**
* Route is only in the admin scope because there is no "NoAdminRequired" annotation or attribute
*
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
*
* 200: Personal settings updated
*/
#[OpenAPI(tags: ['settings', 'admin-settings'])]
public function movedToSettingsTag(): DataResponse {
return new DataResponse();
}
}
73 changes: 73 additions & 0 deletions tests/openapi-administration.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,79 @@
}
}
}
},
"/ocs/v2.php/apps/notifications/api/{apiVersion}/moved-with-tag": {
"post": {
"operationId": "settings-admin-settings-moved-to-settings-tag",
"summary": "Route is only in the admin scope because there is no \"NoAdminRequired\" annotation or attribute",
"description": "This endpoint requires admin access",
"tags": [
"settings",
"admin-settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "apiVersion",
"in": "path",
"required": true,
"schema": {
"type": "string",
"enum": [
"v2"
],
"default": "v2"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Personal settings updated",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
}
},
"tags": []
Expand Down

0 comments on commit 23ca851

Please sign in to comment.