diff --git a/core/controllers/api/ItemController.php b/core/controllers/api/ItemController.php index cc2209b58..fc7dff9aa 100644 --- a/core/controllers/api/ItemController.php +++ b/core/controllers/api/ItemController.php @@ -95,6 +95,7 @@ public function putAction() 'removepolicyuser' => 'itemRemovePolicyuser', 'setmetadata' => 'itemSetmetadata', 'setmultiplemetadata' => 'itemSetmultiplemetadata', + 'addmetadata' => 'itemAddmetadata', 'deletemetadata' => 'itemDeletemetadata', 'deletemetadataall' => 'itemDeletemetadataAll', ); diff --git a/core/controllers/components/ApiitemComponent.php b/core/controllers/components/ApiitemComponent.php index 215523f21..519d148ff 100644 --- a/core/controllers/components/ApiitemComponent.php +++ b/core/controllers/components/ApiitemComponent.php @@ -170,6 +170,71 @@ public function itemSetmultiplemetadata($args) return true; } + /** + * Set multiple metadata fields on an item, will overwrite any existing metadatum with + * the new value; requires passing the metadata in the request body as JSON + * with a key of 'metadata' mapping to a list; each element of the 'metadata' list is + * a metadatum on the item; each metadatum requires an 'element' and 'value' key, with + * 'qualifier' and 'type' being optional keys on the metadatum, 'type' defaults to MIDAS_METADATA_TEXT. + * @path /item/addmetadata/{id} + * @http PUT + * @param id The id of the item + * @param revision (Optional) Item Revision number to set metadata on, defaults to latest revision. + * @return true on success, + * will fail if there are no revisions or the specified revision is not found. + * + * @param array $args parameters + * @throws Exception + */ + public function itemAddmetadata($args) + { + /** @var ApihelperComponent $apihelperComponent */ + $apihelperComponent = MidasLoader::loadComponent('Apihelper'); + $apihelperComponent->validateParams($args, array('id')); + $userDao = $apihelperComponent->getUser($args); + + $apihelperComponent->requirePolicyScopes(array(MIDAS_API_PERMISSION_SCOPE_WRITE_DATA)); + + /** @var ItemModel $itemModel */ + $itemModel = MidasLoader::loadModel('Item'); + $item = $itemModel->load($args['id']); + if ($item === false || !$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_WRITE)) { + throw new Exception("This item doesn't exist or you don't have write permission.", MIDAS_INVALID_POLICY); + } + $revisionNumber = array_key_exists('revision', $args) ? (int) $args['revision'] : null; + $revision = $apihelperComponent->getItemRevision($item, $revisionNumber); + + if (!array_key_exists(0, $args)) { + throw new Exception("Missing request body data.", MIDAS_INVALID_PARAMETER); + } + $json_body = json_decode($args[0]); + if (NULL === $json_body) { + throw new Exception("Request body data must be valid JSON.", MIDAS_INVALID_PARAMETER); + } + if (!array_key_exists('metadata', $json_body)) { + throw new Exception("Request body data missing key 'metadata'.", MIDAS_INVALID_PARAMETER); + } + $metadata = $json_body->metadata; + foreach ($metadata as $metadatum) { + if (!isset($metadatum->element) || !isset($metadatum->value)) { + throw new Exception("All metadata must have 'element' and 'value' keys.", MIDAS_INVALID_PARAMETER); + } + } + + foreach ($metadata as $metadatum) { + $apihelperComponent->setMetadata( + $item, + isset($metadatum->type) ? $metadatum->type : MIDAS_METADATA_TEXT, + $metadatum->element, + isset($metadatum->qualifier) ? $metadatum->qualifier : '', + $metadatum->value, + $revision + ); + } + + return true; + } + /** * Delete a metadata tuple (element, qualifier, type) from a specific item revision, * defaults to the latest revision of the item. diff --git a/core/tests/controllers/api/CMakeLists.txt b/core/tests/controllers/api/CMakeLists.txt index 53ceff6b9..543970c59 100644 --- a/core/tests/controllers/api/CMakeLists.txt +++ b/core/tests/controllers/api/CMakeLists.txt @@ -19,3 +19,4 @@ add_midas_test(CoreRestCallUserMethodsTest RestCallUserMethodsTest.php) add_midas_test(CoreRestKeyControllerTest RestKeyControllerTest.php) +add_midas_test(CoreRestCallItemMethodsTest RestCallItemMethodsTest.php) diff --git a/core/tests/controllers/api/RestCallItemMethodsTest.php b/core/tests/controllers/api/RestCallItemMethodsTest.php new file mode 100644 index 000000000..9259689bd --- /dev/null +++ b/core/tests/controllers/api/RestCallItemMethodsTest.php @@ -0,0 +1,132 @@ +loadData('Item', 'default'); + $itemDao = $this->Item->load($itemsFile[1]->getKey()); + + $apipath = '/item/addmetadata/' . $itemDao->getItemId(); + + // No user will fail. + $this->resetAll(); + $resp = $this->_callRestApi('PUT', $apipath); + $this->_assertStatusFail($resp); + + $usersFile = $this->loadData('User', 'default'); + $userDao = $this->User->load($usersFile[0]->getKey()); + + // Lack of request body will fail. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusFail($resp); + + // Request body without a 'metadata' key will fail. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $this->params[0] = json_encode(array('murkydata' => array())); + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusFail($resp); + + // Metadatum needs 'value' key. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $this->params[0] = json_encode(array('metadata' => array('element' => 'key1'))); + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusFail($resp); + + // Metadatum needs 'element' key. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $this->params[0] = json_encode(array('metadata' => array('value' => 'val1'))); + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusFail($resp); + + // Write some metadata correctly. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $this->params[0] = json_encode(array('metadata' => array( + array('element' => 'key1', 'value' => 'val1'), + array('element' => 'key2', 'value' => 'val2') + ))); + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusOk($resp); + + /** @var ItemModel $itemModel */ + $itemModel = MidasLoader::loadModel('Item'); + $revisionDao = $itemModel->getLastRevision($itemDao); + /** @var ItemRevisionModel $itemRevisionModel */ + $itemRevisionModel = MidasLoader::loadModel('ItemRevision'); + $metadata = $itemRevisionModel->getMetadata($revisionDao); + $metadataArray = array(); + $key1 = false; + $key2 = false; + foreach ($metadata as $metadatum) { + if ($metadatum->element === 'key1' && $metadatum->value === 'val1') { + $key1 = true; + } + if ($metadatum->element === 'key2' && $metadatum->value === 'val2') { + $key2 = true; + } + } + $this->assertTrue($key1 && $key2, 'Metadata incorrectly set'); + + // Update key1, add key3, leave key2 alone. + $this->resetAll(); + $this->params['useSession'] = 'true'; + $this->params[0] = json_encode(array('metadata' => array( + array('element' => 'key1', 'value' => 'newval1'), + array('element' => 'key3', 'value' => 'val3') + ))); + $resp = $this->_callRestApi('PUT', $apipath, $userDao); + $this->_assertStatusOk($resp); + + $metadata = $itemRevisionModel->getMetadata($revisionDao); + $metadataArray = array(); + $key1 = false; + $key2 = false; + $key3 = false; + foreach ($metadata as $metadatum) { + if ($metadatum->element === 'key1' && $metadatum->value === 'newval1') { + $key1 = true; + } + if ($metadatum->element === 'key2' && $metadatum->value === 'val2') { + $key2 = true; + } + if ($metadatum->element === 'key3' && $metadatum->value === 'val3') { + $key3 = true; + } + } + $this->assertTrue($key1 && $key2 && $key3, 'Metadata incorrectly set'); + } +} diff --git a/modules/api/controllers/components/ApiComponent.php b/modules/api/controllers/components/ApiComponent.php index a937bb7a2..8c6b67a03 100644 --- a/modules/api/controllers/components/ApiComponent.php +++ b/modules/api/controllers/components/ApiComponent.php @@ -767,6 +767,28 @@ public function itemSetmultiplemetadata($args) return $this->_callCoreApiMethod($args, 'itemSetmultiplemetadata', 'item'); } + /** + * Set multiple metadata fields on an item, will overwrite any existing metadatum with + * the new value; requires passing the metadata in the request body as JSON + * with a key of 'metadata' mapping to a list; each element of the 'metadata' list is + * a metadatum on the item; each metadatum requires an 'element' and 'value' key, with + * 'qualifier' and 'type' being optional keys on the metadatum, 'type' defaults to MIDAS_METADATA_TEXT. + * + * @param token Authentication token + * @param itemid The id of the item + * @param revision (Optional) Item Revision number to set metadata on, defaults to latest revision. + * @return true on success, + * will fail if there are no revisions or the specified revision is not found. + */ + public function itemAddmetadata($args) + { + /** @var ApihelperComponent $ApihelperComponent */ + $ApihelperComponent = MidasLoader::loadComponent('Apihelper'); + $ApihelperComponent->renameParamKey($args, 'itemid', 'id'); + + return $this->_callCoreApiMethod($args, 'itemAddmetadata', 'item'); + } + /** * Delete a metadata tuple (element, qualifier, type) from a specific item revision, * defaults to the latest revision of the item.