Skip to content

Commit

Permalink
Adjust code to API v3 and use reorder logic from Questions
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Hartmann <[email protected]>
  • Loading branch information
Chartman123 committed Sep 27, 2024
1 parent e9eb867 commit 2ce5993
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 362 deletions.
385 changes: 118 additions & 267 deletions appinfo/routes.php

Large diffs are not rendered by default.

29 changes: 12 additions & 17 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ This file contains the API-Documentation. For more information on the returned D

- In API version 2.5 the following endpoints were introduced:
- `POST /api/2.5/uploadFiles/{formId}/{questionId}` to upload files to answer before form submitting
- In API version 2.5 the following change was made:
- Options now contain a property `order`
- In API version 2.4 the following endpoints were introduced:
- `POST /api/2.4/form/link/{fileFormat}` to link form to a file
- `POST /api/2.4/form/unlink` to unlink form from a file
Expand Down Expand Up @@ -196,12 +198,14 @@ Returns the full-depth object of the requested form (without submissions).
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 2,
"questionId": 1,
"text": "Option 2"
"text": "Option 2",
"order": null
}
],
"accept": [],
Expand Down Expand Up @@ -489,18 +493,6 @@ Update a single or all properties of an option-object
```
"data": 7
```
### Reorder options
- Endpoint: `/api/v2.5/question/{id}/options`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
| _id_ | Integer | ID of the question to reorder options for |
- Parameters:
| Parameter | Type | Description |
|-----------|---------|-------------|
| _order_ | Array | Ordered array of option IDs, the options will be reordered according to their position in this array |
- Method: `PATCH`
- Response: **Status-Code OK**.

### Delete an option

