diff --git a/docs/src/paradox/03-apis/api-admin/lists.md b/docs/src/paradox/03-apis/api-admin/lists.md
index 80bcbc9717..2e7d137750 100644
--- a/docs/src/paradox/03-apis/api-admin/lists.md
+++ b/docs/src/paradox/03-apis/api-admin/lists.md
@@ -23,19 +23,30 @@ License along with Knora. If not, see .
**List Operations:**
-- `GET: /admin/lists[?projectIri=]` : return all lists optionally filtered by project
-- `GET: /admin/lists/` : return complete list with children
+- `GET: /admin/lists[?projectIRI=]` : return all lists optionally filtered by project
+- `GET: /admin/lists/` : return complete list with children
- `POST: /admin/lists` : create new list
-- `POST: /admin/lists/` : create new child node under the supplied parent node IRI
-- NOT IMPLEMENTED: `DELETE: /admin/lists/` : delete list including children if not used
-- `GET: /admin/lists/infos/` : return list information (without children)
-- `PUT: /admin/lists/infos/` : update list information
+- NOT IMPLEMENTED: `PUT: /admin/lists/` : create new list with given IRI
+- NOT IMPLEMENTED: `DELETE: /admin/lists/` : delete list including children if not used
+- `GET: /admin/lists//Info` : return list information (without children)
+- `PUT: /admin/lists//ListInfoName` : update list name information
+- `PUT: /admin/lists//ListInfoLabel` : update list label information
+- `PUT: /admin/lists//ListInfoComment` : update list comment information
+
+
**List Node operations**
-- `GET: /admin/lists/nodes/` : return list node information (without children)
-- NOT IMPLEMENTED: `POST: /admin/lists/nodes/` : update list node information
-- NOT IMPLEMENTED: `DELETE: /admin/lists/nodes/` : delete list node including children if not used
+- NOT IMPLEMENTED: `GET: /admin/lists/nodes/` : return list node information (with children)
+- `GET: /admin/lists/nodes//Info` : return list node information (without children)
+- `POST: /admin/lists/nodes` : create new child node under the supplied parent node IRI
+- NOT IMPLEMENTED: `PUT: /admin/lists/nodes/` : create child node with given IRI und the supplied parent node IRI
+- `PUT: /admin/lists/nodes//NodeInfoName` : update list node name information
+- `PUT: /admin/lists/nodes//NodeInfoLabel` : update list node label information
+- `PUT: /admin/lists/nodes//NodeInfoComment` : update list node comment information
+- NOT IMPLEMENTED: `PUT: /admin/lists/nodes//NodeInfoPosition` : update list node position information
+- NOT IMPLEMENTED: `PUT: /admin/lists/nodes//NodeInfoParent` : update list node parent information
+- NOT IMPLEMENTED: `DELETE: /admin/lists/nodes/` : delete list node including children if not used
## List Operations
@@ -43,18 +54,18 @@ License along with Knora. If not, see .
- Required permission: none
- Return all lists optionally filtered by project
- - GET: `/admin/lists[?projectIri=]`
+ - GET: `/admin/lists[?projectIRI=]`
### Get list
- Required permission: none
- Return complete list with children
- - GET: `/admin/lists/`
+ - GET: `/admin/lists/`
### Create new list
- - Required permission: SystemAdmin / ProjectAdmin
+ - Required permission: SystemAdmin or ProjectAdmin
- POST: `/admin/lists`
- BODY:
```
@@ -65,13 +76,88 @@ License along with Knora. If not, see .
}
```
+### Get list's information
+
+ - Required permission: none
+ - Return list information (without children)
+ - GET: `/admin/lists//Info`
+
+### Update lists's name information
+
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list name information
+ - PUT: `/admin/lists//ListInfoName`
+ - BODY
+ ```
+ {
+ "listIri": "listIri",
+ "projectIri": "projectIri",
+ "name": "name"
+ }
+ ```
+ - Submit empty parameter (`"name": ""`) to delete name
+ - `name` must be unique inside project
+
+### Update list's label information
+
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list label information
+ - PUT: `/admin/lists//ListInfoLabel`
+ - BODY
+ ```
+ {
+ "listIri": "listIri",
+ "projectIri": "projectIri",
+ "labels": [{"value": "New label", "language": "en"}]
+ }
+ ```
+ - At least one label must be submitted
+
+### Update list's comment information
+
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list comment information
+ - PUT: `/admin/lists//ListInfoName`
+ - BODY
+ ```
+ {
+ "listIri": "listIri",
+ "projectIri": "projectIri",
+ "comment": [{"value": "New Comment", "language": "en"}]
+ }
+ ```
+ - Submit empty parameter (`"comment": []`) to delete comments
+
+
+
+### Delete list
+
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Delete List including children if not used
+ - DELETE: `/admin/lists/`
+
+
+## List Node Operations
+
+### Get List Node
+
+ - Required permission: none
+ - Return list node with children
+ - GET: `/admin/lists/nodes/`
+
+### Get node's information
+
+ - Required permission: none
+ - Return node information (without children)
+ - GET: `/admin/lists/node//Info`
+
### Create new child node
- - Required permission: SystemAdmin / ProjectAdmin
+ - Required permission: SystemAdmin or ProjectAdmin
- Appends a new child node under the supplied nodeIri. If the supplied nodeIri
is the listIri, then a new child node is appended to the top level. Children
are currently only appended.
- - POST: `/admin/lists/`
+ - POST: `/admin/lists/nodes`
- BODY:
```
{
@@ -82,32 +168,56 @@ License along with Knora. If not, see .
"comments": [{ "value": "New First Child List Node Comment", "language": "en"}]
}
```
+
+### Delete List node
-### Get list's information
-
- - Required permission: none
- - Return list information (without children)
- - GET: `/admin/lists/infos/`
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Delete node including children if not used
+ - DELETE: `/admin/lists/nodes/`
-### Update list's information
+### Update node's name information
- - Required permission: none
- - Update list information
- - PUT: `/admin/lists/infos/`
- - BODY:
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list node name information
+ - PUT: `/admin/lists/nodes//NodeInfoName`
+ - BODY
```
{
- "listIri": "listIri",
- "projectIri": "someprojectiri",
- "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
- "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ "nodeIri": "nodeIri",
+ "projectIri": "projectIri",
+ "name": "name"
}
```
+ - Submit empty parameter (`"name": ""`) to delete name
+ - `name` must be unique inside project
+
+### Update node's label information
-## List Node Operations
-
-### Get List Node Information
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list node label information
+ - PUT: `/admin/lists/nodes//NodeInfoLabel`
+ - BODY
+ ```
+ {
+ "nodeIri": "nodeIri",
+ "projectIri": "projectIri",
+ "labels": [{"value": "New label", "language": "en"}]
+ }
+ ```
+ - At least one label must be submitted
+
+### Update node's comment information
- - Required permission: none
- - Return list node information (without children)
- - GET: `/admin/lists/nodes/`
+ - Required permission: SystemAdmin or ProjectAdmin
+ - Update list node comment information
+ - PUT: `/admin/lists/nodes//NodeInfoName`
+ - BODY
+ ```
+ {
+ "nodeIri": "nodeIri",
+ "projectIri": "projectIri",
+ "comment": [{"value": "New Comment", "language": "en"}]
+ }
+ ```
+ - Submit empty parameter (`"comment": []`) to delete comments
+
\ No newline at end of file
diff --git a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
index 3a8c1431a6..7cf2db297e 100644
--- a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
+++ b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
@@ -29,6 +29,7 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM
import org.knora.webapi.messages.admin.responder.usersmessages.UserADM
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.util.StringFormatter
+import spray.json.{JsArray, JsObject, JsString}
/**
* This object holds the same user which are loaded with '_test_data/all_data/admin-data.ttl'. Using this object
@@ -632,19 +633,125 @@ object SharedTestDataADM {
val createListRequest: String =
s"""{
| "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newList",
| "labels": [{ "value": "Neue Liste", "language": "de"}],
| "comments": []
|}""".stripMargin
- def updateListInfoRequest(listIri: IRI): String = {
+ def updateListNameRequest(listIri: IRI, projectIri: IRI, name: String): String = {
s"""{
| "listIri": "$listIri",
- | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
- | "labels": [{ "value": "Neue geänderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
- | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ | "projectIri": "$projectIri",
+ | "name": "$name"
+ |}""".stripMargin
+ }
+
+ def updateListLabelsRequest(listIri: IRI, projectIri: IRI, labels: Vector[Map[String, String]]): String = {
+ JsObject(
+ Map(
+ "listIri" -> JsString(listIri),
+ "projectIri" -> JsString(projectIri),
+ "labels" -> JsArray(
+ labels.map {
+ labelMap =>
+ JsObject(
+ labelMap.map {
+ case (key, value) => key -> JsString(value)
+ }
+ )
+ }
+ )
+ )
+ ).prettyPrint
+ }
+
+ def updateListCommentsRequest(listIri: IRI, projectIri: IRI, comments: Vector[Map[String, String]]): String = {
+ JsObject(
+ Map(
+ "listIri" -> JsString(listIri),
+ "projectIri" -> JsString(projectIri),
+ "comments" -> JsArray(
+ comments.map {
+ labelMap =>
+ JsObject(
+ labelMap.map {
+ case (key, value) => key -> JsString(value)
+ }
+ )
+ }
+ )
+ )
+ ).prettyPrint
+ }
+
+ def updateListNodeNameRequest(nodeIri: IRI, projectIri: IRI, name: String): String = {
+ s"""{
+ | "nodeIri": "$nodeIri",
+ | "projectIri": "$projectIri",
+ | "name": "$name"
|}""".stripMargin
}
+ def updateListNodeLabelsRequest(nodeIri: IRI, projectIri: IRI, labels: Vector[Map[String, String]]): String = {
+ JsObject(
+ Map(
+ "nodeIri" -> JsString(nodeIri),
+ "projectIri" -> JsString(projectIri),
+ "labels" -> JsArray(
+ labels.map {
+ labelMap =>
+ JsObject(
+ labelMap.map {
+ case (key, value) => key -> JsString(value)
+ }
+ )
+ }
+ )
+ )
+ ).prettyPrint
+ }
+
+ def updateListNodeCommentsRequest(nodeIri: IRI, projectIri: IRI, comments: Vector[Map[String, String]]): String = {
+ JsObject(
+ Map(
+ "nodeIri" -> JsString(nodeIri),
+ "projectIri" -> JsString(projectIri),
+ "comments" -> JsArray(
+ comments.map {
+ labelMap =>
+ JsObject(
+ labelMap.map {
+ case (key, value) => key -> JsString(value)
+ }
+ )
+ }
+ )
+ )
+ ).prettyPrint
+ }
+
+ val updatedLabels: Vector[Map[String, String]] = Vector(
+ Map(
+ "value" -> "Geändertes Etikett",
+ "language" -> "de"
+ ),
+ Map(
+ "value" -> "Changed label",
+ "language" -> "en"
+ )
+ )
+
+ val updatedComments: Vector[Map[String, String]] = Vector(
+ Map(
+ "value" -> "Geänderter Kommentar",
+ "language" -> "de"
+ ),
+ Map(
+ "value" -> "Changed comment",
+ "language" -> "en"
+ )
+ )
+
def addChildListNodeRequest(parentNodeIri: IRI,
name: String,
label: String,
diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala
index 13c912ba91..16e1f31658 100644
--- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala
+++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala
@@ -69,14 +69,16 @@ case class CreateListApiRequestADM(projectIri: IRI,
}
/**
- * Represents an API request payload that asks the Knora API server to create a new child list node which will be
- * attached to the list node identified by the supplied listNodeIri, where the list node to which a child list node
- * is added can be either a root list node or a child list node. At least one label needs to be supplied. If other
- * child nodes exist, the newly created list node will be appended to the end.
+ * Represents an API request payload that asks the Knora API server to create
+ * a new child list node which will be attached to the list node identified by
+ * the supplied listNodeIri, where the list node to which a child list node
+ * is added can be either a root list node or a child list node. At least one
+ * label needs to be supplied. If other child nodes exist, the newly created
+ * list node will be appended to the end.
*
- * @param parentNodeIri
- * @param labels
- * @param comments
+ * @param parentNodeIri the IRI of the parent node.
+ * @param labels the labels.
+ * @param comments the comments.
*/
case class CreateChildNodeApiRequestADM(parentNodeIri: IRI,
projectIri: IRI,
@@ -115,17 +117,20 @@ case class CreateChildNodeApiRequestADM(parentNodeIri: IRI,
}
/**
- * Represents an API request payload that asks the Knora API server to update an existing list's basic information.
+ * Represents an API request payload that asks the Knora API server to update
+ * an existing list's basic information.
*
* @param listIri the IRI of the list to change.
* @param projectIri the IRI of the project the list belongs to.
+ * @param name the name.
* @param labels the labels.
* @param comments the comments.
*/
case class ChangeListInfoApiRequestADM(listIri: IRI,
projectIri: IRI,
- labels: Seq[StringLiteralV2],
- comments: Seq[StringLiteralV2]) extends ListADMJsonProtocol {
+ name: Option[String] = None,
+ labels: Option[Seq[StringLiteralV2]] = None,
+ comments: Option[Seq[StringLiteralV2]] = None) extends ListADMJsonProtocol {
private val stringFormatter = StringFormatter.getInstanceForConstantOntologies
@@ -145,11 +150,126 @@ case class ChangeListInfoApiRequestADM(listIri: IRI,
throw BadRequestException(PROJECT_IRI_INVALID_ERROR)
}
- if (labels.isEmpty && comments.isEmpty) {
- throw BadRequestException(REQUEST_NOT_CHANGING_DATA_ERROR)
+ def toJsValue: JsValue = changeListInfoApiRequestADMFormat.write(this)
+}
+
+/**
+ * Represents a payload that asks the Knora API server to update
+ * an existing list's basic information.
+ *
+ * @param listIri the IRI of the list to change.
+ * @param projectIri the IRI of the project the list belongs to.
+ * @param name the name.
+ * @param labels the labels.
+ * @param comments the comments.
+ */
+case class ChangeListInfoPayloadADM(listIri: IRI,
+ projectIri: IRI,
+ name: Option[Option[String]] = None,
+ labels: Option[Seq[StringLiteralV2]] = None,
+ comments: Option[Seq[StringLiteralV2]]= None) extends ListADMJsonProtocol {
+
+ private val stringFormatter = StringFormatter.getInstanceForConstantOntologies
+
+ if (listIri.isEmpty) {
+ throw BadRequestException(LIST_IRI_MISSING_ERROR)
}
- def toJsValue: JsValue = changeListInfoApiRequestADMFormat.write(this)
+ if (!stringFormatter.isKnoraListIriStr(listIri)) {
+ throw BadRequestException(LIST_IRI_INVALID_ERROR)
+ }
+
+ if (projectIri.isEmpty) {
+ throw BadRequestException(PROJECT_IRI_MISSING_ERROR)
+ }
+
+ if (!stringFormatter.isKnoraProjectIriStr(projectIri)) {
+ throw BadRequestException(PROJECT_IRI_INVALID_ERROR)
+ }
+
+ if (labels.isDefined && labels.get.isEmpty) {
+ throw BadRequestException("Lists need at least one label.")
+ }
+
+ def toJsValue: JsValue = changeListInfoPayloadADMFormat.write(this)
+}
+
+/**
+ * Represents an API request payload that asks the Knora API server to update
+ * an existing list nodes's basic information.
+ *
+ * @param nodeIri the IRI of the list node to change.
+ * @param projectIri the IRI of the project the list belongs to.
+ * @param name the name.
+ * @param labels the labels.
+ * @param comments the comments.
+ */
+case class ChangeListNodeInfoApiRequestADM(nodeIri: IRI,
+ projectIri: IRI,
+ name: Option[String] = None,
+ labels: Option[Seq[StringLiteralV2]] = None,
+ comments: Option[Seq[StringLiteralV2]] = None) extends ListADMJsonProtocol {
+
+ private val stringFormatter = StringFormatter.getInstanceForConstantOntologies
+
+ if (nodeIri.isEmpty) {
+ throw BadRequestException(LIST_NODE_IRI_MISSING_ERROR)
+ }
+
+ if (!stringFormatter.isKnoraListIriStr(nodeIri)) {
+ throw BadRequestException(LIST_NODE_IRI_INVALID_ERROR)
+ }
+
+ if (projectIri.isEmpty) {
+ throw BadRequestException(PROJECT_IRI_MISSING_ERROR)
+ }
+
+ if (!stringFormatter.isKnoraProjectIriStr(projectIri)) {
+ throw BadRequestException(PROJECT_IRI_INVALID_ERROR)
+ }
+
+ def toJsValue: JsValue = changeListNodeInfoApiRequestADMFormat.write(this)
+}
+
+/**
+ * Represents a payload that asks the Knora API server to update
+ * an existing list nodes's basic information.
+ *
+ * @param nodeIri the IRI of the list node to change.
+ * @param projectIri the IRI of the project the list belongs to.
+ * @param name the name.
+ * @param labels the labels.
+ * @param comments the comments.
+ */
+case class ChangeListNodeInfoPayloadADM(nodeIri: IRI,
+ projectIri: IRI,
+ name: Option[Option[String]] = None,
+ labels: Option[Seq[StringLiteralV2]] = None,
+ comments: Option[Seq[StringLiteralV2]] = None) extends ListADMJsonProtocol {
+
+ private val stringFormatter = StringFormatter.getInstanceForConstantOntologies
+
+ if (nodeIri.isEmpty) {
+ throw BadRequestException(LIST_NODE_IRI_MISSING_ERROR)
+ }
+
+ if (!stringFormatter.isKnoraListIriStr(nodeIri)) {
+ throw BadRequestException(LIST_NODE_IRI_INVALID_ERROR)
+ }
+
+ if (projectIri.isEmpty) {
+ throw BadRequestException(PROJECT_IRI_MISSING_ERROR)
+ }
+
+ if (!stringFormatter.isKnoraProjectIriStr(projectIri)) {
+ throw BadRequestException(PROJECT_IRI_INVALID_ERROR)
+ }
+
+ if (labels.isDefined && labels.get.isEmpty) {
+ throw BadRequestException("Listnodes need at least one label.")
+ }
+
+ def toJsValue: JsValue = changeListNodeInfoPayloadADMFormat.write(this)
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -230,10 +350,23 @@ case class ListCreateRequestADM(createListRequest: CreateListApiRequestADM,
* @param apiRequestID the ID of the API request.
*/
case class ListInfoChangeRequestADM(listIri: IRI,
- changeListRequest: ChangeListInfoApiRequestADM,
+ changeListRequest: ChangeListInfoPayloadADM,
requestingUser: UserADM,
apiRequestID: UUID) extends ListsResponderRequestADM
+/**
+ * Request updating basic information of an existing node.
+ *
+ * @param nodeIri the IRI of the node to be updated.
+ * @param changeNodeRequest the data which needs to be update.
+ * @param requestingUser the user initiating the request.
+ * @param apiRequestID the ID of the API request.
+ */
+case class ListNodeInfoChangeRequestADM(nodeIri: IRI,
+ changeNodeRequest: ChangeListNodeInfoPayloadADM,
+ requestingUser: UserADM,
+ apiRequestID: UUID) extends ListsResponderRequestADM
+
/**
* Request the creation of a new list (child) node.
*
@@ -295,9 +428,9 @@ case class ListNodeInfoGetResponseADM(nodeinfo: ListNodeInfoADM) extends KnoraRe
/**
* Responds to a [[NodePathGetRequestADM]] by providing the path to a particular hierarchical list node.
*
- * @param elements a list of the nodes composing the path from the list's root node up to and including the specified node.
+ * @param nodeList a list of the nodes composing the path from the list's root node up to and including the specified node.
*/
-case class NodePathGetResponseADM(elements: Seq[NodePathElementADM]) extends KnoraResponseADM with ListADMJsonProtocol {
+case class NodePathGetResponseADM(nodeList: Seq[NodePathElementADM]) extends KnoraResponseADM with ListADMJsonProtocol {
def toJsValue = nodePathGetResponseADMFormat.write(this)
}
@@ -398,6 +531,16 @@ case class ListRootNodeInfoADM(id: IRI, projectIri: IRI, name: Option[String], l
}
+
+/**
+ *
+ * @param id the IRI of the list.
+ * @param name the name of the list node.
+ * @param labels the labels of the node in all available languages.
+ * @param comments the comments attached to the node in all available languages.
+ * @param position the position of the node among its siblings (optional).
+ * @param hasRootNode the Iri of the root node, if this is not the root node.
+ */
case class ListChildNodeInfoADM(id: IRI, name: Option[String], labels: StringLiteralSequenceV2, comments: StringLiteralSequenceV2, position: Int, hasRootNode: IRI) extends ListNodeInfoADM(id, name, labels, comments) {
/**
@@ -486,7 +629,7 @@ abstract class ListNodeADM(id: IRI, name: Option[String], labels: StringLiteralS
* @param comments the comment(s) attached to the list in a specific language (if language tags are used) .
* @param children the list node's child nodes.
*/
-case class ListRootNodeADM(id: IRI, projectIri: IRI, name: Option[String], labels: StringLiteralSequenceV2, comments: StringLiteralSequenceV2, children: Seq[ListChildNodeADM]) extends ListNodeADM(id, name, labels, comments, children) {
+case class ListRootNodeADM(id: IRI, projectIri: IRI, name: Option[String], labels: StringLiteralSequenceV2, comments: StringLiteralSequenceV2, children: Seq[ListChildNodeADM]) extends ListNodeADM(id, name, labels, comments, children) {
/**
* Sorts the whole hierarchy.
@@ -873,13 +1016,13 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with
* @param element a [[NodePathElementADM]].
* @return a [[JsValue]].
*/
- def write(element: NodePathElementADM): JsValue = {
+ def write(nodeInfo: NodePathElementADM): JsValue = {
JsObject(
- "id" -> element.id.toJson,
- "name" -> element.name.toJson,
- "labels" -> JsArray(element.labels.stringLiterals.map(_.toJson)),
- "comments" -> JsArray(element.comments.stringLiterals.map(_.toJson))
+ "id" -> nodeInfo.id.toJson,
+ "name" -> nodeInfo.name.toJson,
+ "labels" -> JsArray(nodeInfo.labels.stringLiterals.map(_.toJson)),
+ "comments" -> JsArray(nodeInfo.comments.stringLiterals.map(_.toJson))
)
}
@@ -958,7 +1101,10 @@ trait ListADMJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol with
implicit val createListApiRequestADMFormat: RootJsonFormat[CreateListApiRequestADM] = jsonFormat(CreateListApiRequestADM, "projectIri", "name", "labels", "comments")
implicit val createListNodeApiRequestADMFormat: RootJsonFormat[CreateChildNodeApiRequestADM] = jsonFormat(CreateChildNodeApiRequestADM, "parentNodeIri", "projectIri", "name", "labels", "comments")
- implicit val changeListInfoApiRequestADMFormat: RootJsonFormat[ChangeListInfoApiRequestADM] = jsonFormat(ChangeListInfoApiRequestADM, "listIri", "projectIri", "labels", "comments")
+ implicit val changeListInfoApiRequestADMFormat: RootJsonFormat[ChangeListInfoApiRequestADM] = jsonFormat(ChangeListInfoApiRequestADM, "listIri", "projectIri", "name", "labels", "comments")
+ implicit val changeListInfoPayloadADMFormat: RootJsonFormat[ChangeListInfoPayloadADM] = jsonFormat(ChangeListInfoPayloadADM, "listIri", "projectIri", "name", "labels", "comments")
+ implicit val changeListNodeInfoApiRequestADMFormat: RootJsonFormat[ChangeListNodeInfoApiRequestADM] = jsonFormat(ChangeListNodeInfoApiRequestADM, "nodeIri", "projectIri", "name", "labels", "comments")
+ implicit val changeListNodeInfoPayloadADMFormat: RootJsonFormat[ChangeListNodeInfoPayloadADM] = jsonFormat(ChangeListNodeInfoPayloadADM, "nodeIri", "projectIri", "name", "labels", "comments")
implicit val nodePathGetResponseADMFormat: RootJsonFormat[NodePathGetResponseADM] = jsonFormat(NodePathGetResponseADM, "elements")
implicit val listsGetResponseADMFormat: RootJsonFormat[ListsGetResponseADM] = jsonFormat(ListsGetResponseADM, "lists")
implicit val listGetResponseADMFormat: RootJsonFormat[ListGetResponseADM] = jsonFormat(ListGetResponseADM, "list")
diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala
index 41d53f966f..3415bead01 100644
--- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala
+++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraAdminToApiV2ComplexTransformationRules.scala
@@ -361,30 +361,6 @@ object KnoraAdminToApiV2ComplexTransformationRules extends OntologyTransformatio
)
)
- private val UpdateListInfoRequest = makeClass(
- classIri = OntologyConstants.KnoraAdminV2.UpdateListInfoRequest,
- predicates = Seq(
- makePredicate(
- predicateIri = OntologyConstants.Rdfs.Label,
- objectsWithLang = Map(
- LanguageCodes.EN -> "update list info request"
- )
- ),
- makePredicate(
- predicateIri = OntologyConstants.Rdfs.Comment,
- objectsWithLang = Map(
- LanguageCodes.EN -> "A request to update information about a list."
- )
- )
- ),
- directCardinalities = Map(
- OntologyConstants.KnoraAdminV2.ListIri -> Cardinality.MustHaveOne,
- OntologyConstants.KnoraAdminV2.ProjectIri -> Cardinality.MayHaveOne,
- OntologyConstants.KnoraAdminV2.Labels -> Cardinality.MustHaveSome,
- OntologyConstants.KnoraAdminV2.Comments -> Cardinality.MayHaveMany
- )
- )
-
private val CreateListRequest = makeClass(
classIri = OntologyConstants.KnoraAdminV2.CreateListRequest,
predicates = Seq(
@@ -1721,7 +1697,6 @@ object KnoraAdminToApiV2ComplexTransformationRules extends OntologyTransformatio
ListClass,
CreateListRequest,
CreateChildNodeRequest,
- UpdateListInfoRequest,
ListInfoResponse,
ListNodeInfoResponse,
CreateGroupRequest,
diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala
index 2f9e560d57..0130ce2ddd 100644
--- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala
+++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala
@@ -36,7 +36,7 @@ import org.knora.webapi.util.SmartIri
import scala.annotation.tailrec
import scala.collection.breakOut
-import scala.concurrent.Future
+import scala.concurrent.{Await, Future}
object ListsResponderADM {
@@ -72,6 +72,7 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
case NodePathGetRequestADM(iri, requestingUser) => nodePathGetAdminRequest(iri, requestingUser)
case ListCreateRequestADM(createListRequest, requestingUser, apiRequestID) => listCreateRequestADM(createListRequest, requestingUser, apiRequestID)
case ListInfoChangeRequestADM(listIri, changeListRequest, requestingUser, apiRequestID) => listInfoChangeRequest(listIri, changeListRequest, requestingUser, apiRequestID)
+ case ListNodeInfoChangeRequestADM(nodeIri, changeNodeRequest, requestingUser, apiRequestID) => listNodeInfoChangeRequest(nodeIri, changeNodeRequest, requestingUser, apiRequestID)
case ListChildNodeCreateRequestADM(parentNodeIri, createListNodeRequest, requestingUser, apiRequestID) => listChildNodeCreateRequestADM(parentNodeIri, createListNodeRequest, requestingUser, apiRequestID)
case other => handleUnexpectedMessage(other, log, this.getClass.getName)
}
@@ -586,7 +587,7 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
case None => acc
}
}
- } yield NodePathGetResponseADM(elements = makePath(queryNodeIri, nodeMap, parentMap, Nil))
+ } yield NodePathGetResponseADM(nodeList = makePath(queryNodeIri, nodeMap, parentMap, Nil))
}
@@ -679,14 +680,14 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
* @throws BadRequestException in the case when the project IRI is missing or invalid.
* @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed.
*/
- private def listInfoChangeRequest(listIri: IRI, changeListRequest: ChangeListInfoApiRequestADM, requestingUser: UserADM, apiRequestID: UUID): Future[ListInfoGetResponseADM] = {
+ private def listInfoChangeRequest(listIri: IRI, changeListRequest: ChangeListInfoPayloadADM, requestingUser: UserADM, apiRequestID: UUID): Future[ListInfoGetResponseADM] = {
/**
* The actual task run with an IRI lock.
*/
- def listInfoChangeTask(listIri: IRI, changeListRequest: ChangeListInfoApiRequestADM, requestingUser: UserADM, apiRequestID: UUID): Future[ListInfoGetResponseADM] = for {
+ def listInfoChangeTask(listIri: IRI, changeListRequest: ChangeListInfoPayloadADM, requestingUser: UserADM, apiRequestID: UUID): Future[ListInfoGetResponseADM] = for {
// check if required information is supplied
- _ <- Future(if (changeListRequest.labels.isEmpty && changeListRequest.comments.isEmpty) throw BadRequestException(REQUEST_NOT_CHANGING_DATA_ERROR))
+ _ <- Future(if (changeListRequest.name.isEmpty && changeListRequest.labels.isEmpty && changeListRequest.comments.isEmpty) throw BadRequestException(REQUEST_NOT_CHANGING_DATA_ERROR))
_ = if (!listIri.equals(changeListRequest.listIri)) throw BadRequestException("List IRI in path and payload don't match.")
// check if the requesting user is allowed to perform operation
@@ -712,6 +713,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
case None => throw BadRequestException(s"Project '${list.listinfo.projectIri}' not found.")
}
+ /* verify that the list node name is unique for the project */
+ nodeNameUnique: Boolean <- listNodeNameIsProjectUnique(changeListRequest.projectIri, changeListRequest.name.flatten)
+ _ = if (!nodeNameUnique) {
+ throw DuplicateValueException(s"The node name ${changeListRequest.name.get} is already used by a list inside the project ${changeListRequest.projectIri}.")
+ }
+
// get the data graph of the project.
dataNamedGraph = stringFormatter.projectDataNamedGraphV2(project)
@@ -722,35 +729,43 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
listIri = listIri,
projectIri = project.id,
listClassIri = OntologyConstants.KnoraBase.ListNode,
+ maybeName = changeListRequest.name,
maybeLabels = changeListRequest.labels,
maybeComments = changeListRequest.comments
).toString
- // _ = log.debug("listCreateRequestADM - createNewListSparqlString: {}", createNewListSparqlString)
- changeResourceResponse <- (storeManager ? SparqlUpdateRequest(changeListInfoSparqlString)).mapTo[SparqlUpdateResponse]
+ _ <- (storeManager ? SparqlUpdateRequest(changeListInfoSparqlString)).mapTo[SparqlUpdateResponse]
/* Verify that the list was updated */
maybeListADM <- listGetADM(listIri, KnoraSystemInstances.Users.SystemUser)
updatedList = maybeListADM.getOrElse(throw UpdateNotPerformedException(s"List $listIri was not updated. Please report this as a possible bug."))
-
-
+ /* verify name update */
+ _ = if (changeListRequest.name.nonEmpty) {
+ if (changeListRequest.name.get.nonEmpty) {
+ if (updatedList.listinfo.name.nonEmpty) {
+ if (updatedList.listinfo.name.get != changeListRequest.name.get.get) throw UpdateNotPerformedException("Lists's 'name' was not updated. Please report this as a possible bug.")
+ }
+ } else {
+ if (updatedList.listinfo.name.nonEmpty) {
+ throw UpdateNotPerformedException("Lists's 'name' was not updated. Please report this as a possible bug.")
+ }
+ }
+ }
+ /* verify label update */
_ = if (changeListRequest.labels.nonEmpty) {
- if (updatedList.listinfo.labels.stringLiterals.sorted != changeListRequest.labels.sorted) throw UpdateNotPerformedException("Lists's 'labels' where not updated. Please report this as a possible bug.")
+ if (updatedList.listinfo.labels.stringLiterals.sorted != changeListRequest.labels.get.sorted) throw UpdateNotPerformedException("Lists's 'labels' was not updated. Please report this as a possible bug.")
}
-
+ /* verify comment update */
_ = if (changeListRequest.comments.nonEmpty) {
- if (updatedList.listinfo.comments.stringLiterals.sorted != changeListRequest.comments.sorted) throw UpdateNotPerformedException("List's 'comments' was not updated. Please report this as a possible bug.")
+ if (updatedList.listinfo.comments.stringLiterals.sorted != changeListRequest.comments.get.sorted) throw UpdateNotPerformedException("List's 'comments' was not updated. Please report this as a possible bug.")
}
-
- // _ = log.debug(s"listInfoChangeRequest - updatedList: {}", updatedList)
-
} yield ListInfoGetResponseADM(listinfo = updatedList.listinfo)
for {
// run list info update with an local IRI lock
taskResult <- IriLocker.runWithIriLock(
apiRequestID,
- listIri,
+ LISTS_GLOBAL_LOCK_IRI,
() => listInfoChangeTask(listIri, changeListRequest, requestingUser, apiRequestID)
)
} yield taskResult
@@ -862,6 +877,114 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde
}
+ /**
+ * Changes basic list node information stored in the list node.
+ *
+ * @param nodeIri the node's IRI.
+ * @param changeNodeRequest the new list information.
+ * @param requestingUser the user that is making the request.
+ * @param apiRequestID the unique api request ID.
+ * @return a [[ListNodeInfoGetResponseADM]]
+ * @throws ForbiddenException in the case that the user is not allowed to perform the operation.
+ * @throws BadRequestException in the case when the project IRI is missing or invalid.
+ * @throws UpdateNotPerformedException in the case something else went wrong, and the change could not be performed.
+ */
+ private def listNodeInfoChangeRequest(nodeIri: IRI, changeNodeRequest: ChangeListNodeInfoPayloadADM, requestingUser: UserADM, apiRequestID: UUID): Future[ListNodeInfoGetResponseADM] = {
+
+ /**
+ * The actual task run with an IRI lock.
+ */
+ def listNodeInfoChangeTask(nodeIri: IRI, changeNodeRequest: ChangeListNodeInfoPayloadADM, requestingUser: UserADM, apiRequestID: UUID) = for {
+ // check if the requesting user is allowed to perform operation
+ _ <- Future(
+ if (!requestingUser.permissions.isProjectAdmin(changeNodeRequest.projectIri) && !requestingUser.permissions.isSystemAdmin) {
+ // not project or a system admin
+ throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR)
+ }
+ )
+ // check if required information is supplied
+ _ = if (!nodeIri.equals(changeNodeRequest.nodeIri)) throw BadRequestException("Node IRI in path and payload don't match.")
+
+ /* Verify that the node exists. */
+ maybeListNode <- listNodeGetADM(nodeIri = nodeIri, shallow = false, requestingUser = KnoraSystemInstances.Users.SystemUser)
+ rootNodeIri = maybeListNode match {
+ case Some(root: ListRootNodeADM) => root.asInstanceOf[ListRootNodeADM].id
+ case Some(child: ListChildNodeADM) => child.asInstanceOf[ListChildNodeADM].hasRootNode
+ case _ => throw BadRequestException(s"Node '$nodeIri' not found.")
+ }
+
+ /* Get the project information */
+ maybeProject <- (responderManager ? ProjectGetADM(ProjectIdentifierADM(maybeIri = Some(changeNodeRequest.projectIri)), KnoraSystemInstances.Users.SystemUser)).mapTo[Option[ProjectADM]]
+ project: ProjectADM = maybeProject match {
+ case Some(project: ProjectADM) => project
+ case _ => throw BadRequestException(s"Project '${changeNodeRequest.projectIri}' not found.")
+ }
+
+ /* verify that the list node name is unique for the project */
+ _ = if (changeNodeRequest.name.nonEmpty) {
+ val result = listNodeNameIsProjectUnique(changeNodeRequest.projectIri, changeNodeRequest.name.get)
+ if (!Await.result(result, timeout.duration)) throw DuplicateValueException(s"The node name ${changeNodeRequest.name.get} is already used by a list inside the project ${changeNodeRequest.projectIri}.")
+ }
+
+ nodeNameUnique: Boolean <- listNodeNameIsProjectUnique(changeNodeRequest.projectIri, changeNodeRequest.name.flatten)
+ _ = if (!nodeNameUnique) {
+ throw DuplicateValueException(s"The node name ${changeNodeRequest.name.get} is already used by a list inside the project ${changeNodeRequest.projectIri}.")
+ }
+
+ // get the data graph of the project.
+ dataNamedGraph = stringFormatter.projectDataNamedGraphV2(project)
+
+ // Update the list
+ changeListNodeInfoSparqlString = queries.sparql.admin.txt.updateListNodeInfo(
+ dataNamedGraph = dataNamedGraph,
+ triplestore = settings.triplestoreType,
+ nodeIri = nodeIri,
+ nodeClassIri = OntologyConstants.KnoraBase.ListNode,
+ maybeName = changeNodeRequest.name,
+ maybeLabels = changeNodeRequest.labels,
+ maybeComments = changeNodeRequest.comments
+ ).toString
+ _ <- (storeManager ? SparqlUpdateRequest(changeListNodeInfoSparqlString)).mapTo[SparqlUpdateResponse]
+
+ /* Verify that the list was updated */
+ maybeListNodeInfoADM <- listNodeInfoGetADM(nodeIri = nodeIri, requestingUser = KnoraSystemInstances.Users.SystemUser)
+ (updatedNode, name, labels, comments) = maybeListNodeInfoADM match {
+ case Some(root: ListRootNodeInfoADM) => (root.asInstanceOf[ListRootNodeInfoADM], root.name, root.labels, root.comments)
+ case Some(child: ListChildNodeInfoADM) => (child.asInstanceOf[ListChildNodeInfoADM], child.name, child.labels, child.comments)
+ case _ => throw UpdateNotPerformedException(s"Node $nodeIri was not updated. Please report this as a possible bug.")
+ }
+ /* verify name update */
+ _ = if (changeNodeRequest.name.nonEmpty) {
+ if (changeNodeRequest.name.get.nonEmpty) {
+ if (name.nonEmpty) {
+ if (name.get != changeNodeRequest.name.get.get) throw UpdateNotPerformedException(s"Node $nodeIri's 'name' was not updated. Please report this as a possible bug.")
+ }
+ } else {
+ if (name.nonEmpty) {
+ throw UpdateNotPerformedException(s"Node $nodeIri's 'name' was not updated. Please report this as a possible bug.")
+ }
+ }
+ }
+ /* verify label update */
+ _ = if (changeNodeRequest.labels.nonEmpty) {
+ if (labels.stringLiterals.sorted != changeNodeRequest.labels.get.sorted) throw UpdateNotPerformedException(s"Node $nodeIri's 'labels' was not updated. Please report this as a possible bug.")
+ }
+ /* verify comment update */
+ _ = if (changeNodeRequest.comments.nonEmpty) {
+ if (comments.stringLiterals.sorted != changeNodeRequest.comments.get.sorted) throw UpdateNotPerformedException(s"Node $nodeIri's 'comments' was not updated. Please report this as a possible bug.")
+ }
+ } yield ListNodeInfoGetResponseADM(nodeinfo = updatedNode)
+
+ for {
+ // run list info update with an local IRI lock
+ taskResult <- IriLocker.runWithIriLock(
+ apiRequestID,
+ LISTS_GLOBAL_LOCK_IRI,
+ () => listNodeInfoChangeTask(nodeIri, changeNodeRequest, requestingUser, apiRequestID)
+ )
+ } yield taskResult
+ }
+
////////////////////
// Helper Methods //
////////////////////
diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala
index 3eee0bb0dc..6b96e531fc 100644
--- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala
+++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ListsRouteADM.scala
@@ -81,26 +81,35 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
private val ListResponse = classRef(OntologyConstants.KnoraAdminV2.ListResponse.toSmartIri)
private val ListInfoResponse = classRef(OntologyConstants.KnoraAdminV2.ListInfoResponse.toSmartIri)
private val ListNodeInfoResponse = classRef(OntologyConstants.KnoraAdminV2.ListNodeInfoResponse.toSmartIri)
- private val UpdateListInfoRequest = classRef(OntologyConstants.KnoraAdminV2.UpdateListInfoRequest.toSmartIri)
private val CreateChildNodeRequest = classRef(OntologyConstants.KnoraAdminV2.CreateChildNodeRequest.toSmartIri)
+ private val StringLiteral = classRef(OntologyConstants.KnoraAdminV2.StringLiteral.toSmartIri)
private val anythingList = URLEncoder.encode("http://rdfh.ch/lists/0001/treeList", "UTF-8")
private val anythingListNode = URLEncoder.encode("http://rdfh.ch/lists/0001/treeList01", "UTF-8")
/**
* Returns the route.
*/
- override def knoraApiPath: Route = getLists ~ postList ~ getList ~ putList ~ postListChildNode ~ deleteListNode ~
- getListInfo ~ updateListInfo ~ getListNodeInfo
+ /* concatenate paths in the CORRECT order and return */
+ override def knoraApiPath: Route = getLists ~ postList ~ getList ~ putListWithIRI ~ deleteList ~ getListInfo ~
+ putListInfo ~ postListChildNode ~ getListNode ~ putNodeWithIRI ~ deleteListNode ~ getListNodeInfo ~ putNodeInfo
+
+ // -------------------------------------
+ // --------------- LISTS ---------------
+ // -------------------------------------
/* return all lists optionally filtered by project */
- @ApiOperation(value = "Get lists", nickname = "getlists", httpMethod = "GET", response = classOf[ListsGetResponseADM])
+ @ApiOperation(
+ value = "Get all lists optionally filtered by project",
+ nickname = "getlists",
+ httpMethod = "GET",
+ response = classOf[ListsGetResponseADM]
+ )
@ApiResponses(Array(
new ApiResponse(code = 500, message = "Internal server error")
))
/* return all lists optionally filtered by project */
def getLists: Route = path(ListsBasePath) {
get {
- /* return all lists */
parameters("projectIri".?) { maybeProjectIri: Option[IRI] =>
requestContext =>
val projectIri = stringFormatter.toOptionalIri(maybeProjectIri, throw BadRequestException(s"Invalid param project IRI: $maybeProjectIri"))
@@ -148,7 +157,12 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
}
/* create a new list (root node) */
- @ApiOperation(value = "Add new list", nickname = "addList", httpMethod = "POST", response = classOf[ListGetResponseADM])
+ @ApiOperation(
+ value = "Add new list",
+ nickname = "addList",
+ httpMethod = "POST",
+ response = classOf[ListGetResponseADM]
+ )
@ApiImplicitParams(Array(
new ApiImplicitParam(name = "body", value = "\"list\" to create", required = true,
dataTypeClass = classOf[CreateListApiRequestADM], paramType = "body")
@@ -158,7 +172,6 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
))
def postList: Route = path(ListsBasePath) {
post {
- /* create a list */
entity(as[CreateListApiRequestADM]) { apiRequest =>
requestContext =>
val requestMessage: Future[ListCreateRequestADM] = for {
@@ -199,15 +212,19 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
)
}
- /* get a list */
+ /** get a list with all list nodes */
@Path("/{IRI}")
- @ApiOperation(value = "Get a list", nickname = "getlist", httpMethod = "GET", response = classOf[ListGetResponseADM])
+ @ApiOperation(
+ value = "Get a list with all list nodes",
+ nickname = "getList",
+ httpMethod = "GET",
+ response = classOf[ListGetResponseADM]
+ )
@ApiResponses(Array(
new ApiResponse(code = 500, message = "Internal server error")
))
def getList: Route = path(ListsBasePath / Segment) { iri =>
get {
- /* return a list (a graph with all list nodes) */
requestContext =>
val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
@@ -243,11 +260,78 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
)
}
- /**
- * update list
- */
- @Path("/{IRI}")
- @ApiOperation(value = "Update basic list information", nickname = "putList", httpMethod = "PUT", response = classOf[ListInfoGetResponseADM])
+ /** create new list (root node) with given IRI */
+ def putListWithIRI: Route = path(ListsBasePath / Segment) { iri =>
+ put {
+ throw NotImplementedException("Method not implemented.")
+ ???
+ }
+ }
+
+ /** delete list/node which should also delete all children */
+ def deleteList: Route = path(ListsBasePath / Segment) { iri =>
+ delete {
+ throw NotImplementedException("Method not implemented.")
+ ???
+ }
+ }
+
+ @Path("/{IRI}/Info")
+ @ApiOperation(
+ value = "Get basic list information (without children)",
+ nickname = "getListInfo",
+ httpMethod = "GET",
+ response = classOf[ListInfoGetResponseADM]
+ )
+ @ApiResponses(Array(
+ new ApiResponse(code = 500, message = "Internal server error")
+ ))
+ /** return basic information about a list (without children) */
+ def getListInfo: Route = path(ListsBasePath / Segment / "Info") { iri =>
+ get {
+ requestContext =>
+ val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
+
+ val requestMessage: Future[ListInfoGetRequestADM] = for {
+ requestingUser <- getUserADM(requestContext)
+ } yield ListInfoGetRequestADM(listIri, requestingUser)
+
+ RouteUtilADM.runJsonRoute(
+ requestMessage,
+ requestContext,
+ settings,
+ responderManager,
+ log
+ )
+ }
+ }
+
+ private val getListInfoFunction: ClientFunction =
+ "getListInfo" description "Returns information about a list." params (
+ "iri" description "The IRI of the list." paramType UriDatatype
+ ) doThis {
+ httpGet(
+ path = arg("iri") / str("Info")
+ )
+ } returns ListInfoResponse
+
+ private def getListInfoTestResponse: Future[SourceCodeFileContent] = {
+ for {
+ responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/$anythingList/Info") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)))
+ } yield SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("get-list-info-response"),
+ text = responseStr
+ )
+ }
+
+ /** update existing list info */
+ @Path("/{IRI}/Info")
+ @ApiOperation(
+ value = "Update basic list information",
+ nickname = "putListInfo",
+ httpMethod = "PUT",
+ response = classOf[ListInfoGetResponseADM]
+ )
@ApiImplicitParams(Array(
new ApiImplicitParam(name = "body", value = "\"list\" to update", required = true,
dataTypeClass = classOf[ChangeListInfoApiRequestADM], paramType = "body")
@@ -255,18 +339,45 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
@ApiResponses(Array(
new ApiResponse(code = 500, message = "Internal server error")
))
- def putList: Route = path(ListsBasePath / Segment) { iri =>
+ def putListInfo: Route = path(ListsBasePath / Segment / Segment) { (iri, attribute) =>
put {
- /* update existing list node (either root or child) */
entity(as[ChangeListInfoApiRequestADM]) { apiRequest =>
requestContext =>
val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
+ val requestPayload = attribute match {
+ case "ListInfoName" =>
+ ChangeListInfoPayloadADM(
+ listIri = apiRequest.listIri,
+ projectIri = apiRequest.projectIri,
+ name = apiRequest.name.getOrElse(throw BadRequestException("Missing parameter for name")) match {
+ case "" => Some(None)
+ case _ => Some(apiRequest.name)
+ }
+ )
+
+ case "ListInfoLabel" =>
+ ChangeListInfoPayloadADM(
+ listIri = apiRequest.listIri,
+ projectIri = apiRequest.projectIri,
+ labels = apiRequest.labels
+ )
+
+ case "ListInfoComment" =>
+ ChangeListInfoPayloadADM(
+ listIri = apiRequest.listIri,
+ projectIri = apiRequest.projectIri,
+ comments = apiRequest.comments
+ )
+
+ case _ => throw BadRequestException(s"Invalid attribute: $attribute")
+ }
+
val requestMessage: Future[ListInfoChangeRequestADM] = for {
requestingUser <- getUserADM(requestContext)
} yield ListInfoChangeRequestADM(
listIri = listIri,
- changeListRequest = apiRequest,
+ changeListRequest = requestPayload,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)
@@ -282,30 +393,106 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
}
}
- private val updateListInfoFunction: ClientFunction =
- "updateListInfo" description "Updates information about a list." params (
- "listInfo" description "Information about the list to be created." paramType UpdateListInfoRequest
- ) doThis {
+ private val updateListNameFunction: ClientFunction =
+ "updateListName" description "Updates the name of a list." params(
+ "listIri" description "The IRI of the list." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "name" description "The new name of the list." paramType StringDatatype
+ ) doThis {
httpPut(
- path = argMember("listInfo", "listIri"),
- body = Some(arg("listInfo"))
+ path = arg("listIri") / str("ListInfoName"),
+ body = Some(json(
+ "listIri" -> arg("listIri"),
+ "projectIri" -> arg("projectIri"),
+ "name" -> arg("name")
+ ))
)
} returns ListInfoResponse
- private def updateListInfoTestRequest: Future[SourceCodeFileContent] = {
+ private def updateListNameTestRequest: Future[SourceCodeFileContent] = {
FastFuture.successful(
SourceCodeFileContent(
- filePath = SourceCodeFilePath.makeJsonPath("update-list-info-request"),
- text = SharedTestDataADM.updateListInfoRequest("http://rdfh.ch/lists/0001/treeList01")
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-name-request"),
+ text = SharedTestDataADM.updateListNameRequest(
+ listIri = "http://rdfh.ch/lists/0001/treeList",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = "newTestName"
+ )
)
)
}
- /**
- * create a new child node
- */
+ private val updateListLabelsFunction: ClientFunction =
+ "updateListLabels" description "Updates the labels of a list." params(
+ "listIri" description "The IRI of the list." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "labels" description "The new labels of the list." paramType ArrayType(StringLiteral)
+ ) doThis {
+ httpPut(
+ path = arg("listIri") / str("ListInfoLabel"),
+ body = Some(json(
+ "listIri" -> arg("listIri"),
+ "projectIri" -> arg("projectIri"),
+ "labels" -> arg("labels")
+ ))
+ )
+ } returns ListInfoResponse
+
+ private def updateListLabelsTestRequest: Future[SourceCodeFileContent] = {
+ FastFuture.successful(
+ SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-labels-request"),
+ text = SharedTestDataADM.updateListLabelsRequest(
+ listIri = "http://rdfh.ch/lists/0001/treeList",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ labels = SharedTestDataADM.updatedLabels
+ )
+ )
+ )
+ }
+
+ private val updateListCommentsFunction: ClientFunction =
+ "updateListComments" description "Updates the comments of a list." params(
+ "listIri" description "The IRI of the list." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "comments" description "The new comments of the list." paramType ArrayType(StringLiteral)
+ ) doThis {
+ httpPut(
+ path = arg("listIri") / str("ListInfoComment"),
+ body = Some(json(
+ "listIri" -> arg("listIri"),
+ "projectIri" -> arg("projectIri"),
+ "comments" -> arg("comments")
+ ))
+ )
+ } returns ListInfoResponse
+
+ private def updateListCommentsTestRequest: Future[SourceCodeFileContent] = {
+ FastFuture.successful(
+ SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-comments-request"),
+ text = SharedTestDataADM.updateListCommentsRequest(
+ listIri = "http://rdfh.ch/lists/0001/treeList",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = SharedTestDataADM.updatedComments
+ )
+ )
+ )
+ }
+
+ // -------------------------------------
+ // --------------- NODES ---------------
+ // -------------------------------------
+
+
+ /** create a new child node */
@Path("/{IRI}")
- @ApiOperation(value = "Add new child node", nickname = "addListChildNode", httpMethod = "POST", response = classOf[ListNodeInfoGetResponseADM])
+ @ApiOperation(
+ value = "Add new child node",
+ nickname = "addListChildNode",
+ httpMethod = "POST",
+ response = classOf[ListNodeInfoGetResponseADM]
+ )
@ApiImplicitParams(Array(
new ApiImplicitParam(name = "body", value = "\"node\" to create", required = true,
dataTypeClass = classOf[CreateChildNodeApiRequestADM], paramType = "body")
@@ -313,11 +500,12 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
@ApiResponses(Array(
new ApiResponse(code = 500, message = "Internal server error")
))
- def postListChildNode: Route = path(ListsBasePath / Segment) { iri =>
+ def postListChildNode: Route = path(ListsBasePath / "nodes") {
post {
/* add node to existing list node. the existing list node can be either the root or a child */
entity(as[CreateChildNodeApiRequestADM]) { apiRequest =>
requestContext =>
+ val iri = apiRequest.parentNodeIri
val parentNodeIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
val requestMessage: Future[ListChildNodeCreateRequestADM] = for {
@@ -345,7 +533,7 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
"node" description "The node to be created." paramType CreateChildNodeRequest
) doThis {
httpPost(
- path = argMember("node", "parentNodeIri"),
+ path = str("nodes"),
body = Some(arg("node"))
)
} returns ListNodeInfoResponse
@@ -364,24 +552,39 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
)
}
- /* delete list node which should also delete its children */
- def deleteListNode: Route = path(ListsBasePath / Segment) { iri =>
+ /** return node with children */
+ def getListNode: Route = path(ListsBasePath / "nodes" / Segment) { iri =>
+ get {
+ throw NotImplementedException("Method not implemented.")
+ ???
+ }
+ }
+
+ /** create new child node with given IRI */
+ def putNodeWithIRI: Route = path(ListsBasePath / "nodes" / Segment) { iri =>
+ put {
+ throw NotImplementedException("Method not implemented.")
+ ???
+ }
+ }
+
+ /** delete list node with children if not used */
+ def deleteListNode: Route = path(ListsBasePath / "nodes" / Segment) { iri =>
delete {
- /* delete (deactivate) list */
throw NotImplementedException("Method not implemented.")
???
}
}
- def getListInfo: Route = path(ListsBasePath / "infos" / Segment) { iri =>
+ /** return information about a single node (without children) */
+ def getListNodeInfo: Route = path(ListsBasePath / "nodes" / Segment / "Info") { iri =>
get {
- /* return information about a list (without children) */
requestContext =>
- val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
+ val nodeIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list node IRI: $iri"))
- val requestMessage: Future[ListInfoGetRequestADM] = for {
+ val requestMessage: Future[ListNodeInfoGetRequestADM] = for {
requestingUser <- getUserADM(requestContext)
- } yield ListInfoGetRequestADM(listIri, requestingUser)
+ } yield ListNodeInfoGetRequestADM(nodeIri, requestingUser)
RouteUtilADM.runJsonRoute(
requestMessage,
@@ -393,36 +596,64 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
}
}
- private val getListInfoFunction: ClientFunction =
- "getListInfo" description "Returns information about a list." params (
- "iri" description "The IRI of the list." paramType UriDatatype
+ private val getListNodeInfoFunction: ClientFunction =
+ "getListNodeInfo" description "Returns information about a list node." params (
+ "iri" description "The IRI of the node." paramType UriDatatype
) doThis {
httpGet(
- path = str("infos") / arg("iri")
+ path = str("nodes") / arg("iri") / str("Info")
)
- } returns ListInfoResponse
+ } returns ListNodeInfoResponse
- private def getListInfoTestResponse: Future[SourceCodeFileContent] = {
+ private def getListNodeInfoTestResponse: Future[SourceCodeFileContent] = {
for {
- responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/infos/$anythingList") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)))
+ responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/nodes/$anythingListNode/Info") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)))
} yield SourceCodeFileContent(
- filePath = SourceCodeFilePath.makeJsonPath("get-list-info-response"),
+ filePath = SourceCodeFilePath.makeJsonPath("get-list-node-info-response"),
text = responseStr
)
}
- def updateListInfo: Route = path(ListsBasePath / "infos" / Segment) { iri =>
+ /** update list node */
+ def putNodeInfo: Route = path(ListsBasePath / "nodes" / Segment / Segment) { (iri, attribute) =>
put {
- /* update list info */
- entity(as[ChangeListInfoApiRequestADM]) { apiRequest =>
+ entity(as[ChangeListNodeInfoApiRequestADM]) { apiRequest =>
requestContext =>
- val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
-
- val requestMessage: Future[ListInfoChangeRequestADM] = for {
+ val nodeIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param node IRI: $iri"))
+
+ val requestPayload = attribute match {
+ case "NodeInfoName" =>
+ ChangeListNodeInfoPayloadADM(
+ nodeIri = apiRequest.nodeIri,
+ projectIri = apiRequest.projectIri,
+ name = apiRequest.name.getOrElse(throw BadRequestException("Missing parameter for name")) match {
+ case "" => Some(None)
+ case _ => Some(apiRequest.name)
+ }
+ )
+
+ case "NodeInfoLabel" => ChangeListNodeInfoPayloadADM(
+ nodeIri = apiRequest.nodeIri,
+ projectIri = apiRequest.projectIri,
+ labels = apiRequest.labels
+ )
+ case "NodeInfoComment" =>
+ ChangeListNodeInfoPayloadADM(
+ nodeIri = apiRequest.nodeIri,
+ projectIri = apiRequest.projectIri,
+ comments = apiRequest.comments
+ )
+
+ case "NodeInfoPosition" => throw NotImplementedException("Move listnode to new position is not implemented.")
+ case "NodeInfoParent" => throw NotImplementedException("Move listnode to new parent is not implemented.")
+ case _ => throw BadRequestException(s"Invalid attribute: $attribute")
+ }
+
+ val requestMessage: Future[ListNodeInfoChangeRequestADM] = for {
requestingUser <- getUserADM(requestContext)
- } yield ListInfoChangeRequestADM(
- listIri = listIri,
- changeListRequest = apiRequest,
+ } yield ListNodeInfoChangeRequestADM(
+ nodeIri = nodeIri,
+ changeNodeRequest = requestPayload,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)
@@ -438,51 +669,90 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
}
}
- def getListNodeInfo: Route = path(ListsBasePath / "nodes" / Segment) { iri =>
- get {
- /* return information about a single node (without children) */
- requestContext =>
- val listIri = stringFormatter.validateAndEscapeIri(iri, throw BadRequestException(s"Invalid param list IRI: $iri"))
+ private val updateListNodeNameFunction: ClientFunction =
+ "updateListNodeName" description "Updates the name of a list node." params(
+ "nodeIri" description "The IRI of the list node." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "name" description "The new name of the list node." paramType StringDatatype
+ ) doThis {
+ httpPut(
+ path = str("nodes") / arg("nodeIri") / str("NodeInfoName"),
+ body = Some(json(
+ "nodeIri" -> arg("nodeIri"),
+ "projectIri" -> arg("projectIri"),
+ "name" -> arg("name")
+ ))
+ )
+ } returns ListInfoResponse
- val requestMessage: Future[ListNodeInfoGetRequestADM] = for {
- requestingUser <- getUserADM(requestContext)
- } yield ListNodeInfoGetRequestADM(listIri, requestingUser)
+ private def updateListNodeNameTestRequest: Future[SourceCodeFileContent] = {
+ FastFuture.successful(
+ SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-node-name-request"),
+ text = SharedTestDataADM.updateListNodeNameRequest(
+ nodeIri = "http://rdfh.ch/lists/0001/treeList01",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = "newTestName"
+ )
+ )
+ )
+ }
- RouteUtilADM.runJsonRoute(
- requestMessage,
- requestContext,
- settings,
- responderManager,
- log
+ private val updateListNodeLabelsFunction: ClientFunction =
+ "updateListNodeLabels" description "Updates the labels of a list node." params(
+ "nodeIri" description "The IRI of the list node." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "labels" description "The new labels of the list node." paramType ArrayType(StringLiteral)
+ ) doThis {
+ httpPut(
+ path = str("nodes") / arg("nodeIri") / str("NodeInfoLabel"),
+ body = Some(json(
+ "nodeIri" -> arg("nodeIri"),
+ "projectIri" -> arg("projectIri"),
+ "labels" -> arg("labels")
+ ))
+ )
+ } returns ListInfoResponse
+
+ private def updateListNodeLabelsTestRequest: Future[SourceCodeFileContent] = {
+ FastFuture.successful(
+ SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-node-labels-request"),
+ text = SharedTestDataADM.updateListNodeLabelsRequest(
+ nodeIri = "http://rdfh.ch/lists/0001/treeList01",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ labels = SharedTestDataADM.updatedLabels
)
- } ~
- put {
- /* update list node */
- throw NotImplementedException("Method not implemented.")
- ???
- } ~
- delete {
- /* delete list node */
- throw NotImplementedException("Method not implemented.")
- ???
- }
+ )
+ )
}
- private val getListNodeInfoFunction: ClientFunction =
- "getListNodeInfo" description "Returns information about a list node." params (
- "iri" description "The IRI of the node." paramType UriDatatype
- ) doThis {
- httpGet(
- path = str("nodes") / arg("iri")
+ private val updateListNodeCommentsFunction: ClientFunction =
+ "updateListNodeComments" description "Updates the comments of a list node." params(
+ "nodeIri" description "The IRI of the list node." paramType UriDatatype,
+ "projectIri" description "The IRI of the project that the list belongs to." paramType UriDatatype,
+ "comments" description "The new comments of the list node." paramType ArrayType(StringLiteral)
+ ) doThis {
+ httpPut(
+ path = str("nodes") / arg("nodeIri") / str("NodeInfoComment"),
+ body = Some(json(
+ "nodeIri" -> arg("nodeIri"),
+ "projectIri" -> arg("projectIri"),
+ "comments" -> arg("comments")
+ ))
)
- } returns ListNodeInfoResponse
+ } returns ListInfoResponse
- private def getListNodeInfoTestResponse: Future[SourceCodeFileContent] = {
- for {
- responseStr <- doTestDataRequest(Get(s"$baseApiUrl$ListsBasePathString/nodes/$anythingListNode") ~> addCredentials(BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)))
- } yield SourceCodeFileContent(
- filePath = SourceCodeFilePath.makeJsonPath("get-list-node-info-response"),
- text = responseStr
+ private def updateListNodeCommentsTestRequest: Future[SourceCodeFileContent] = {
+ FastFuture.successful(
+ SourceCodeFileContent(
+ filePath = SourceCodeFilePath.makeJsonPath("update-list-node-comments-request"),
+ text = SharedTestDataADM.updateListNodeCommentsRequest(
+ nodeIri = "http://rdfh.ch/lists/0001/treeList01",
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = SharedTestDataADM.updatedComments
+ )
+ )
)
}
@@ -494,10 +764,15 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
getListsInProjectFunction,
createListFunction,
getListFunction,
- updateListInfoFunction,
+ updateListNameFunction,
+ updateListLabelsFunction,
+ updateListCommentsFunction,
createChildNodeFunction,
getListInfoFunction,
- getListNodeInfoFunction
+ getListNodeInfoFunction,
+ updateListNodeNameFunction,
+ updateListNodeLabelsFunction,
+ updateListNodeCommentsFunction
)
/**
@@ -511,10 +786,15 @@ class ListsRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
getListsTestResponse,
createListTestRequest,
getListTestResponse,
- updateListInfoTestRequest,
+ updateListNameTestRequest,
+ updateListLabelsTestRequest,
+ updateListCommentsTestRequest,
createChildNodeTestRequest,
getListInfoTestResponse,
- getListNodeInfoTestResponse
+ getListNodeInfoTestResponse,
+ updateListNodeNameTestRequest,
+ updateListNodeLabelsTestRequest,
+ updateListNodeCommentsTestRequest
)
}
}
diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala
index afbbcadfb8..9d7cab5af3 100644
--- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala
+++ b/webapi/src/main/scala/org/knora/webapi/util/clientapi/ClientApi.scala
@@ -213,8 +213,8 @@ trait ClientEndpoint {
val paramClasses: Set[SmartIri] = function.params.map {
param => param.objectType
}.collect {
- case classRef: ClassRef => classRef.classIri
- }.toSet
+ case typeWithClassIri: TypeWithClassIri => typeWithClassIri.getClassIri
+ }.flatten.toSet
paramClasses ++ maybeReturnedClass
}.toSet
@@ -337,7 +337,7 @@ object EndpointFunctionDSL {
* @param name the name of the argument.
* @return an [[ArgValue]].
*/
- def arg(name: String) = ArgValue(name)
+ def arg(name: String): ArgValue = ArgValue(name)
/**
* Constructs an [[ArgValue]] referring to a member of a function argument.
@@ -346,7 +346,7 @@ object EndpointFunctionDSL {
* @param member the name of the member of the argument.
* @return an [[ArgValue]].
*/
- def argMember(name: String, member: String) = ArgValue(name = name, memberVariableName = Some(member))
+ def argMember(name: String, member: String): ArgValue = ArgValue(name = name, memberVariableName = Some(member))
/**
* A URL path representing the base path of an endpoint.
diff --git a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala b/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala
index a5e6fc7061..bb102bc1f3 100644
--- a/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala
+++ b/webapi/src/main/scala/org/knora/webapi/util/clientapi/GeneratorFrontEnd.scala
@@ -70,7 +70,7 @@ class GeneratorFrontEnd(routeData: KnoraRouteData, requestingUser: UserADM) {
def getClassDefsRec(classIri: SmartIri, definitionAcc: ClientDefsWithOntologies): Future[ClientDefsWithOntologies] = {
val className = classIri.getEntityName
- // Is this the IRI of a derived class that hasn't been generated yet?
+ // Is this is the IRI of a derived class that hasn't been generated yet?
if (ClassRef.isGeneratedDerivedClassName(className)) {
// Yes. Do we already have the definition of the base class?
@@ -84,7 +84,7 @@ class GeneratorFrontEnd(routeData: KnoraRouteData, requestingUser: UserADM) {
getClassDefsRec(baseClassIri, definitionAcc)
}
} else {
- // Get the IRI of the ontology containing the class.
+ // No, this is the IRI of a class defined in an ontology. Get the IRI of the ontology containing the class.
val classOntologyIri: SmartIri = classIri.getOntologyFromEntity
for {
diff --git a/webapi/src/main/twirl/queries/sparql/admin/updateListInfo.scala.txt b/webapi/src/main/twirl/queries/sparql/admin/updateListInfo.scala.txt
index b3ec1ee0c2..61c7539a33 100644
--- a/webapi/src/main/twirl/queries/sparql/admin/updateListInfo.scala.txt
+++ b/webapi/src/main/twirl/queries/sparql/admin/updateListInfo.scala.txt
@@ -29,6 +29,7 @@
* @param listIri the IRI of the list we want to update.
* @param projectIri the IRI of the list's project.
* @param listClassIri the IRI of the OWL class that the list should belong to.
+ * @param maybeName the new optional name value
* @param maybelabels the new optional label values.
* @param maybeComments the new optional comment values.
*@
@@ -37,8 +38,9 @@
listIri: IRI,
projectIri: IRI,
listClassIri: IRI,
- maybeLabels: Seq[StringLiteralV2],
- maybeComments: Seq[StringLiteralV2])
+ maybeName: Option[Option[String]],
+ maybeLabels: Option[Seq[StringLiteralV2]],
+ maybeComments: Option[Seq[StringLiteralV2]])
prefix rdf:
prefix rdfs:
@@ -50,8 +52,12 @@ DELETE {
@* Delete current values, for which we have a new one. *@
+ @if(maybeName.nonEmpty) {
+ ?listIri knora-base:listNodeName ?currentListNodeName .
+ }
+
@if(maybeLabels.nonEmpty) {
- ?listIri rdfs:label ?currentLabels .
+ ?listIri rdfs:label ?currentLabels .
}
@if(maybeComments.nonEmpty) {
@@ -62,8 +68,14 @@ DELETE {
@* Add the new values. *@
+ @if(maybeName.nonEmpty) {
+ @if(maybeName.get.nonEmpty) {
+ ?listIri knora-base:listNodeName "@maybeName.get.get"^^xsd:string .
+ }
+ }
+
@if(maybeLabels.nonEmpty) {
- @for(label <- maybeLabels) {
+ @for(label <- maybeLabels.get) {
@if(label.language.nonEmpty) {
?listIri rdfs:label """@label.value"""@@@{label.language.get} .
} else {
@@ -73,7 +85,7 @@ DELETE {
}
@if(maybeComments.nonEmpty) {
- @for(comment <- maybeComments) {
+ @for(comment <- maybeComments.get) {
@if(comment.language.nonEmpty) {
?listIri rdfs:comment """@comment.value"""@@@{comment.language.get} .
} else {
@@ -109,6 +121,8 @@ WHERE {
?listIri knora-base:attachedToProject ?projectIri .
+ optional {?listIri knora-base:listNodeName ?currentListNodeName .}
+
optional {?listIri rdfs:label ?currentLabels .}
optional {?listIri rdfs:comment ?currentComments .}
diff --git a/webapi/src/main/twirl/queries/sparql/admin/updateListNodeInfo.scala.txt b/webapi/src/main/twirl/queries/sparql/admin/updateListNodeInfo.scala.txt
new file mode 100644
index 0000000000..bd70871f10
--- /dev/null
+++ b/webapi/src/main/twirl/queries/sparql/admin/updateListNodeInfo.scala.txt
@@ -0,0 +1,125 @@
+@*
+ * Copyright © 2015-2019 the contributors (see Contributors.md).
+ *
+ * This file is part of Knora.
+ *
+ * Knora is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Knora is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with Knora. If not, see .
+ *@
+
+@import org.knora.webapi.IRI
+@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
+
+@**
+ * Updates an existing listnode with the provided values.
+ *
+ * @param dataNamedGraph the named graph to update.
+ * @param triplestore the name of the triplestore being used. The template uses this value to exclude inferred
+ results from the WHERE clause of the update.
+ * @param nodeIri the IRI of the listnode we want to update.
+ * @param nodeClassIri the IRI of the OWL class that the node should belong to.
+ * @param maybeName the new optional name value
+ * @param maybelabels the new optional label values.
+ * @param maybeComments the new optional comment values.
+ *@
+@(dataNamedGraph: IRI,
+ triplestore: String,
+ nodeIri: IRI,
+ nodeClassIri: IRI,
+ maybeName: Option[Option[String]],
+ maybeLabels: Option[Seq[StringLiteralV2]],
+ maybeComments: Option[Seq[StringLiteralV2]])
+
+prefix rdf:
+prefix rdfs:
+prefix xsd:
+prefix knora-base:
+
+WITH <@dataNamedGraph>
+DELETE {
+
+ @* Delete current values, for which we have a new one. *@
+
+ @if(maybeName.nonEmpty) {
+ ?nodeIri knora-base:listNodeName ?currentListNodeName .
+ }
+
+ @if(maybeLabels.nonEmpty) {
+ ?nodeIri rdfs:label ?currentLabels .
+ }
+
+ @if(maybeComments.nonEmpty) {
+ ?nodeIri rdfs:comment ?currentComments .
+ }
+
+} INSERT {
+
+ @* Add the new values. *@
+
+ @if(maybeName.nonEmpty) {
+ @if(maybeName.get.nonEmpty) {
+ ?nodeIri knora-base:listNodeName "@maybeName.get.get"^^xsd:string .
+ }
+ }
+
+ @if(maybeLabels.nonEmpty) {
+ @for(label <- maybeLabels.get) {
+ @if(label.language.nonEmpty) {
+ ?nodeIri rdfs:label """@label.value"""@@@{label.language.get} .
+ } else {
+ ?nodeIri rdfs:label """@label.value"""^^xsd:string .
+ }
+ }
+ }
+
+ @if(maybeComments.nonEmpty) {
+ @for(comment <- maybeComments.get) {
+ @if(comment.language.nonEmpty) {
+ ?nodeIri rdfs:comment """@comment.value"""@@@{comment.language.get} .
+ } else {
+ ?nodeIri rdfs:comment """@comment.value"""^^xsd:string .
+ }
+ }
+ }
+}
+
+@*
+
+GraphDB's consistency checking requires reasoning, but reasoning interferes with certain things
+in the WHERE clauses of our SPARQL updates, so we set a GraphDB-specific flag to return only
+explicit statements in the WHERE clause here.
+
+*@
+
+@triplestore match {
+ case "graphdb" | "graphdb-free" => {
+ USING
+ }
+
+ case other => {}
+}
+
+WHERE {
+ BIND(IRI("@nodeIri") AS ?nodeIri)
+
+ @* Get all current defined values. *@
+
+ ?nodeIri rdf:type knora-base:ListNode .
+
+ optional {?nodeIri knora-base:listNodeName ?currentListNodeName .}
+
+ optional {?nodeIri rdfs:label ?currentLabels .}
+
+ optional {?nodeIri rdfs:comment ?currentComments .}
+
+}
diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ListsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ListsADME2ESpec.scala
index 3c50dd7b10..bf46bedc39 100644
--- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ListsADME2ESpec.scala
+++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ListsADME2ESpec.scala
@@ -30,7 +30,7 @@ import org.knora.webapi.messages.v1.responder.sessionmessages.SessionJsonProtoco
import org.knora.webapi.messages.v1.routing.authenticationmessages.CredentialsADM
import org.knora.webapi.testing.tags.E2ETest
import org.knora.webapi.util.{AkkaHttpUtils, MutableTestIri}
-import org.knora.webapi.{E2ESpec, SharedListsTestDataADM, SharedTestDataADM, SharedTestDataV1}
+import org.knora.webapi.{E2ESpec, SharedListsTestDataADM, SharedTestDataADM}
import scala.concurrent.duration._
@@ -78,7 +78,12 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
private val treeListInfo: ListRootNodeInfoADM = SharedListsTestDataADM.treeListInfo
private val treeListNodes: Seq[ListChildNodeADM] = SharedListsTestDataADM.treeListChildNodes
- "The Lists Route (/admin/lists)" when {
+ val newListIri = new MutableTestIri
+ val firstChildIri = new MutableTestIri
+ val secondChildIri = new MutableTestIri
+ val thirdChildIri = new MutableTestIri
+
+ "The Lists Route ('/admin/lists')" when {
"used to query information about lists" should {
@@ -126,7 +131,7 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
}
"return basic list information" in {
- val request = Get(baseApiUrl + s"/admin/lists/infos/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList") ~> addCredentials(rootCreds.basicHttpCredentials)
+ val request = Get(baseApiUrl + s"/admin/lists/http%3A%2F%2Frdfh.ch%2Flists%2F0001%2FtreeList/Info") ~> addCredentials(rootCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
// log.debug(s"response: ${response.toString}")
@@ -154,14 +159,10 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
"used to modify list information" should {
- val newListIri = new MutableTestIri
- val firstChildIri = new MutableTestIri
- val secondChildIri = new MutableTestIri
- val thirdChildIri = new MutableTestIri
-
"create a list" in {
- val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, SharedTestDataADM.createListRequest)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val params = SharedTestDataADM.createListRequest
+ val request = Post(baseApiUrl + s"/admin/lists", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
// log.debug(s"response: ${response.toString}")
response.status should be(StatusCodes.OK)
@@ -171,6 +172,9 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
val listInfo = receivedList.listinfo
listInfo.projectIri should be (SharedTestDataADM.ANYTHING_PROJECT_IRI)
+ val name = listInfo.name
+ name should be (Some("newList"))
+
val labels: Seq[StringLiteralV2] = listInfo.labels.stringLiterals
labels.size should be (1)
labels.head should be (StringLiteralV2(value = "Neue Liste", language = Some("de")))
@@ -252,13 +256,66 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
}
- "update basic list information" in {
- val params = SharedTestDataADM.updateListInfoRequest(newListIri.get)
- val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+ "return a BadRequestException during list change when the provided name is already in use" in {
+ val params =
+ s"""
+ |{
+ | "listIri": "${newListIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newList"
+ |}
+ """.stripMargin
+
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
- val request = Put(baseApiUrl + s"/admin/lists/infos/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val request = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response: HttpResponse = singleAwaitingRequest(request)
+ response.status should be(StatusCodes.BadRequest)
+ }
+
+ "update basic list information: name" in {
+ val paramsUpdate = SharedTestDataADM.updateListNameRequest(
+ listIri = newListIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = "newTestName"
+ )
+
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+
+ val requestUpdate = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, paramsUpdate)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseUpdate: HttpResponse = singleAwaitingRequest(requestUpdate)
+ responseUpdate.status should be(StatusCodes.OK)
+
+ val receivedListInfoUpdate: ListRootNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseUpdate).fields("listinfo").convertTo[ListRootNodeInfoADM]
+ receivedListInfoUpdate.projectIri should be (SharedTestDataADM.ANYTHING_PROJECT_IRI)
+ receivedListInfoUpdate.name should be (Some("newTestName"))
+
+ val paramsDelete = SharedTestDataADM.updateListNameRequest(
+ listIri = newListIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = ""
+ )
+
+ val requestDelete = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, paramsDelete)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseDelete: HttpResponse = singleAwaitingRequest(requestDelete)
+ responseDelete.status should be(StatusCodes.OK)
+
+ val receivedListInfoDelete: ListRootNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseDelete).fields("listinfo").convertTo[ListRootNodeInfoADM]
+ receivedListInfoDelete.projectIri should be (SharedTestDataADM.ANYTHING_PROJECT_IRI)
+ receivedListInfoDelete.name.isEmpty should be (true)
+ }
+
+ "update basic list information: label" in {
+ val params = SharedTestDataADM.updateListLabelsRequest(
+ listIri = newListIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ labels = SharedTestDataADM.updatedLabels
+ )
+
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+
+ val request = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoLabel", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
- // log.debug(s"response: ${response.toString}")
response.status should be(StatusCodes.OK)
val receivedListInfo: ListRootNodeInfoADM = AkkaHttpUtils.httpResponseToJson(response).fields("listinfo").convertTo[ListRootNodeInfoADM]
@@ -267,9 +324,42 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
val labels: Seq[StringLiteralV2] = receivedListInfo.labels.stringLiterals
labels.size should be (2)
+ }
+
+ "update basic list information: comment" in {
+ val paramsUpdate = SharedTestDataADM.updateListCommentsRequest(
+ listIri = newListIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = SharedTestDataADM.updatedComments
+ )
+
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
- val comments = receivedListInfo.comments.stringLiterals
- comments.size should be (2)
+ val requestUpdate = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoComment", HttpEntity(ContentTypes.`application/json`, paramsUpdate)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseUpdate: HttpResponse = singleAwaitingRequest(requestUpdate)
+ responseUpdate.status should be(StatusCodes.OK)
+
+ val receivedListInfoComment: ListRootNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseUpdate).fields("listinfo").convertTo[ListRootNodeInfoADM]
+ receivedListInfoComment.projectIri should be (SharedTestDataADM.ANYTHING_PROJECT_IRI)
+
+ val commentsUpdate = receivedListInfoComment.comments.stringLiterals
+ commentsUpdate.size should be (2)
+
+ val paramsDelete = SharedTestDataADM.updateListCommentsRequest(
+ listIri = newListIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = Vector.empty
+ )
+
+ val requestDelete = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoComment", HttpEntity(ContentTypes.`application/json`, paramsDelete)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseDelete: HttpResponse = singleAwaitingRequest(requestDelete)
+ responseDelete.status should be(StatusCodes.OK)
+
+ val receivedListInfoDelete: ListRootNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseDelete).fields("listinfo").convertTo[ListRootNodeInfoADM]
+ receivedListInfoDelete.projectIri should be (SharedTestDataADM.ANYTHING_PROJECT_IRI)
+
+ val commentsDelete = receivedListInfoDelete.comments.stringLiterals
+ commentsDelete.size should be (0)
}
"return a ForbiddenException if the user updating the list is not project or system admin" in {
@@ -278,22 +368,33 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
|{
| "listIri": "${newListIri.get}",
| "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
""".stripMargin
- val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
- val request = Put(baseApiUrl + s"/admin/lists/infos/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
- val response: HttpResponse = singleAwaitingRequest(request)
- // log.debug(s"response: ${response.toString}")
- response.status should be(StatusCodes.Forbidden)
+ val requestName = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseName: HttpResponse = singleAwaitingRequest(requestName)
+
+ responseName.status should be(StatusCodes.Forbidden)
+
+ val requestLabel = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoLabel", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseLabel: HttpResponse = singleAwaitingRequest(requestLabel)
+
+ responseLabel.status should be(StatusCodes.Forbidden)
+
+ val requestComment = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoComment", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseComment: HttpResponse = singleAwaitingRequest(requestComment)
+
+ responseComment.status should be(StatusCodes.Forbidden)
}
"return a BadRequestException during list change when payload is not correct" in {
- val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+ val encodedListIri = java.net.URLEncoder.encode(newListIri.get, "utf-8")
// empty list IRI
val params01 =
@@ -301,15 +402,21 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
|{
| "listIri": "",
| "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
""".stripMargin
- val request01 = Put(baseApiUrl + s"/admin/lists/infos/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
- val response01: HttpResponse = singleAwaitingRequest(request01)
- // log.debug(s"response: ${response.toString}")
- response01.status should be(StatusCodes.BadRequest)
+ val request01Name = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Name: HttpResponse = singleAwaitingRequest(request01Name)
+ response01Name.status should be(StatusCodes.BadRequest)
+ val request01Label = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoLabel", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Label: HttpResponse = singleAwaitingRequest(request01Label)
+ response01Label.status should be(StatusCodes.BadRequest)
+ val request01Comment = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoComment", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Comment: HttpResponse = singleAwaitingRequest(request01Comment)
+ response01Comment.status should be(StatusCodes.BadRequest)
// empty project
val params02 =
@@ -317,52 +424,77 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
|{
| "listIri": "${newListIri.get}",
| "projectIri": "",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
""".stripMargin
- val request02 = Put(baseApiUrl + s"/admin/lists/infos/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
- val response02: HttpResponse = singleAwaitingRequest(request02)
- // log.debug(s"response: ${response.toString}")
- response02.status should be(StatusCodes.BadRequest)
-
- // empty parameters
+ val request02Name = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoName", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Name: HttpResponse = singleAwaitingRequest(request02Name)
+ response02Name.status should be(StatusCodes.BadRequest)
+ val request02Label = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoLabel", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Label: HttpResponse = singleAwaitingRequest(request02Label)
+ response02Label.status should be(StatusCodes.BadRequest)
+ val request02Comment = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoComment", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Comment: HttpResponse = singleAwaitingRequest(request02Comment)
+ response02Comment.status should be(StatusCodes.BadRequest)
+
+ // empty parameters (at least one label must be supplied)
val params03 =
s"""
|{
| "listIri": "${newListIri.get}",
| "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newTestName",
| "labels": [],
| "comments": []
|}
""".stripMargin
- val request03 = Put(baseApiUrl + s"/admin/lists/infos/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params03)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
- val response03: HttpResponse = singleAwaitingRequest(request03)
- // log.debug(s"response: ${response.toString}")
- response03.status should be(StatusCodes.BadRequest)
+ val request03Label = Put(baseApiUrl + s"/admin/lists/" + encodedListIri + "/ListInfoLabel", HttpEntity(ContentTypes.`application/json`, params03)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response03Label: HttpResponse = singleAwaitingRequest(request03Label)
+ response03Label.status should be(StatusCodes.BadRequest)
+ }
+ }
+ }
+
+ "The Nodes Route ('/admin/lists/nodes')" when {
+
+ "used to query information about nodes" should {
+
+ "return a list" ignore {
}
- "add child to list - to the root node" in {
+ "return a sublist" ignore {
- val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8")
+ }
- val name = "first"
- val label = "New First Child List Node Value"
- val comment = "New First Child List Node Comment"
+ "return basic node information" ignore {
- val params = SharedTestDataADM.addChildListNodeRequest(
- parentNodeIri = newListIri.get,
- name = name,
- label = label,
- comment = comment
- )
+ }
+
+ }
+
+ "used to modify node information" should {
+
+ "add child to list - to the root node" in {
- val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val params =
+ s"""
+ |{
+ | "parentNodeIri": "${newListIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first",
+ | "labels": [{ "value": "New First Child List Node Value", "language": "en"}],
+ | "comments": [{ "value": "New First Child List Node Comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val request = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
- // println(s"response: ${response.toString}")
+ //println(s"response: ${response.toString}")
response.status should be(StatusCodes.OK)
val received: ListNodeInfoADM = AkkaHttpUtils.httpResponseToJson(response).fields("nodeinfo").convertTo[ListNodeInfoADM]
@@ -376,12 +508,12 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
// check labels
val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals
labels.size should be (1)
- labels.sorted should be (Seq(StringLiteralV2(value = label, language = Some("en"))))
+ labels.sorted should be (Seq(StringLiteralV2(value = "New First Child List Node Value", language = Some("en"))))
// check comments
val comments = childNodeInfo.comments.stringLiterals
comments.size should be (1)
- comments.sorted should be (Seq(StringLiteralV2(value = comment, language = Some("en"))))
+ comments.sorted should be (Seq(StringLiteralV2(value = "New First Child List Node Comment", language = Some("en"))))
// check position
val position = childNodeInfo.position
@@ -396,20 +528,18 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
"add second child to list - to the root node" in {
- val encodedListUrl = java.net.URLEncoder.encode(newListIri.get, "utf-8")
-
- val name = "second"
- val label = "New Second Child List Node Value"
- val comment = "New Second Child List Node Comment"
-
- val params = SharedTestDataADM.addChildListNodeRequest(
- parentNodeIri = newListIri.get,
- name = name,
- label = label,
- comment = comment
- )
+ val params =
+ s"""
+ |{
+ | "parentNodeIri": "${newListIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "second",
+ | "labels": [{ "value": "New Second Child List Node Value", "language": "en"}],
+ | "comments": [{ "value": "New Second Child List Node Comment", "language": "en"}]
+ |}
+ """.stripMargin
- val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val request = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
// println(s"response: ${response.toString}")
response.status should be(StatusCodes.OK)
@@ -425,12 +555,12 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
// check labels
val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals
labels.size should be (1)
- labels.sorted should be (Seq(StringLiteralV2(value = label, language = Some("en"))))
+ labels.sorted should be (Seq(StringLiteralV2(value = "New Second Child List Node Value", language = Some("en"))))
// check comments
val comments = childNodeInfo.comments.stringLiterals
comments.size should be (1)
- comments.sorted should be (Seq(StringLiteralV2(value = comment, language = Some("en"))))
+ comments.sorted should be (Seq(StringLiteralV2(value = "New Second Child List Node Comment", language = Some("en"))))
// check position
val position = childNodeInfo.position
@@ -446,20 +576,18 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
"add child to second child node" in {
- val encodedListUrl = java.net.URLEncoder.encode(secondChildIri.get, "utf-8")
-
- val name = "third"
- val label = "New Third Child List Node Value"
- val comment = "New Third Child List Node Comment"
-
- val params = SharedTestDataADM.addChildListNodeRequest(
- parentNodeIri = secondChildIri.get,
- name = name,
- label = label,
- comment = comment
- )
+ val params =
+ s"""
+ |{
+ | "parentNodeIri": "${secondChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "third",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": [{ "value": "New Child List Node Comment", "language": "en"}]
+ |}
+ """.stripMargin
- val request = Post(baseApiUrl + s"/admin/lists/" + encodedListUrl, HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val request = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
val response: HttpResponse = singleAwaitingRequest(request)
// println(s"response: ${response.toString}")
response.status should be(StatusCodes.OK)
@@ -475,12 +603,12 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
// check labels
val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals
labels.size should be (1)
- labels.sorted should be (Seq(StringLiteralV2(value = label, language = Some("en"))))
+ labels.sorted should be (Seq(StringLiteralV2(value = "New Child List Node Value", language = Some("en"))))
// check comments
val comments = childNodeInfo.comments.stringLiterals
comments.size should be (1)
- comments.sorted should be (Seq(StringLiteralV2(value = comment, language = Some("en"))))
+ comments.sorted should be (Seq(StringLiteralV2(value = "New Child List Node Comment", language = Some("en"))))
// check position
val position = childNodeInfo.position
@@ -493,6 +621,300 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
thirdChildIri.set(childNodeInfo.id)
}
+ "return a 'ForbiddenException' if the user creating the node is not project or system admin" in {
+ val params =
+ s"""
+ |{
+ | "parentNodeIri": "${secondChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val response: HttpResponse = singleAwaitingRequest(request)
+ response.status should be(StatusCodes.Forbidden)
+ }
+
+ "return a BadRequestException during node creation when payload is not correct" in {
+ // no parentNode IRI
+ val params00 =
+ s"""
+ |{
+ | "parentNodeIri": "",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request00 = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params00)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response00: HttpResponse = singleAwaitingRequest(request00)
+ response00.status should be(StatusCodes.BadRequest)
+
+
+ // invalid parentNode IRI
+ val params01 =
+ s"""
+ |{
+ | "parentNodeIri": "invalidIRI",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request01 = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01: HttpResponse = singleAwaitingRequest(request01)
+ response01.status should be(StatusCodes.BadRequest)
+
+
+ // no project IRI
+ val params02 =
+ s"""
+ |{
+ | "parentNodeIri": "${secondChildIri.get}",
+ | "projectIri": "",
+ | "name": "first",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request02 = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02: HttpResponse = singleAwaitingRequest(request02)
+ response02.status should be(StatusCodes.BadRequest)
+
+
+ // invalid project IRI
+ val params03 =
+ s"""
+ |{
+ | "parentNodeIri": "${secondChildIri.get}",
+ | "projectIri": "invalidIRI",
+ | "name": "first",
+ | "labels": [{ "value": "New Child List Node Value", "language": "en"}],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request03 = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params03)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response03: HttpResponse = singleAwaitingRequest(request03)
+ response03.status should be(StatusCodes.BadRequest)
+
+
+ // missing label
+ val params05 =
+ s"""
+ |{
+ | "parentNodeIri": "${secondChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first",
+ | "labels": [],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request05 = Post(baseApiUrl + s"/admin/lists/nodes", HttpEntity(ContentTypes.`application/json`, params05)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response05: HttpResponse = singleAwaitingRequest(request05)
+ // println(s"response: ${response03.toString}")
+ response05.status should be(StatusCodes.BadRequest)
+
+ }
+
+ "return a BadRequestException during list node change when the provided name is already in use" in {
+ val params =
+ s"""
+ |{
+ | "nodeIri": "${firstChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "first"
+ |}
+ """.stripMargin
+
+ val encodedListIri = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ val request = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListIri + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response: HttpResponse = singleAwaitingRequest(request)
+ response.status should be(StatusCodes.BadRequest)
+ }
+
+ "update basic list node information: name" in {
+ val paramsUpdate = SharedTestDataADM.updateListNodeNameRequest(
+ nodeIri = firstChildIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = "newTestName"
+ )
+
+ val encodedListNodeUrl = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ val requestUpdate = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, paramsUpdate)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseUpdate: HttpResponse = singleAwaitingRequest(requestUpdate)
+ responseUpdate.status should be(StatusCodes.OK)
+
+ val receivedListNodeInfoUpdate: ListChildNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseUpdate).fields("nodeinfo").convertTo[ListChildNodeInfoADM]
+ receivedListNodeInfoUpdate.name should be (Some("newTestName"))
+
+ val paramsDelete = SharedTestDataADM.updateListNodeNameRequest(
+ nodeIri = firstChildIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ name = ""
+ )
+
+ val requestDelete = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, paramsDelete)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseDelete: HttpResponse = singleAwaitingRequest(requestDelete)
+ responseDelete.status should be(StatusCodes.OK)
+
+ val receivedListNodeInfoDelete: ListChildNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseDelete).fields("nodeinfo").convertTo[ListChildNodeInfoADM]
+ receivedListNodeInfoDelete.name.isEmpty should be (true)
+ }
+
+ "update basic list node information: label" in {
+ val params = SharedTestDataADM.updateListNodeLabelsRequest(
+ nodeIri = firstChildIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ labels = SharedTestDataADM.updatedLabels
+ )
+
+ val encodedListNodeUrl = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ val request = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoLabel", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response: HttpResponse = singleAwaitingRequest(request)
+ response.status should be(StatusCodes.OK)
+
+ val receivedListNodeInfo: ListChildNodeInfoADM = AkkaHttpUtils.httpResponseToJson(response).fields("nodeinfo").convertTo[ListChildNodeInfoADM]
+
+ val labels: Seq[StringLiteralV2] = receivedListNodeInfo.labels.stringLiterals
+ labels.size should be (2)
+ }
+
+ "update basic list node information: comment" in {
+ val paramsUpdate = SharedTestDataADM.updateListNodeCommentsRequest(
+ nodeIri = firstChildIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = SharedTestDataADM.updatedComments
+ )
+
+ val encodedListNodeUrl = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ val requestUpdate = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoComment", HttpEntity(ContentTypes.`application/json`, paramsUpdate)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseUpdate: HttpResponse = singleAwaitingRequest(requestUpdate)
+ responseUpdate.status should be(StatusCodes.OK)
+
+ val receivedListInfoComment: ListChildNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseUpdate).fields("nodeinfo").convertTo[ListChildNodeInfoADM]
+ val commentsUpdate = receivedListInfoComment.comments.stringLiterals
+ commentsUpdate.size should be (2)
+
+ val paramsDelete = SharedTestDataADM.updateListNodeCommentsRequest(
+ nodeIri = firstChildIri.get,
+ projectIri = SharedTestDataADM.ANYTHING_PROJECT_IRI,
+ comments = Vector.empty
+ )
+
+ val requestDelete = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoComment", HttpEntity(ContentTypes.`application/json`, paramsDelete)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val responseDelete: HttpResponse = singleAwaitingRequest(requestDelete)
+ responseDelete.status should be(StatusCodes.OK)
+
+ val receivedListInfoDelete: ListChildNodeInfoADM = AkkaHttpUtils.httpResponseToJson(responseDelete).fields("nodeinfo").convertTo[ListChildNodeInfoADM]
+ val commentsDelete = receivedListInfoDelete.comments.stringLiterals
+ commentsDelete.size should be (0)
+ }
+
+ "return a 'ForbiddenException' if the user updating the list node is not project or system admin" in {
+ val params =
+ s"""
+ |{
+ | "nodeIri": "${firstChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val encodedListNodeUrl = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ val requestName = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseName: HttpResponse = singleAwaitingRequest(requestName)
+ responseName.status should be(StatusCodes.Forbidden)
+
+ val requestLabel = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoLabel", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseLabel: HttpResponse = singleAwaitingRequest(requestLabel)
+ responseLabel.status should be(StatusCodes.Forbidden)
+
+ val requestComment = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoComment", HttpEntity(ContentTypes.`application/json`, params)) ~> addCredentials(anythingUserCreds.basicHttpCredentials)
+ val responseComment: HttpResponse = singleAwaitingRequest(requestComment)
+ responseComment.status should be(StatusCodes.Forbidden)
+ }
+
+ "return a BadRequestException during list node change when payload is not correct" in {
+ val encodedListNodeUrl = java.net.URLEncoder.encode(firstChildIri.get, "utf-8")
+
+ // empty node IRI
+ val params01 =
+ s"""
+ |{
+ | "nodeIri": "",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val request01Name = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Name: HttpResponse = singleAwaitingRequest(request01Name)
+ response01Name.status should be(StatusCodes.BadRequest)
+ val request01Label = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoLabel", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Label: HttpResponse = singleAwaitingRequest(request01Label)
+ response01Label.status should be(StatusCodes.BadRequest)
+ val request01Comment = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoComment", HttpEntity(ContentTypes.`application/json`, params01)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response01Comment: HttpResponse = singleAwaitingRequest(request01Comment)
+ response01Comment.status should be(StatusCodes.BadRequest)
+
+ // empty project
+ val params02 =
+ s"""
+ |{
+ | "nodeIri": "${firstChildIri.get}",
+ | "projectIri": "",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val request02Name = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoName", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Name: HttpResponse = singleAwaitingRequest(request02Name)
+ response02Name.status should be(StatusCodes.BadRequest)
+ val request02Label = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoLabel", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Label: HttpResponse = singleAwaitingRequest(request02Label)
+ response02Label.status should be(StatusCodes.BadRequest)
+ val request02Comment = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoComment", HttpEntity(ContentTypes.`application/json`, params02)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response02Comment: HttpResponse = singleAwaitingRequest(request02Comment)
+ response02Comment.status should be(StatusCodes.BadRequest)
+
+ // empty parameters (at least one label must be supplied)
+ val params03 =
+ s"""
+ |{
+ | "nodeIri": "${firstChildIri.get}",
+ | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}",
+ | "name": "",
+ | "labels": [],
+ | "comments": []
+ |}
+ """.stripMargin
+
+ val request03Label = Put(baseApiUrl + s"/admin/lists/nodes/" + encodedListNodeUrl + "/NodeInfoLabel", HttpEntity(ContentTypes.`application/json`, params03)) ~> addCredentials(anythingAdminUserCreds.basicHttpCredentials)
+ val response03Label: HttpResponse = singleAwaitingRequest(request03Label)
+ response03Label.status should be(StatusCodes.BadRequest)
+ }
+
"add flat nodes" ignore {
}
@@ -508,7 +930,6 @@ class ListsADME2ESpec extends E2ESpec(ListsADME2ESpec.config) with SessionJsonPr
"delete node if not in use" ignore {
}
-
}
}
}
diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala
index dc8abc4361..864e9cb4ba 100644
--- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala
+++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala
@@ -166,6 +166,7 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
|{
| "listIri": "",
| "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
@@ -183,6 +184,7 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
|{
| "listIri": "notvalidIRI",
| "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
@@ -200,6 +202,7 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
|{
| "listIri": "$exampleListIri",
| "projectIri": "",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
@@ -217,6 +220,7 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
|{
| "listIri": "$exampleListIri",
| "projectIri": "notvalidIRI",
+ | "name": "newTestName",
| "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
| "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
|}
@@ -227,24 +231,6 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
thrown.getMessage should equal (PROJECT_IRI_INVALID_ERROR)
}
- "throw 'BadRequestException' for `ChangeListInfoApiRequestADM` when labels and comments are empty" in {
-
- val payload =
- s"""
- |{
- | "listIri": "$exampleListIri",
- | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}",
- | "labels": [],
- | "comments": []
- |}
- """.stripMargin
-
- val thrown = the [BadRequestException] thrownBy payload.parseJson.convertTo[ChangeListInfoApiRequestADM]
-
- thrown.getMessage should equal (REQUEST_NOT_CHANGING_DATA_ERROR)
-
- }
-
"throw 'BadRequestException' for `CreateChildNodeApiRequestADM` when list node iri is empty" in {
val payload =
@@ -335,5 +321,76 @@ class ListsMessagesADMSpec extends WordSpecLike with Matchers with ListADMJsonPr
}
+ "throw 'BadRequestException' for `ChangeListNodeInfoApiRequestADM` when node IRI is empty" in {
+
+ val payload =
+ s"""
+ |{
+ | "nodeIri": "",
+ | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val thrown = the [BadRequestException] thrownBy payload.parseJson.convertTo[ChangeListNodeInfoApiRequestADM]
+
+ thrown.getMessage should equal (LIST_NODE_IRI_MISSING_ERROR)
+ }
+
+ "throw 'BadRequestException' for `ChangeListNodeInfoApiRequestADM` when node IRI is invalid" in {
+
+ val payload =
+ s"""
+ |{
+ | "nodeIri": "notvalidIRI",
+ | "projectIri": "${SharedTestDataADM.IMAGES_PROJECT_IRI}",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val thrown = the [BadRequestException] thrownBy payload.parseJson.convertTo[ChangeListNodeInfoApiRequestADM]
+
+ thrown.getMessage should equal (LIST_NODE_IRI_INVALID_ERROR)
+ }
+
+ "throw 'BadRequestException' for `ChangeListNodeInfoApiRequestADM` when project IRI is empty" in {
+
+ val payload =
+ s"""
+ |{
+ | "nodeIri": "$exampleListIri",
+ | "projectIri": "",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val thrown = the [BadRequestException] thrownBy payload.parseJson.convertTo[ChangeListNodeInfoApiRequestADM]
+
+ thrown.getMessage should equal (PROJECT_IRI_MISSING_ERROR)
+ }
+
+ "throw 'BadRequestException' for `ChangeListNodeInfoApiRequestADM` when project IRI is invalid" in {
+
+ val payload =
+ s"""
+ |{
+ | "nodeIri": "$exampleListIri",
+ | "projectIri": "notvalidIRI",
+ | "name": "newTestName",
+ | "labels": [{ "value": "Neue geönderte Liste", "language": "de"}, { "value": "Changed list", "language": "en"}],
+ | "comments": [{ "value": "Neuer Kommentar", "language": "de"}, { "value": "New comment", "language": "en"}]
+ |}
+ """.stripMargin
+
+ val thrown = the [BadRequestException] thrownBy payload.parseJson.convertTo[ChangeListNodeInfoApiRequestADM]
+
+ thrown.getMessage should equal (PROJECT_IRI_INVALID_ERROR)
+ }
}
}
\ No newline at end of file
diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala
index 7912163579..6495a15bcc 100644
--- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala
+++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala
@@ -217,17 +217,18 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with
"update basic list information" in {
responderManager ! ListInfoChangeRequestADM(
listIri = newListIri.get,
- changeListRequest = ChangeListInfoApiRequestADM(
+ changeListRequest = ChangeListInfoPayloadADM(
listIri = newListIri.get,
projectIri = IMAGES_PROJECT_IRI,
- labels = Seq(
+ name = Some(Some("new test name")),
+ labels = Some(Seq(
StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")),
StringLiteralV2(value = "Changed list", language = Some("en"))
- ),
- comments = Seq(
+ )),
+ comments = Some(Seq(
StringLiteralV2(value = "Neuer Kommentar", language = Some("de")),
StringLiteralV2(value = "New comment", language = Some("en"))
- )
+ ))
),
requestingUser = SharedTestDataADM.imagesUser01,
apiRequestID = UUID.randomUUID
@@ -238,6 +239,9 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with
val listInfo = received.listinfo
listInfo.projectIri should be (IMAGES_PROJECT_IRI)
+ val name: String = listInfo.name.get
+ name should be ("new test name")
+
val labels: Seq[StringLiteralV2] = listInfo.labels.stringLiterals
labels.size should be (2)
labels.sorted should be (Seq(
@@ -257,17 +261,18 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with
"return a 'ForbiddenException' if the user changing the list is not project or system admin" in {
responderManager ! ListInfoChangeRequestADM(
listIri = newListIri.get,
- changeListRequest = ChangeListInfoApiRequestADM(
+ changeListRequest = ChangeListInfoPayloadADM(
listIri = newListIri.get,
projectIri = IMAGES_PROJECT_IRI,
- labels = Seq(
+ name = Some(Some("newTestName")),
+ labels = Some(Seq(
StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")),
StringLiteralV2(value = "Changed list", language = Some("en"))
- ),
- comments = Seq(
+ )),
+ comments = Some(Seq(
StringLiteralV2(value = "Neuer Kommentar", language = Some("de")),
StringLiteralV2(value = "New comment", language = Some("en"))
- )
+ ))
),
requestingUser = SharedTestDataADM.imagesUser02,
apiRequestID = UUID.randomUUID
@@ -410,6 +415,73 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with
thirdChildIri.set(childNodeInfo.id)
}
+ "update basic list node information" in {
+ responderManager ! ListNodeInfoChangeRequestADM(
+ nodeIri = firstChildIri.get,
+ changeNodeRequest = ChangeListNodeInfoPayloadADM(
+ nodeIri = firstChildIri.get,
+ projectIri = IMAGES_PROJECT_IRI,
+ name = Some(Some("new TestName")),
+ labels = Some(Seq(StringLiteralV2(value = "New Test List Node Value", language = Some("en")))),
+ comments = Some(Seq(StringLiteralV2(value = "New Test List Node Comment", language = Some("en"))))
+ ),
+ requestingUser = SharedTestDataADM.imagesUser01,
+ apiRequestID = UUID.randomUUID
+ )
+
+ val received: ListNodeInfoGetResponseADM = expectMsgType[ListNodeInfoGetResponseADM](timeout)
+ val nodeInfo = received.nodeinfo
+
+ // check correct node info
+ val childNodeInfo = nodeInfo match {
+ case info: ListChildNodeInfoADM => info
+ case something => fail(s"expecting ListChildNodeInfoADM but got ${something.getClass.toString} instead.")
+ }
+
+ // check name
+ val name = childNodeInfo.name
+ name should be (Some("new TestName"))
+
+ // check labels
+ val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals
+ labels.size should be (1)
+ labels.sorted should be (Seq(StringLiteralV2(value = "New Test List Node Value", language = Some("en"))))
+
+ // check comments
+ val comments = childNodeInfo.comments.stringLiterals
+ comments.size should be (1)
+ comments.sorted should be (Seq(StringLiteralV2(value = "New Test List Node Comment", language = Some("en"))))
+
+ // check position
+ val position = childNodeInfo.position
+ position should be (0)
+
+ // check has root node
+ val rootNode = childNodeInfo.hasRootNode
+ rootNode should be (newListIri.get)
+ }
+
+ "return a 'ForbiddenException' if the user changing the list node is not project or system admin" in {
+ responderManager ! ListNodeInfoChangeRequestADM(
+ nodeIri = firstChildIri.get,
+ changeNodeRequest = ChangeListNodeInfoPayloadADM(
+ nodeIri = firstChildIri.get,
+ projectIri = IMAGES_PROJECT_IRI,
+ name = Some(Some("newTestName")),
+ labels = Some(Seq(
+ StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")),
+ StringLiteralV2(value = "Changed list", language = Some("en"))
+ )),
+ comments = Some(Seq(
+ StringLiteralV2(value = "Neuer Kommentar", language = Some("de")),
+ StringLiteralV2(value = "New comment", language = Some("en"))
+ ))
+ ),
+ requestingUser = SharedTestDataADM.imagesUser02,
+ apiRequestID = UUID.randomUUID
+ )
+ expectMsg(Failure(ForbiddenException(LIST_CHANGE_PERMISSION_ERROR)))
+ }
"change node order" ignore {