New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(value): make impossible to set list root node as a value (DEV-973) #2088
fix(value): make impossible to set list root node as a value (DEV-973) #2088
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't add a test. Is there already one? Or is it the case you mentioned today in the daily that you don't know how to test it?
...pi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala
Outdated
Show resolved
Hide resolved
comment = getComment(jsonLDObject) | ||
) | ||
for { | ||
isRootNode <- checkIfNodeIsRoot( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems to be request specific. Did you check if fromJsonLDObject
should never allow a root node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get your question. In the case of list values, yes it shouldn't be allowed a root node as value. That's actually the clue of this bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, than that's fine. I thought there might be cases where the value can be a root node (although I don't know what cases there could be).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, that this check is implemented in the correct place. It should be probably better to move it to here:
dsp-api/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
Lines 195 to 201 in 3a0902e
// If this is a list value, check that it points to a real list node. | |
_ <- submittedInternalValueContent match { | |
case listValue: HierarchicalListValueContentV2 => | |
ResourceUtilV2.checkListNodeExists(listValue.valueHasListNode, appActor) | |
case _ => FastFuture.successful(()) | |
} |
This way, you will also be able to write the missing tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@subotic Good point, moved checking method to ResourceUtilV2
and added test to ValuesResponderV2Spec
.
@irinaschubert this is what I was mentioning on the daily. As an exercise I did an attempt to write tests based on |
comment = getComment(jsonLDObject) | ||
) | ||
for { | ||
isRootNode <- checkIfNodeIsRoot( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, that this check is implemented in the correct place. It should be probably better to move it to here:
dsp-api/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
Lines 195 to 201 in 3a0902e
// If this is a list value, check that it points to a real list node. | |
_ <- submittedInternalValueContent match { | |
case listValue: HierarchicalListValueContentV2 => | |
ResourceUtilV2.checkListNodeExists(listValue.valueHasListNode, appActor) | |
case _ => FastFuture.successful(()) | |
} |
This way, you will also be able to write the missing tests.
// If this is a list value, check if list value is not root list node. | ||
_ <- submittedInternalValueContent match { | ||
case listValue: HierarchicalListValueContentV2 => | ||
ResourceUtilV2.checkIfNodeIsRoot( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming is a bit misleading. checkIfNodeIsRoot
implies that it is going to be true
when it is a root node. In this case, you get an exception if it is a root node, i.e., a false
. Also, what happens if the node is not a node at all, i.e., it does not exist in the database?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, doing two queries just to check if a node exists and is not a root node is a bit inefficient. You should try to make only one query (a SPARQL ASK
), that will return true
in the case that it is an existing child node, and a false
in the case that it is not existing or a root node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How this name is misleading? It says what it does = checks if the list node is root. To make it more precise it might be adjusted to checkIfNodeIsRootAndThrowsErrorIfNot
, but IMO this is not really necessary here.
On the other comment you're right, this two queries are not optimal solution. I was actually thinking to use the one I created - which uses inside getListNode
. At the end it gets the node, so if it isn't in the database should also return that information. But it seems it isn't that straighfroward, because it's checked somewhere else and this is thrown:
2022-06-30 07:24:18 | ERROR | KnoraExceptionHandler$ | Unable to run route /v2/values
java.util.NoSuchElementException: head of empty list
listNodeResponse <- | ||
appActor | ||
.ask( | ||
SparqlExtendedConstructRequest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at this: https://github.com/dasch-swiss/dsp-api/blob/main/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/checkListRootNodeExistsByIri.scala.txt
What you should do is write an ASK
query that will return true
or false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is only one but. With one query for both checks, I can only return one error, this shouldn't be a problem but it's less information for the user.
* | ||
* @param listNodeIri the IRI of the list node. | ||
*/ | ||
def checkListNodeExists(listNodeIri: IRI, appActor: ActorRef)(implicit | ||
def checkListNodeExistsAndHasRootNode(listNodeIri: IRI, appActor: ActorRef)(implicit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we say, that this method should return a boolean
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, I'm missing a test for this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That wasn't we but you are trying to force me to do so :) My opinion was different on that. However I've tried and spent some time to find out, that this method has been extracted to ResourceUtilV2
because it's used twice in ValuesResponderV2
as well as once in ResourcesResponderV2
, where actually the usage is slightly different and I couldn't make things compile. Changing it from Unit method which throws to one which returns Boolean will also create more code duplication.
All this only strengthen my feelings:
- I'm against increasing the scope of tasks, especially bugs. If we have bugs first policy, they should be fixed ASAP. This task is about fixing the bug, which I did applying the convention in the package, I also wrote test.
- Changing the way how only
checkListNodeExistsAndHasRootNode
works w/o touching other methods inResourceUtilV2
doesn't bring any value, in fact it makes even more confusion. If this is so important - should be moved to refactor task which will solve the problem comprehensively. - At the end I don't mind to refactor code while fixing the bugs, which in fact we all do. But this should be rather only necessary changes which helps in fixing the bug, like in this PR changing
checkListNodeExists
tocheckListNodeExistsAndHasRootNode
in order to reduce number of queries and lines of code - one method instead of two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just some minor things. (Not sure if that will show up easily because I responded in a resolved conversation, but I also commented on the .gitignore thing)
webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
Outdated
Show resolved
Hide resolved
) | ||
|
||
expectMsgPF(timeout) { case msg: akka.actor.Status.Failure => | ||
assert(msg.cause.isInstanceOf[NotFoundException]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this really be a "NotFoundException", seems more like a "BadRequest", right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been improved with 58b4551 and now depends of outcome I throw BadRequestException
or NotFoundException
.
…ist-iri-as-list-property-value
webapi/src/main/scala/org/knora/webapi/responders/v2/ResourceUtilV2.scala
Show resolved
Hide resolved
FastFuture.successful(()) | ||
// it does have isRootNode property - it's a root node | ||
case Right(true) => | ||
FastFuture.failed( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if you need FastFuture.failed
when you are throwing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was the most tricky part. W/oFastFuture.failed()
it didn't compile, but strangely now it seems working. Suspect metals was broken at that time...
webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala
Show resolved
Hide resolved
_ = checkNode match { | ||
// it doesn't have isRootNode property - it's a child node | ||
case Right(false) => | ||
FastFuture.successful(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are not interested in the value, so simply return ()
. No need to warp it into a Future only to discard it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above - I went for the same solution as I implemented in ValueResponderV2
. but w/oFastFuture.successful(())
it didn't compile. Strangely now it seems working. Suspect metals was broken at that time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala
Outdated
Show resolved
Hide resolved
webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala
Outdated
Show resolved
Hide resolved
webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala
Outdated
Show resolved
Hide resolved
Kudos, SonarCloud Quality Gate passed!
|
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: DEV-973
What is the new behavior?
Does this PR introduce a breaking change?
Other information