Expand Down Expand Up @@ -647,17 +639,20 @@ Get all Submissions to a Form
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 27,
"questionId": 1,
"text": "Option 2"
"text": "Option 2",
"order": null
},
{
"id": 30,
"questionId": 1,
"text": "Option 3"
"text": "Option 3",
"order": null
}
],
"extraSettings": {}
Expand Down
42 changes: 34 additions & 8 deletions docs/API_v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ This file contains the API-Documentation. For more information on the returned D
- `GET /api/v3/forms/{formId}/questions` to get all questions of a form
- `GET /api/v3/forms/{formId}/questions/{questionId}` to get a single question
- `POST /api/v3/forms/{formId}/questions/{questionId}/options` does now accept more options at once
- `PATCH /api/v3/forms/{formId}/questions/{questionId}/options/reorder` to reorder the options
- `POST /api/v3/forms/{formId}/submissions/files/{questionId}` to upload a file to a file question before submitting the form
- In API version 2.5 the following endpoints were introduced:
- `POST /api/v2.5/uploadFiles/{formId}/{questionId}` to upload files to answer before form submitting
Expand Down Expand Up @@ -178,12 +179,14 @@ Returns the full-depth object of the requested form (without submissions).
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 2,
"questionId": 1,
"text": "Option 2"
"text": "Option 2",
"order": null
}
],
"accept": [],
Expand Down Expand Up @@ -304,12 +307,14 @@ Returns the questions and options of the given form (without submissions).
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 2,
"questionId": 1,
"text": "Option 2"
"text": "Option 2",
"order": null
}
],
"accept": [],
Expand Down Expand Up @@ -385,12 +390,14 @@ Returns the requested question and options of the given form (without submission
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 2,
"questionId": 1,
"text": "Option 2"
"order": null
}
],
"accept": [],
Expand Down Expand Up @@ -549,6 +556,22 @@ Update a single or all properties of an option-object
"data": 7
```

### Reorder options

- Endpoint: `/api/v3/forms/{formId}/questions/{questionId}/options/reorder`
- Method: `PATCH`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
| _formId_ | Integer | ID of the form containing the question and option |
| _questionId_ | Integer | ID of the question, the new option will belong to |
- Parameters:
| Parameter | Type | Description |
|-----------|---------|-------------|
| _newOrder_ | Array | Array of **all** option IDs, ordered in the desired order |
- Restrictions: The Array **must** contain all option IDs corresponding to the specified question and **must not** contain any duplicates.
- Response: Array of optionIds and their corresponding order.

## Sharing Endpoints

### Add a new Share
Expand Down Expand Up @@ -685,17 +708,20 @@ Get all Submissions to a Form
{
"id": 1,
"questionId": 1,
"text": "Option 1"
"text": "Option 1",
"order": null
},
{
"id": 27,
"questionId": 1,
"text": "Option 2"
"text": "Option 2",
"order": null
},
{
"id": 30,
"questionId": 1,
"text": "Option 3"
"text": "Option 3",
"order": null
}
],
"extraSettings": {}
Expand Down
165 changes: 118 additions & 47 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,12 +764,22 @@ public function newOption(int $formId, int $questionId, array $optionTexts): Dat
throw new OCSBadRequestException();
}

// Retrieve all options sorted by 'order'. Takes the order of the last array-element and adds one.
$options = $this->optionMapper->findByQuestion($questionId);
$lastOption = array_pop($options);
if ($lastOption) {
$optionOrder = $lastOption->getOrder() + 1;

Check warning on line 771 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L768-L771

Added lines #L768 - L771 were not covered by tests
} else {
$optionOrder = 1;

Check warning on line 773 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L773

Added line #L773 was not covered by tests
}

$addedOptions = [];
foreach ($optionTexts as $text) {
$option = new Option();

$option->setQuestionId($questionId);
$option->setText($text);
$option->setOrder($optionOrder++);

Check warning on line 782 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L782

Added line #L782 was not covered by tests

try {
$option = $this->optionMapper->insert($option);
Expand Down Expand Up @@ -889,11 +899,104 @@ public function deleteOption(int $formId, int $questionId, int $optionId): DataR
}

$this->optionMapper->delete($option);

// Reorder the remaining options
$options = array_values($this->optionMapper->findByQuestion($questionId));
foreach ($options as $order => $option) {

Check warning on line 905 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L904-L905

Added lines #L904 - L905 were not covered by tests
// Always start order with 1
$option->setOrder($order + 1);
$this->optionMapper->update($option);

Check warning on line 908 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L907-L908

Added lines #L907 - L908 were not covered by tests
}

$this->formMapper->update($form);

return new DataResponse($optionId);
}

/**
* Reorder options for a given question
* @param int $formId id of form
* @param int $questionId id of question
* @param Array<int, int> $newOrder Order to use
*/
public function reorderOptions(int $formId, int $questionId, array $newOrder) {
$form = $this->getFormIfAllowed($formId);
if ($this->formsService->isFormArchived($form)) {
$this->logger->debug('This form is archived and can not be modified');
throw new OCSForbiddenException();

Check warning on line 926 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L922-L926

Added lines #L922 - L926 were not covered by tests
}

try {
$question = $this->questionMapper->findById($questionId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find form or question', ['exception' => $e]);
throw new OCSNotFoundException('Could not find form or question');

Check warning on line 933 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L930-L933

Added lines #L930 - L933 were not covered by tests
}

if ($question->getFormId() !== $formId) {
$this->logger->debug('The given question id doesn\'t match the form.');
throw new OCSBadRequestException();

Check warning on line 938 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L936-L938

Added lines #L936 - L938 were not covered by tests
}

// Check if array contains duplicates
if (array_unique($newOrder) !== $newOrder) {
$this->logger->debug('The given array contains duplicates');
throw new OCSBadRequestException('The given array contains duplicates');

Check warning on line 944 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L942-L944

Added lines #L942 - L944 were not covered by tests
}

$options = $this->optionMapper->findByQuestion($questionId);

Check warning on line 947 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L947

Added line #L947 was not covered by tests

if (sizeof($options) !== sizeof($newOrder)) {
$this->logger->debug('The length of the given array does not match the number of stored options');
throw new OCSBadRequestException('The length of the given array does not match the number of stored options');

Check warning on line 951 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L949-L951

Added lines #L949 - L951 were not covered by tests
}

$options = []; // Clear Array of Entities
$response = []; // Array of ['optionId' => ['order' => newOrder]]

Check warning on line 955 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L954-L955

Added lines #L954 - L955 were not covered by tests

// Store array of Option entities and check the Options questionId & old order.
foreach ($newOrder as $arrayKey => $optionId) {

Check warning on line 958 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L958

Added line #L958 was not covered by tests
try {
$options[$arrayKey] = $this->optionMapper->findById($optionId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find option. Id: {optionId}', [
'optionId' => $optionId
]);
throw new OCSBadRequestException();

Check warning on line 965 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L960-L965

Added lines #L960 - L965 were not covered by tests
}

// Abort if a question is not part of the Form.
if ($options[$arrayKey]->getQuestionId() !== $questionId) {
$this->logger->debug('This Option is not part of the given Question: formId: {formId}', [
'formId' => $formId
]);
throw new OCSBadRequestException();

Check warning on line 973 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L969-L973

Added lines #L969 - L973 were not covered by tests
}

// Abort if a question is already marked as deleted (order==0)
$oldOrder = $options[$arrayKey]->getOrder();

Check warning on line 977 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L977

Added line #L977 was not covered by tests

// Only set order, if it changed.
if ($oldOrder !== $arrayKey + 1) {

Check warning on line 980 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L980

Added line #L980 was not covered by tests
// Set Order. ArrayKey counts from zero, order counts from 1.
$options[$arrayKey]->setOrder($arrayKey + 1);

Check warning on line 982 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L982

Added line #L982 was not covered by tests
}
}

// Write to Database
foreach ($options as $option) {
$this->optionMapper->update($option);

Check warning on line 988 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L987-L988

Added lines #L987 - L988 were not covered by tests

$response[$option->getId()] = [
'order' => $option->getOrder()
];

Check warning on line 992 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L990-L992

Added lines #L990 - L992 were not covered by tests
}

$this->formMapper->update($form);

Check warning on line 995 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L995

Added line #L995 was not covered by tests

return new DataResponse($response);

Check warning on line 997 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L997

Added line #L997 was not covered by tests
}

// Submissions

/**
Expand Down Expand Up @@ -1940,7 +2043,7 @@ public function cloneQuestionLegacy(int $id): DataResponse {
* @throws OCSBadRequestException
* @throws OCSForbiddenException
*/
public function newOptionLegacy(int $questionId, string $text, int|null $order = null): DataResponse {
public function newOptionLegacy(int $questionId, string $text): DataResponse {
$this->logger->debug('Adding new option: questionId: {questionId}, text: {text}', [
'questionId' => $questionId,
'text' => $text,
Expand All @@ -1964,13 +2067,20 @@ public function newOptionLegacy(int $questionId, string $text, int|null $order =
throw new OCSForbiddenException();
}

// Retrieve all options sorted by 'order'. Takes the order of the last array-element and adds one.
$options = $this->optionMapper->findByQuestion($questionId);
$lastOption = array_pop($options);
if ($lastOption) {
$optionOrder = $lastOption->getOrder() + 1;

Check warning on line 2074 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2071-L2074

Added lines #L2071 - L2074 were not covered by tests
} else {
$optionOrder = 1;

Check warning on line 2076 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2076

Added line #L2076 was not covered by tests
}

$option = new Option();

$option->setQuestionId($questionId);
$option->setText($text);
if ($order !== null) {
$option->setOrder($order);
}
$option->setOrder($optionOrder);

Check warning on line 2083 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2083

Added line #L2083 was not covered by tests

$option = $this->optionMapper->insert($option);
$this->formMapper->update($form);
Expand Down Expand Up @@ -2076,57 +2186,18 @@ public function deleteOptionLegacy(int $id): DataResponse {
$this->optionMapper->delete($option);

// Reorder the remaining options
$options = array_values($this->optionMapper->findByQuestion($question->getId()));
$options = array_values($this->optionMapper->findByQuestion($option->getQuestionId()));
foreach ($options as $order => $option) {

Check warning on line 2190 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2189-L2190

Added lines #L2189 - L2190 were not covered by tests
$option->setOrder($order);
// Always start order with 1
$option->setOrder($order + 1);
$this->optionMapper->update($option);

Check warning on line 2193 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2192-L2193

Added lines #L2192 - L2193 were not covered by tests
}

$this->formMapper->update($form);

return new DataResponse($id);
}

/**
* Reorder options for a given question
* @param int $id The question ID
* @param int[] $order Order to use
*/
public function reorderOptions(int $id, array $order) {
try {
/** @var int[] */
$order = array_flip(array_values($order));
} catch (\Error $e) {
throw new OCSBadRequestException('Invalid order parameter');
}

try {
$question = $this->questionMapper->findById($id);
$form = $this->formMapper->findById($question->getFormId());
} catch (IMapperException $e) {
$this->logger->debug('Could not find form or question', ['exception' => $e]);
throw new OCSNotFoundException('Could not find question');
}

if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();
}

if ($this->formsService->isFormArchived($form)) {
$this->logger->debug('This form is archived and can not be modified');
throw new OCSForbiddenException();
}

$options = $this->optionMapper->findByQuestion($id);

foreach ($options as $option) {
$option->setOrder($order[$option->getId()] ?? 0);
$this->optionMapper->update($option);
}

return new DataResponse([]);
}

/**
* @CORS
* @NoAdminRequired
Expand Down
Loading

0 comments on commit 2ce5993

Please sign in to comment.