Skip to content

Commit 1e2895a

Browse files
committed
fix: Ignore IUser and other services in method parameters
Assisted-by: ClaudeCode:claude-sonnet-5 Signed-off-by: Carl Schwan <carl@carlschwan.eu>
1 parent da79825 commit 1e2895a

7 files changed

Lines changed: 340 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
### Added
1414
- Support backed enums (`enum Foo: string`/`enum Foo: int`) as OpenAPI types, resolved from their native parameter type hint
1515
- Support the built-in `SortDirection` enum from PHP 8.6 (unbacked, so its cases are mapped to the `'ASC'`/`'DESC'` strings)
16+
- Ignore controller method parameters typed as an injected service (e.g. `\OCP\IUser`) instead of requiring docs for them and listing them in the generated OpenAPI file
1617

1718
### Fixed
1819
- Clean whitespace in description fields

src/ControllerMethod.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,13 @@ public static function parse(string $context,
468468
foreach ($methodParameters as $methodParameter) {
469469
$methodParameterName = $methodParameter->var->name;
470470

471+
// Services like `\OCP\IUser` are injected by the dispatcher, never
472+
// filled from the request - they need no docs and aren't part of
473+
// the API surface.
474+
if (OpenApiType::isInjectedParameter($methodParameter->type)) {
475+
continue;
476+
}
477+
471478
$paramTag = null;
472479
$psalmParamTag = null;
473480
foreach ($docParameters as $docParameterType => $typeDocParameters) {

src/OpenApiType.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,26 @@ enum: $enum->enum,
439439
);
440440
}
441441

442+
public static function isInjectedParameter(?Node $node): bool {
443+
if ($node instanceof NullableType) {
444+
$node = $node->type;
445+
}
446+
if (!$node instanceof Name) {
447+
return false;
448+
}
449+
if (self::resolveNativeEnum('', $node) !== null) {
450+
return false;
451+
}
452+
// Anything else resolveIdentifier recognizes (e.g. the built-in
453+
// `SortDirection`) is a real, documentable type, not a service.
454+
try {
455+
self::resolveIdentifier('', [], $node->getLast());
456+
return false;
457+
} catch (LoggerException) {
458+
return true;
459+
}
460+
}
461+
442462
/**
443463
* @param OpenApiType[] $types
444464
* @return OpenApiType[]

tests/appinfo/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
['name' => 'Settings#custom403', 'url' => '/api/{apiVersion}/custom/403', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
9696
['name' => 'Settings#stringBackedEnumParameter', 'url' => '/api/{apiVersion}/enums/string-backed', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
9797
['name' => 'Settings#sortDirectionParameter', 'url' => '/api/{apiVersion}/enums/sort-direction', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
98+
['name' => 'Settings#injectedServiceParameter', 'url' => '/api/{apiVersion}/injected-service', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']],
9899
['name' => 'V1\SubDir#subDirRoute', 'url' => '/sub-dir', 'verb' => 'GET'],
99100
],
100101
];

tests/lib/Controller/SettingsController.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use OCP\AppFramework\Http\JSONResponse;
2424
use OCP\AppFramework\OCS\OCSNotFoundException;
2525
use OCP\AppFramework\OCSController;
26+
use OCP\IUser;
2627

2728
/**
2829
* @psalm-import-type NotificationsPushDevice from ResponseDefinitions
@@ -875,4 +876,16 @@ public function stringBackedEnumParameter(NotificationLevel $level): DataRespons
875876
public function sortDirectionParameter(\SortDirection $direction): DataResponse {
876877
return new DataResponse();
877878
}
879+
880+
/**
881+
* A route with an injected service parameter, which needs no docs and isn't part of the API surface
882+
*
883+
* @param string $path Path of the file
884+
* @return DataResponse<Http::STATUS_OK, array{}, array{}>
885+
*
886+
* 200: OK
887+
*/
888+
public function injectedServiceParameter(IUser $user, string $path): DataResponse {
889+
return new DataResponse();
890+
}
878891
}

