Skip to content

Commit a3d3049

Browse files
committed
Enhancement: Add validation for unique values for short text fields (code review and error reporting improvements)
Signed-off-by: Kostiantyn Miakshyn <molodchick@gmail.com>
1 parent 1f3b103 commit a3d3049

16 files changed

+325
-27
lines changed

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Share your tables and views with users and groups within your cloud.
2626
Have a good time and manage whatever you want.
2727
2828
]]></description>
29-
<version>0.9.2</version>
29+
<version>0.9.3</version>
3030
<licence>agpl</licence>
3131
<author mail="florian.steffens@nextcloud.com">Florian Steffens</author>
3232
<namespace>Tables</namespace>

cypress/e2e/ToDo list.json

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,213 @@
1-
{"title":"ToDo list","emoji":"\u2705","columns":[{"id":491,"tableId":132,"title":"Task","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"text","subtype":"line","mandatory":true,"description":"","numberDefault":null,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false,"showUserStatus":false},{"id":492,"tableId":132,"title":"Description","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"text","subtype":"rich","mandatory":false,"description":"Title or short description","numberDefault":null,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false,"showUserStatus":false},{"id":493,"tableId":132,"title":"Target","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"text","subtype":"rich","mandatory":false,"description":"Date, time or whatever","numberDefault":null,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false,"showUserStatus":false},{"id":494,"tableId":132,"title":"Progress","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"number","subtype":"progress","mandatory":false,"description":"","numberDefault":0,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false,"showUserStatus":false},{"id":495,"tableId":132,"title":"Comments","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"text","subtype":"rich","mandatory":false,"description":"","numberDefault":null,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false,"showUserStatus":false},{"id":496,"tableId":132,"title":"Proofed","createdBy":"admin","createdByDisplayName":"admin","createdAt":"2024-08-26 12:58:43","lastEditBy":"admin","lastEditByDisplayName":"admin","lastEditAt":"2024-08-26 12:58:43","type":"selection","subtype":"check","mandatory":false,"description":"","numberDefault":null,"numberMin":null,"numberMax":null,"numberDecimals":0,"numberPrefix":"","numberSuffix":"","textDefault":"","textAllowedPattern":"","textMaxLength":-1,"selectionOptions":[],"selectionDefault":"","datetimeDefault":"","usergroupDefault":[],"usergroupMultipleItems":false,"usergroupSelectUsers":false,"usergroupSelectGroups":false,"usergroupSelectTeams":false, "showUserStatus":false}],"views":[],"description":"","tablesVersion":"0.8.0-beta.2"}
1+
{
2+
"title": "ToDo list",
3+
"emoji": "\u2705",
4+
"columns": [
5+
{
6+
"id": 491,
7+
"tableId": 132,
8+
"title": "Task",
9+
"createdBy": "admin",
10+
"createdByDisplayName": "admin",
11+
"createdAt": "2024-08-26 12:58:43",
12+
"lastEditBy": "admin",
13+
"lastEditByDisplayName": "admin",
14+
"lastEditAt": "2024-08-26 12:58:43",
15+
"type": "text",
16+
"subtype": "line",
17+
"mandatory": true,
18+
"description": "",
19+
"numberDefault": null,
20+
"numberMin": null,
21+
"numberMax": null,
22+
"numberDecimals": 0,
23+
"numberPrefix": "",
24+
"numberSuffix": "",
25+
"textDefault": "",
26+
"textAllowedPattern": "",
27+
"textMaxLength": -1,
28+
"textUnique": false,
29+
"selectionOptions": [],
30+
"selectionDefault": "",
31+
"datetimeDefault": "",
32+
"usergroupDefault": [],
33+
"usergroupMultipleItems": false,
34+
"usergroupSelectUsers": false,
35+
"usergroupSelectGroups": false,
36+
"usergroupSelectTeams": false,
37+
"showUserStatus": false
38+
},
39+
{
40+
"id": 492,
41+
"tableId": 132,
42+
"title": "Description",
43+
"createdBy": "admin",
44+
"createdByDisplayName": "admin",
45+
"createdAt": "2024-08-26 12:58:43",
46+
"lastEditBy": "admin",
47+
"lastEditByDisplayName": "admin",
48+
"lastEditAt": "2024-08-26 12:58:43",
49+
"type": "text",
50+
"subtype": "rich",
51+
"mandatory": false,
52+
"description": "Title or short description",
53+
"numberDefault": null,
54+
"numberMin": null,
55+
"numberMax": null,
56+
"numberDecimals": 0,
57+
"numberPrefix": "",
58+
"numberSuffix": "",
59+
"textDefault": "",
60+
"textAllowedPattern": "",
61+
"textMaxLength": -1,
62+
"textUnique": false,
63+
"selectionOptions": [],
64+
"selectionDefault": "",
65+
"datetimeDefault": "",
66+
"usergroupDefault": [],
67+
"usergroupMultipleItems": false,
68+
"usergroupSelectUsers": false,
69+
"usergroupSelectGroups": false,
70+
"usergroupSelectTeams": false,
71+
"showUserStatus": false
72+
},
73+
{
74+
"id": 493,
75+
"tableId": 132,
76+
"title": "Target",
77+
"createdBy": "admin",
78+
"createdByDisplayName": "admin",
79+
"createdAt": "2024-08-26 12:58:43",
80+
"lastEditBy": "admin",
81+
"lastEditByDisplayName": "admin",
82+
"lastEditAt": "2024-08-26 12:58:43",
83+
"type": "text",
84+
"subtype": "rich",
85+
"mandatory": false,
86+
"description": "Date, time or whatever",
87+
"numberDefault": null,
88+
"numberMin": null,
89+
"numberMax": null,
90+
"numberDecimals": 0,
91+
"numberPrefix": "",
92+
"numberSuffix": "",
93+
"textDefault": "",
94+
"textAllowedPattern": "",
95+
"textMaxLength": -1,
96+
"textUnique": false,
97+
"selectionOptions": [],
98+
"selectionDefault": "",
99+
"datetimeDefault": "",
100+
"usergroupDefault": [],
101+
"usergroupMultipleItems": false,
102+
"usergroupSelectUsers": false,
103+
"usergroupSelectGroups": false,
104+
"usergroupSelectTeams": false,
105+
"showUserStatus": false
106+
},
107+
{
108+
"id": 494,
109+
"tableId": 132,
110+
"title": "Progress",
111+
"createdBy": "admin",
112+
"createdByDisplayName": "admin",
113+
"createdAt": "2024-08-26 12:58:43",
114+
"lastEditBy": "admin",
115+
"lastEditByDisplayName": "admin",
116+
"lastEditAt": "2024-08-26 12:58:43",
117+
"type": "number",
118+
"subtype": "progress",
119+
"mandatory": false,
120+
"description": "",
121+
"numberDefault": 0,
122+
"numberMin": null,
123+
"numberMax": null,
124+
"numberDecimals": 0,
125+
"numberPrefix": "",
126+
"numberSuffix": "",
127+
"textDefault": "",
128+
"textAllowedPattern": "",
129+
"textMaxLength": -1,
130+
"textUnique": false,
131+
"selectionOptions": [],
132+
"selectionDefault": "",
133+
"datetimeDefault": "",
134+
"usergroupDefault": [],
135+
"usergroupMultipleItems": false,
136+
"usergroupSelectUsers": false,
137+
"usergroupSelectGroups": false,
138+
"usergroupSelectTeams": false,
139+
"showUserStatus": false
140+
},
141+
{
142+
"id": 495,
143+
"tableId": 132,
144+
"title": "Comments",
145+
"createdBy": "admin",
146+
"createdByDisplayName": "admin",
147+
"createdAt": "2024-08-26 12:58:43",
148+
"lastEditBy": "admin",
149+
"lastEditByDisplayName": "admin",
150+
"lastEditAt": "2024-08-26 12:58:43",
151+
"type": "text",
152+
"subtype": "rich",
153+
"mandatory": false,
154+
"description": "",
155+
"numberDefault": null,
156+
"numberMin": null,
157+
"numberMax": null,
158+
"numberDecimals": 0,
159+
"numberPrefix": "",
160+
"numberSuffix": "",
161+
"textDefault": "",
162+
"textAllowedPattern": "",
163+
"textMaxLength": -1,
164+
"textUnique": false,
165+
"selectionOptions": [],
166+
"selectionDefault": "",
167+
"datetimeDefault": "",
168+
"usergroupDefault": [],
169+
"usergroupMultipleItems": false,
170+
"usergroupSelectUsers": false,
171+
"usergroupSelectGroups": false,
172+
"usergroupSelectTeams": false,
173+
"showUserStatus": false
174+
},
175+
{
176+
"id": 496,
177+
"tableId": 132,
178+
"title": "Proofed",
179+
"createdBy": "admin",
180+
"createdByDisplayName": "admin",
181+
"createdAt": "2024-08-26 12:58:43",
182+
"lastEditBy": "admin",
183+
"lastEditByDisplayName": "admin",
184+
"lastEditAt": "2024-08-26 12:58:43",
185+
"type": "selection",
186+
"subtype": "check",
187+
"mandatory": false,
188+
"description": "",
189+
"numberDefault": null,
190+
"numberMin": null,
191+
"numberMax": null,
192+
"numberDecimals": 0,
193+
"numberPrefix": "",
194+
"numberSuffix": "",
195+
"textDefault": "",
196+
"textAllowedPattern": "",
197+
"textMaxLength": -1,
198+
"textUnique": false,
199+
"selectionOptions": [],
200+
"selectionDefault": "",
201+
"datetimeDefault": "",
202+
"usergroupDefault": [],
203+
"usergroupMultipleItems": false,
204+
"usergroupSelectUsers": false,
205+
"usergroupSelectGroups": false,
206+
"usergroupSelectTeams": false,
207+
"showUserStatus": false
208+
}
209+
],
210+
"views": [],
211+
"description": "",
212+
"tablesVersion": "0.8.0-beta.2"
213+
}

cypress/fixtures/widgets/richObject.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"textDefault": "",
3434
"textAllowedPattern": "",
3535
"textMaxLength": -1,
36+
"textUnique": false,
3637
"selectionOptions": [],
3738
"selectionDefault": "",
3839
"datetimeDefault": ""
@@ -60,6 +61,7 @@
6061
"textDefault": "",
6162
"textAllowedPattern": "",
6263
"textMaxLength": -1,
64+
"textUnique": false,
6365
"selectionOptions": [],
6466
"selectionDefault": "",
6567
"datetimeDefault": ""
@@ -87,6 +89,7 @@
8789
"textDefault": "",
8890
"textAllowedPattern": "",
8991
"textMaxLength": -1,
92+
"textUnique": false,
9093
"selectionOptions": [],
9194
"selectionDefault": "",
9295
"datetimeDefault": ""
@@ -114,6 +117,7 @@
114117
"textDefault": "",
115118
"textAllowedPattern": "",
116119
"textMaxLength": -1,
120+
"textUnique": false,
117121
"selectionOptions": [],
118122
"selectionDefault": "",
119123
"datetimeDefault": "today"
@@ -141,6 +145,7 @@
141145
"textDefault": "",
142146
"textAllowedPattern": "",
143147
"textMaxLength": -1,
148+
"textUnique": false,
144149
"selectionOptions": [],
145150
"selectionDefault": "",
146151
"datetimeDefault": ""
@@ -168,6 +173,7 @@
168173
"textDefault": "",
169174
"textAllowedPattern": "",
170175
"textMaxLength": -1,
176+
"textUnique": false,
171177
"selectionOptions": [],
172178
"selectionDefault": "",
173179
"datetimeDefault": ""
@@ -195,6 +201,7 @@
195201
"textDefault": "",
196202
"textAllowedPattern": "",
197203
"textMaxLength": -1,
204+
"textUnique": false,
198205
"selectionOptions": [],
199206
"selectionDefault": "",
200207
"datetimeDefault": ""
@@ -222,6 +229,7 @@
222229
"textDefault": "",
223230
"textAllowedPattern": "",
224231
"textMaxLength": -1,
232+
"textUnique": false,
225233
"selectionOptions": [],
226234
"selectionDefault": "",
227235
"datetimeDefault": ""
@@ -249,6 +257,7 @@
249257
"textDefault": "",
250258
"textAllowedPattern": "",
251259
"textMaxLength": -1,
260+
"textUnique": false,
252261
"selectionOptions": [],
253262
"selectionDefault": "",
254263
"datetimeDefault": ""

lib/Controller/AOCSController.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ protected function handleNotFoundError(NotFoundError $e): DataResponse {
7070
*/
7171
protected function handleBadRequestError(BadRequestError $e): DataResponse {
7272
$this->logger->warning('An bad request was encountered: ' . $e->getMessage(), ['exception' => $e]);
73-
return new DataResponse(['message' => $this->n->t('An error caused by an invalid request occurred. More details can be found in the logs. Please reach out to your administration.')], Http::STATUS_BAD_REQUEST);
73+
return new DataResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
7474
}
75-
7675
}

lib/Controller/Api1Controller.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OCA\Tables\AppInfo\Application;
1616
use OCA\Tables\Db\ViewMapper;
1717
use OCA\Tables\Dto\Column as ColumnDto;
18+
use OCA\Tables\Errors\BadRequestError;
1819
use OCA\Tables\Errors\InternalError;
1920
use OCA\Tables\Errors\NotFoundError;
2021
use OCA\Tables\Errors\PermissionError;
@@ -1171,7 +1172,7 @@ public function createRowInView(int $viewId, $data): DataResponse {
11711172
$this->logger->warning('A permission error occurred: ' . $e->getMessage(), ['exception' => $e]);
11721173
$message = ['message' => $e->getMessage()];
11731174
return new DataResponse($message, Http::STATUS_FORBIDDEN);
1174-
} catch (InternalError|Exception $e) {
1175+
} catch (BadRequestError|InternalError|Exception $e) {
11751176
$this->logger->error('An internal error or exception occurred: ' . $e->getMessage(), ['exception' => $e]);
11761177
$message = ['message' => $e->getMessage()];
11771178
return new DataResponse($message, Http::STATUS_INTERNAL_SERVER_ERROR);
@@ -1217,7 +1218,7 @@ public function createRowInTable(int $tableId, $data): DataResponse {
12171218
$this->logger->warning('A permission error occurred: ' . $e->getMessage(), ['exception' => $e]);
12181219
$message = ['message' => $e->getMessage()];
12191220
return new DataResponse($message, Http::STATUS_FORBIDDEN);
1220-
} catch (InternalError|Exception $e) {
1221+
} catch (BadRequestError|InternalError|Exception $e) {
12211222
$this->logger->error('An internal error or exception occurred: ' . $e->getMessage(), ['exception' => $e]);
12221223
$message = ['message' => $e->getMessage()];
12231224
return new DataResponse($message, Http::STATUS_INTERNAL_SERVER_ERROR);

lib/Controller/ContextController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public function create(string $name, string $iconName, string $description = '',
115115
} catch (PermissionError $e) {
116116
return $this->handlePermissionError($e);
117117
} catch (InvalidArgumentException $e) {
118-
return $this->handleBadRequestError(new BadRequestError($e->getMessage(), $e->getCode(), $e));
118+
return $this->handleError(new InternalError($e->getMessage(), $e->getCode(), $e));
119119
}
120120
}
121121

lib/Controller/RowOCSController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public function createRow(string $nodeCollection, int $nodeId, mixed $data): Dat
7878

7979
try {
8080
return new DataResponse($this->rowService->create($tableId, $viewId, $newRowData)->jsonSerialize());
81+
} catch (BadRequestError $e) {
82+
return $this->handleBadRequestError($e);
8183
} catch (NotFoundError $e) {
8284
return $this->handleNotFoundError($e);
8385
} catch (PermissionError $e) {

lib/Service/ColumnTypes/IColumnTypeBusiness.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace OCA\Tables\Service\ColumnTypes;
99

1010
use OCA\Tables\Db\Column;
11+
use OCA\Tables\Errors\BadRequestError;
1112

1213
interface IColumnTypeBusiness {
1314

@@ -32,4 +33,13 @@ public function parseValue($value, ?Column $column): string;
3233
* @return bool
3334
*/
3435
public function canBeParsed($value, ?Column $column): bool;
36+
37+
/**
38+
* @throws BadRequestError In case the value is not valid
39+
*
40+
* @param mixed $value
41+
* @param Column $column
42+
* @param int|null $rowId
43+
*/
44+
public function validateValue(mixed $value, Column $column, string $userId, int $tableId, ?int $rowId): void;
3545
}

lib/Service/ColumnTypes/SuperBusiness.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public function canBeParsed($value, ?Column $column = null): bool {
3737
return true;
3838
}
3939

40+
public function validateValue(mixed $value, Column $column, string $userId, int $tableId, ?int $rowId): void {
41+
// override this method in the child class when needed
42+
}
43+
4044
protected function isValidDate(string $dateString, string $format): bool {
4145
try {
4246
$dateTime = new DateTime($dateString);

0 commit comments

Comments
 (0)