Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question duplication #1423

Merged
merged 2 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 38 additions & 30 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
'verb' => 'OPTIONS',
'requirements' => [
'path' => '.+',
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],

Expand All @@ -83,31 +83,31 @@
'url' => '/api/{apiVersion}/forms',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#newForm',
'url' => '/api/{apiVersion}/form',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#getForm',
'url' => '/api/{apiVersion}/form/{id}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#cloneForm',
'url' => '/api/{apiVersion}/form/clone/{id}',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
// TODO: Remove POST in next API release
Expand All @@ -116,47 +116,47 @@
'url' => '/api/{apiVersion}/form/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#updateForm',
'url' => '/api/{apiVersion}/form/update',
'verb' => 'PATCH',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],
[
'name' => 'api#transferOwner',
'url' => '/api/{apiVersion}/form/transfer',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],
[
'name' => 'api#deleteForm',
'url' => '/api/{apiVersion}/form/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#getPartialForm',
'url' => '/api/{apiVersion}/partial_form/{hash}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#getSharedForms',
'url' => '/api/{apiVersion}/shared_forms',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],

Expand All @@ -166,7 +166,7 @@
'url' => '/api/{apiVersion}/question',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
// TODO: Remove POST in next API release
Expand All @@ -175,15 +175,15 @@
'url' => '/api/{apiVersion}/question/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#updateQuestion',
'url' => '/api/{apiVersion}/question/update',
'verb' => 'PATCH',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],
// TODO: Remove POST in next API release
Expand All @@ -192,23 +192,31 @@
'url' => '/api/{apiVersion}/question/reorder',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#reorderQuestions',
'url' => '/api/{apiVersion}/question/reorder',
'verb' => 'PUT',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],
[
'name' => 'api#deleteQuestion',
'url' => '/api/{apiVersion}/question/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#cloneQuestion',
'url' => '/api/{apiVersion}/question/clone/{id}',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2.3'
]
],

Expand All @@ -218,7 +226,7 @@
'url' => '/api/{apiVersion}/option',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
// TODO: Remove POST in next API release
Expand All @@ -227,23 +235,23 @@
'url' => '/api/{apiVersion}/option/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#updateOption',
'url' => '/api/{apiVersion}/option/update',
'verb' => 'PATCH',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],
[
'name' => 'api#deleteOption',
'url' => '/api/{apiVersion}/option/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],

Expand All @@ -253,15 +261,15 @@
'url' => '/api/{apiVersion}/share',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'shareApi#deleteShare',
'url' => '/api/{apiVersion}/share/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
// TODO: Remove POST in next API release
Expand All @@ -278,7 +286,7 @@
'url' => '/api/{apiVersion}/share/update',
'verb' => 'PATCH',
'requirements' => [
'apiVersion' => 'v2.2'
'apiVersion' => 'v2\.[2-3]'
]
],

Expand All @@ -288,47 +296,47 @@
'url' => '/api/{apiVersion}/submissions/{hash}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#exportSubmissions',
'url' => '/api/{apiVersion}/submissions/export/{hash}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#exportSubmissionsToCloud',
'url' => '/api/{apiVersion}/submissions/export',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#deleteAllSubmissions',
'url' => '/api/{apiVersion}/submissions/{formId}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#insertSubmission',
'url' => '/api/{apiVersion}/submission/insert',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
[
'name' => 'api#deleteSubmission',
'url' => '/api/{apiVersion}/submission/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v2(\.[1-2])?'
'apiVersion' => 'v2(\.[1-3])?'
]
],
]
Expand Down
15 changes: 15 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ This file contains the API-Documentation. For more information on the returned D

### Other API changes
- In API version 2.1 the endpoint `/api/v2.1/share/update` was added to update a Share
- In API version 2.2 the endpoint `/api/v2.2/form/transfer` was added to transfer ownership of a form
- In API version 2.3 the endpoint `/api/v2.3/question/clone` was added to clone a question

## Form Endpoints
### List owned Forms
Expand Down Expand Up @@ -329,6 +331,19 @@ Reorders all Questions of a single form
"data": 4
```

### Clone a question
Creates a clone of a question with all its options.
- Endpoint: `/api/v2.3/question/clone/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
| _id_ | Integer | ID of the question to clone |
- Method: `POST`
- Response: Returns cloned question object with the new ID set.
```
See section 'Create a new question'.
```

## Option Endpoints
Contains only manipulative question-endpoints. To retrieve options, request the full form data.

Expand Down
55 changes: 55 additions & 0 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
Expand Down Expand Up @@ -715,6 +716,60 @@

/**
* @CORS
* @NoAdminRequired
*
* Clone a question
*
* @param int $id the question id
* @return DataResponse
* @throws OCSBadRequestException|OCSForbiddenException
*/
public function cloneQuestion(int $id): DataResponse {
$this->logger->debug('Question to be cloned: {id}', [
'id' => $id
]);

try {
$sourceQuestion = $this->questionMapper->findById($id);
$sourceOptions = $this->optionMapper->findByQuestion($id);
$form = $this->formMapper->findById($sourceQuestion->getFormId());
} catch (IMapperException $e) {
$this->logger->debug('Could not find form or question');
throw new OCSNotFoundException('Could not find form or question');
}

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

$allQuestions = $this->questionMapper->findByForm($form->getId());

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L746

Added line #L746 was not covered by tests

$questionData = $sourceQuestion->read();
unset($questionData['id']);
$questionData["order"] = end($allQuestions)->getOrder() + 1;

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L748-L750

Added lines #L748 - L750 were not covered by tests

$newQuestion = Question::fromParams($questionData);
$this->questionMapper->insert($newQuestion);

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L752-L753

Added lines #L752 - L753 were not covered by tests

$response = $newQuestion->read();
$response['options'] = [];

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L755-L756

Added lines #L755 - L756 were not covered by tests

foreach ($sourceOptions as $sourceOption) {
$optionData = $sourceOption->read();

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L758-L759

Added lines #L758 - L759 were not covered by tests

unset($optionData['id']);
$optionData['questionId'] = $newQuestion->getId();
$newOption = Option::fromParams($optionData);
$insertedOption = $this->optionMapper->insert($newOption);

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L761-L764

Added lines #L761 - L764 were not covered by tests

$response['options'][] = $insertedOption->read();

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L766

Added line #L766 was not covered by tests
}

return new DataResponse($response);

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

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L769

Added line #L769 was not covered by tests
}

/**
* @NoAdminRequired
*
* Add a new option to a question
Expand Down
Loading