tests/openapi-administration.json

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9566,6 +9566,155 @@
95669566
}
95679567
}
95689568
},
9569+
"/ocs/v2.php/apps/notifications/api/{apiVersion}/injected-service": {
9570+
"post": {
9571+
"operationId": "settings-injected-service-parameter",
9572+
"summary": "A route with an injected service parameter, which needs no docs and isn't part of the API surface",
9573+
"description": "This endpoint requires admin access",
9574+
"tags": [
9575+
"settings"
9576+
],
9577+
"security": [
9578+
{
9579+
"bearer_auth": []
9580+
},
9581+
{
9582+
"basic_auth": []
9583+
}
9584+
],
9585+
"requestBody": {
9586+
"required": true,
9587+
"content": {
9588+
"application/json": {
9589+
"schema": {
9590+
"type": "object",
9591+
"required": [
9592+
"path"
9593+
],
9594+
"properties": {
9595+
"path": {
9596+
"type": "string",
9597+
"description": "Path of the file"
9598+
}
9599+
}
9600+
}
9601+
}
9602+
}
9603+
},
9604+
"parameters": [
9605+
{
9606+
"name": "apiVersion",
9607+
"in": "path",
9608+
"required": true,
9609+
"schema": {
9610+
"type": "string",
9611+
"enum": [
9612+
"v2"
9613+
],
9614+
"default": "v2"
9615+
}
9616+
},
9617+
{
9618+
"name": "OCS-APIRequest",
9619+
"in": "header",
9620+
"description": "Required to be true for the API request to pass",
9621+
"required": true,
9622+
"schema": {
9623+
"type": "boolean",
9624+
"default": true
9625+
}
9626+
}
9627+
],
9628+
"responses": {
9629+
"200": {
9630+
"description": "OK",
9631+
"content": {
9632+
"application/json": {
9633+
"schema": {
9634+
"type": "object",
9635+
"required": [
9636+
"ocs"
9637+
],
9638+
"properties": {
9639+
"ocs": {
9640+
"type": "object",
9641+
"required": [
9642+
"meta",
9643+
"data"
9644+
],
9645+
"properties": {
9646+
"meta": {
9647+
"$ref": "#/components/schemas/OCSMeta"
9648+
},
9649+
"data": {
9650+
"type": "object"
9651+
}
9652+
}
9653+
}
9654+
}
9655+
}
9656+
}
9657+
}
9658+
},
9659+
"401": {
9660+
"description": "Current user is not logged in",
9661+
"content": {
9662+
"application/json": {
9663+
"schema": {
9664+
"type": "object",
9665+
"required": [
9666+
"ocs"
9667+
],
9668+
"properties": {
9669+
"ocs": {
9670+
"type": "object",
9671+
"required": [
9672+
"meta",
9673+
"data"
9674+
],
9675+
"properties": {
9676+
"meta": {
9677+
"$ref": "#/components/schemas/OCSMeta"
9678+
},
9679+
"data": {}
9680+
}
9681+
}
9682+
}
9683+
}
9684+
}
9685+
}
9686+
},
9687+
"403": {
9688+
"description": "Logged in account must be an admin",
9689+
"content": {
9690+
"application/json": {
9691+
"schema": {
9692+
"type": "object",
9693+
"required": [
9694+
"ocs"
9695+
],
9696+
"properties": {
9697+
"ocs": {
9698+
"type": "object",
9699+
"required": [
9700+
"meta",
9701+
"data"
9702+
],
9703+
"properties": {
9704+
"meta": {
9705+
"$ref": "#/components/schemas/OCSMeta"
9706+
},
9707+
"data": {}
9708+
}
9709+
}
9710+
}
9711+
}
9712+
}
9713+
}
9714+
}
9715+
}
9716+
}
9717+
},
95699718
"/ocs/v2.php/apps/notifications/optional-parameters": {
95709719
"post": {
95719720
"operationId": "admin_settings-optional-parameters",

tests/openapi-full.json

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9766,6 +9766,155 @@
97669766
}
97679767
}
97689768
},
9769+
"/ocs/v2.php/apps/notifications/api/{apiVersion}/injected-service": {
9770+
"post": {
9771+
"operationId": "settings-injected-service-parameter",
9772+
"summary": "A route with an injected service parameter, which needs no docs and isn't part of the API surface",
9773+
"description": "This endpoint requires admin access",
9774+
"tags": [
9775+
"settings"
9776+
],
9777+
"security": [
9778+
{
9779+
"bearer_auth": []
9780+
},
9781+
{
9782+
"basic_auth": []
9783+
}
9784+
],
9785+
"requestBody": {
9786+
"required": true,
9787+
"content": {
9788+
"application/json": {
9789+
"schema": {
9790+
"type": "object",
9791+
"required": [
9792+
"path"
9793+
],
9794+
"properties": {
9795+
"path": {
9796+
"type": "string",
9797+
"description": "Path of the file"
9798+
}
9799+
}
9800+
}
9801+
}
9802+
}
9803+
},
9804+
"parameters": [
9805+
{
9806+
"name": "apiVersion",
9807+
"in": "path",
9808+
"required": true,
9809+
"schema": {
9810+
"type": "string",
9811+
"enum": [
9812+
"v2"
9813+
],
9814+
"default": "v2"
9815+
}
9816+
},
9817+
{
9818+
"name": "OCS-APIRequest",
9819+
"in": "header",
9820+
"description": "Required to be true for the API request to pass",
9821+
"required": true,
9822+
"schema": {
9823+
"type": "boolean",
9824+
"default": true
9825+
}
9826+
}
9827+
],
9828+
"responses": {
9829+
"200": {
9830+
"description": "OK",
9831+
"content": {
9832+
"application/json": {
9833+
"schema": {
9834+
"type": "object",
9835+
"required": [
9836+
"ocs"
9837+
],
9838+
"properties": {
9839+
"ocs": {
9840+
"type": "object",
9841+
"required": [
9842+
"meta",
9843+
"data"
9844+
],
9845+
"properties": {
9846+
"meta": {
9847+
"$ref": "#/components/schemas/OCSMeta"
9848+
},
9849+
"data": {
9850+
"type": "object"
9851+
}
9852+
}
9853+
}
9854+
}
9855+
}
9856+
}
9857+
}
9858+
},
9859+
"401": {
9860+
"description": "Current user is not logged in",
9861+
"content": {
9862+
"application/json": {
9863+
"schema": {
9864+
"type": "object",
9865+
"required": [
9866+
"ocs"
9867+
],
9868+
"properties": {
9869+
"ocs": {
9870+
"type": "object",
9871+
"required": [
9872+
"meta",
9873+
"data"
9874+
],
9875+
"properties": {
9876+
"meta": {
9877+
"$ref": "#/components/schemas/OCSMeta"
9878+
},
9879+
"data": {}
9880+
}
9881+
}
9882+
}
9883+
}
9884+
}
9885+
}
9886+
},
9887+
"403": {
9888+
"description": "Logged in account must be an admin",
9889+
"content": {
9890+
"application/json": {
9891+
"schema": {
9892+
"type": "object",
9893+
"required": [
9894+
"ocs"
9895+
],
9896+
"properties": {
9897+
"ocs": {
9898+
"type": "object",
9899+
"required": [
9900+
"meta",
9901+
"data"
9902+
],
9903+
"properties": {
9904+
"meta": {
9905+
"$ref": "#/components/schemas/OCSMeta"
9906+
},
9907+
"data": {}
9908+
}
9909+
}
9910+
}
9911+
}
9912+
}
9913+
}
9914+
}
9915+
}
9916+
}
9917+
},
97699918
"/ocs/v2.php/apps/notifications/optional-parameters": {
97709919
"post": {
97719920
"operationId": "admin_settings-optional-parameters",

0 commit comments

Comments
 (0)