From 841aed3edd78f8f04f9bce4f5b3c8d1d86e68058 Mon Sep 17 00:00:00 2001 From: Christian Hartmann Date: Mon, 28 Oct 2024 16:33:26 +0100 Subject: [PATCH] fix Signed-off-by: Christian Hartmann --- lib/Controller/ApiController.php | 25 +-- lib/Controller/ShareApiController.php | 16 +- lib/ResponseDefinitions.php | 89 +++++++---- openapi.json | 220 ++++++++++++++++++++------ 4 files changed, 249 insertions(+), 101 deletions(-) diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 2110c671f..181fc75f0 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -75,10 +75,11 @@ use Psr\Log\LoggerInterface; /** - * @psalm-import-type FormsPartialForm from ResponseDefinitions * @psalm-import-type FormsForm from ResponseDefinitions - * @psalm-import-type FormsQuestion from ResponseDefinitions * @psalm-import-type FormsOption from ResponseDefinitions + * @psalm-import-type FormsOrder from ResponseDefinitions + * @psalm-import-type FormsPartialForm from ResponseDefinitions + * @psalm-import-type FormsQuestion from ResponseDefinitions * @psalm-import-type FormsSubmissions from ResponseDefinitions * @psalm-import-type FormsUploadedFile from ResponseDefinitions */ @@ -512,7 +513,7 @@ public function newQuestion(int $formId, ?string $type = null, string $text = '' $question = $this->questionMapper->insert($question); - $response = $question->read(); + $response = $this->formsService->getQuestion($question->getId()); $response['options'] = []; $response['accept'] = []; } else { @@ -703,11 +704,11 @@ public function deleteQuestion(int $formId, int $questionId): DataResponse { * Updates the Order of all Questions of a Form * * @param int $formId Id of the form to reorder - * @param array $newOrder Array of Question-Ids in new order. - * @return DataResponse, array{}> + * @param list $newOrder Array of Question-Ids in new order. + * @return DataResponse, array{}> * @throws OCSBadRequestException The given array contains duplicates * @throws OCSBadRequestException The length of the given array does not match the number of stored questions - * @throws OCSBadRequestException Question doesn\'t belong to given Form + * @throws OCSBadRequestException Question doesn't belong to given Form * @throws OCSBadRequestException One question has already been marked as deleted * @throws OCSForbiddenException This form is archived and can not be modified * @throws OCSForbiddenException User has no permissions to get this form @@ -786,7 +787,7 @@ public function reorderQuestions(int $formId, array $newOrder): DataResponse { foreach ($questions as $question) { $this->questionMapper->update($question); - $response[$question->getId()] = [ + $response[(string)$question->getId()] = [ 'order' => $question->getOrder() ]; } @@ -881,7 +882,7 @@ public function newOption(int $formId, int $questionId, array $optionTexts): Dat * @param int $formId id of form * @param int $questionId id of question * @param int $optionId id of option to update - * @param array{key: string, value: mixed} $keyValuePairs Array of key=>value pairs to update. + * @param array $keyValuePairs Array of key=>value pairs to update. * @return DataResponse Returns the id of the updated option * @throws OCSBadRequestException The given option id doesn't match the question or form * @throws OCSForbiddenException This form is archived and can not be modified @@ -1008,8 +1009,8 @@ public function deleteOption(int $formId, int $questionId, int $optionId): DataR * Reorder options for a given question * @param int $formId id of form * @param int $questionId id of question - * @param array $newOrder Array of option ids in new order. - * @return DataResponse, array{}> + * @param list $newOrder Array of option ids in new order. + * @return DataResponse, array{}> * @throws OCSBadRequestException The given question id doesn't match the form * @throws OCSBadRequestException The given array contains duplicates * @throws OCSBadRequestException The length of the given array does not match the number of stored options @@ -1112,7 +1113,7 @@ public function reorderOptions(int $formId, int $questionId, array $newOrder) { * - `csv`: Comma-separated value * - `ods`: OpenDocument Spreadsheet * - `xlsx`: Excel Open XML Spreadsheet - * @return DataResponse|DataDownloadResponse + * @return DataResponse, array{}>|DataDownloadResponse * @throws OCSNotFoundException Could not find form * @throws OCSForbiddenException The current user has no permission to get the results for this form * @@ -1396,7 +1397,7 @@ public function exportSubmissionsToCloud(int $formId, string $path, string $file * @param int $formId id of the form * @param int $questionId id of the question * @param string $shareHash hash of the form share - * @return DataResponse + * @return DataResponse, array{}> * @throws OCSBadRequestException No files provided * @throws OCSBadRequestException Question doesn't belong to the given form * @throws OCSBadRequestException Invalid file provided diff --git a/lib/Controller/ShareApiController.php b/lib/Controller/ShareApiController.php index 1249eeacf..66aa06cc5 100644 --- a/lib/Controller/ShareApiController.php +++ b/lib/Controller/ShareApiController.php @@ -90,12 +90,12 @@ public function __construct( * @param int $formId The form to share * @param int $shareType Nextcloud-ShareType * @param string $shareWith ID of user/group/... to share with. For Empty shareWith and shareType Link, this will be set as RandomID. - * @param array $permissions the permissions granted on the share, defaults to `submit` - * Possible values: - * - `submit` user can submit - * - `results` user can see the results - * - `results_delete` user can see and delete results - * @return DataResponse + * @param list $permissions the permissions granted on the share, defaults to `submit` + * Possible values: + * - `submit` user can submit + * - `results` user can see the results + * - `results_delete` user can see and delete results + * @return DataResponse, array{}> * @throws OCSBadRequestException Invalid shareType * @throws OCSBadRequestException Invalid permission given * @throws OCSBadRequestException Invalid user to share with @@ -231,7 +231,7 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar * * @param int $formId of the form * @param int $shareId of the share to update - * @param array{key: string, values: mixed} $keyValuePairs Array of key=>value pairs to update. + * @param array $keyValuePairs Array of key=>value pairs to update. * @return DataResponse * @throws OCSBadRequestException Share doesn't belong to given Form * @throws OCSBadRequestException Invalid permission given @@ -277,7 +277,7 @@ public function updateShare(int $formId, int $shareId, array $keyValuePairs): Da } //Don't allow to change other properties than permissions - if (count($keyValuePairs) > 1 || !key_exists('permissions', $keyValuePairs)) { + if (count($keyValuePairs) > 1 || !array_key_exists('permissions', $keyValuePairs)) { $this->logger->debug('Not allowed to update other properties than permissions'); throw new OCSForbiddenException('Not allowed to update other properties than permissions'); } diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index c77acd061..6a78436bd 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -24,16 +24,6 @@ namespace OCA\Forms; /** - * @psalm-type FormsPartialForm = array{ - * id: int, - * hash: string, - * title: string, - * expires: int, - * permissions: string[], - * partial: bool, - * state: int - * } - * * @psalm-type FormsOption = array{ * id: int, * questionId: int, @@ -41,17 +31,35 @@ * order: ?int * } * + * @psalm-type FormsOrder = array{ + * order: int + * } + * + * @psalm-type FormsQuestionExtraSettings = array{ + * allowOtherAnswer?: ?bool, + * allowedFileExtensions?: ?list, + * allowedFileTypes?: ?list, + * maxAllowedFilesCount?: ?int, + * maxFileSize?: ?int, + * optionsLimitMax?: ?int, + * optionsLimitMin?: ?int, + * shuffleOptions?: ?bool, + * validationRegex?: ?string, + * validationType?: ?string + * } + * * @psalm-type FormsQuestion = array{ * id: int, * formId: int, * order: int, - * type: string, + * type: "dropdown"|"multiple"|"multiple_unique"|"date"|"time"|"short"|"long"|"file", * isRequired: bool, * text: string, * name: string, - * options: array, - * accept: string[], - * extraSettings: \stdClass + * description: string, + * extraSettings: FormsQuestionExtraSettings|array{}, + * options: list|array{}, + * accept: list|array{} * } * * @psalm-type FormsAnswer = array{ @@ -66,13 +74,39 @@ * formId: int, * userId: string, * timestamp: int, - * answers: array, + * answers: list, * userDisplayName: string * } * * @psalm-type FormsSubmissions = array{ - * submissions: array, - * questions: array + * submissions: list, + * questions: list + * } + * + * @psalm-type FormsAccess = array{ + * permitAllUsers: bool, + * showToAllUsers: bool + * } + * + * @psalm-type FormsPermission = "edit"|"results"|"results_delete"|"submit"|"embed" + * + * @psalm-type FormsShare = array{ + * id: int, + * formId: int, + * shareType: int, + * shareWith: string, + * permissions: list, + * displayName: string + * } + * + * @psalm-type FormsPartialForm = array{ + * id: int, + * hash: string, + * title: string, + * expires: int, + * permissions: list, + * partial: true, + * state: int * } * * @psalm-type FormsForm = array{ @@ -82,32 +116,23 @@ * description: string, * ownerId: string, * created: int, - * access: \stdClass, + * access: FormsAccess, * expires: int, * isAnonymous: bool, * submitMultiple: bool, * showExpiration: bool, * canSubmit: bool, - * permissions: string[], - * questions: array, - * state: int, - * shares: string[], - * submissions: array, + * permissions: list, + * questions: list, + * state: 0|1|2, + * shares: list, + * submissions: list, * } * * @psalm-type FormsUploadedFile = array{ * uploadedFileId: int, * fileName: string * } - * - * @psalm-type FormsShare = array{ - * id: int, - * formId: int, - * shareType: int, - * shareWith: string, - * permissions: string[], - * displayName: string - * } */ class ResponseDefinitions { } diff --git a/openapi.json b/openapi.json index 5e94ba642..b63eec6e9 100644 --- a/openapi.json +++ b/openapi.json @@ -20,6 +20,21 @@ } }, "schemas": { + "Access": { + "type": "object", + "required": [ + "permitAllUsers", + "showToAllUsers" + ], + "properties": { + "permitAllUsers": { + "type": "boolean" + }, + "showToAllUsers": { + "type": "boolean" + } + } + }, "Answer": { "type": "object", "required": [ @@ -115,8 +130,7 @@ "format": "int64" }, "access": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/Access" }, "expires": { "type": "integer", @@ -137,7 +151,7 @@ "permissions": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Permission" } }, "questions": { @@ -148,7 +162,12 @@ }, "state": { "type": "integer", - "format": "int64" + "format": "int64", + "enum": [ + 0, + 1, + 2 + ] }, "shares": { "type": "array", @@ -215,6 +234,18 @@ } } }, + "Order": { + "type": "object", + "required": [ + "order" + ], + "properties": { + "order": { + "type": "integer", + "format": "int64" + } + } + }, "PartialForm": { "type": "object", "required": [ @@ -244,11 +275,14 @@ "permissions": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Permission" } }, "partial": { - "type": "boolean" + "type": "boolean", + "enum": [ + true + ] }, "state": { "type": "integer", @@ -256,6 +290,16 @@ } } }, + "Permission": { + "type": "string", + "enum": [ + "edit", + "results", + "results_delete", + "submit", + "embed" + ] + }, "Question": { "type": "object", "required": [ @@ -266,9 +310,10 @@ "isRequired", "text", "name", + "description", + "extraSettings", "options", - "accept", - "extraSettings" + "accept" ], "properties": { "id": { @@ -284,7 +329,17 @@ "format": "int64" }, "type": { - "type": "string" + "type": "string", + "enum": [ + "dropdown", + "multiple", + "multiple_unique", + "date", + "time", + "short", + "long", + "file" + ] }, "isRequired": { "type": "boolean" @@ -295,21 +350,99 @@ "name": { "type": "string" }, + "description": { + "type": "string" + }, + "extraSettings": { + "anyOf": [ + { + "$ref": "#/components/schemas/QuestionExtraSettings" + }, + { + "type": "object" + } + ] + }, "options": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/components/schemas/Option" + } + }, + { + "type": "object" + } + ] + }, + "accept": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object" + } + ] + } + } + }, + "QuestionExtraSettings": { + "type": "object", + "properties": { + "allowOtherAnswer": { + "type": "boolean", + "nullable": true + }, + "allowedFileExtensions": { "type": "array", + "nullable": true, "items": { - "$ref": "#/components/schemas/Option" + "type": "string" } }, - "accept": { + "allowedFileTypes": { "type": "array", + "nullable": true, "items": { "type": "string" } }, - "extraSettings": { - "type": "object", - "additionalProperties": true + "maxAllowedFilesCount": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "maxFileSize": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "optionsLimitMax": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "optionsLimitMin": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "shuffleOptions": { + "type": "boolean", + "nullable": true + }, + "validationRegex": { + "type": "string", + "nullable": true + }, + "validationType": { + "type": "string", + "nullable": true } } }, @@ -342,7 +475,7 @@ "permissions": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Permission" } }, "displayName": { @@ -1382,9 +1515,9 @@ ], "properties": { "newOrder": { - "type": "object", + "type": "array", "description": "Array of Question-Ids in new order.", - "additionalProperties": { + "items": { "type": "integer", "format": "int64" } @@ -1440,8 +1573,7 @@ "data": { "type": "object", "additionalProperties": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/Order" } } } @@ -2252,9 +2384,9 @@ ], "properties": { "newOrder": { - "type": "object", + "type": "array", "description": "Array of option ids in new order.", - "additionalProperties": { + "items": { "type": "integer", "format": "int64" } @@ -2320,8 +2452,7 @@ "data": { "type": "object", "additionalProperties": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/Order" } } } @@ -2443,17 +2574,8 @@ "keyValuePairs": { "type": "object", "description": "Array of key=>value pairs to update.", - "required": [ - "key", - "value" - ], - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "object" - } + "additionalProperties": { + "type": "object" } } } @@ -2858,7 +2980,10 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "$ref": "#/components/schemas/Submissions" + "type": "array", + "items": { + "$ref": "#/components/schemas/Submissions" + } } } } @@ -3643,7 +3768,10 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "$ref": "#/components/schemas/UploadedFile" + "type": "array", + "items": { + "$ref": "#/components/schemas/UploadedFile" + } } } } @@ -3774,7 +3902,7 @@ "permissions": { "type": "array", "default": [], - "description": "the permissions granted on the share, defaults to `submit`\n Possible values:\n - `submit` user can submit\n - `results` user can see the results\n - `results_delete` user can see and delete results", + "description": "the permissions granted on the share, defaults to `submit`\n Possible values:\n - `submit` user can submit\n - `results` user can see the results\n - `results_delete` user can see and delete results", "items": { "type": "string" } @@ -3828,7 +3956,10 @@ "$ref": "#/components/schemas/OCSMeta" }, "data": { - "$ref": "#/components/schemas/Share" + "type": "array", + "items": { + "$ref": "#/components/schemas/Share" + } } } } @@ -3949,17 +4080,8 @@ "keyValuePairs": { "type": "object", "description": "Array of key=>value pairs to update.", - "required": [ - "key", - "values" - ], - "properties": { - "key": { - "type": "string" - }, - "values": { - "type": "object" - } + "additionalProperties": { + "type": "object" } } }