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
refactor: move value objects to separate project (DEV-615) #2069
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.
Another big one, nice job!
It mostly looks good (and most of my comments are cosmetic or can be ignored).
One issue needs addressing though:
I think the way the ZIO Tests are written is not yet entirely correct
- as far as I remember, multiple
assertTrue()
statements must be combined with&&
otherwise only the last one is actually tested. - I think multiple
test()
in onesuite()
should be combined with comma, not plus.
I used this file as a reference: https://github.com/zio/zio-schema/blob/main/zio-schema/shared/src/test/scala/zio/schema/ValidationSpec.scala
if (value.isEmpty) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriMissing)) | ||
} else { | ||
val isUUID: Boolean = V2UuidValidation.hasUUIDLength(value.split("/").last) | ||
|
||
if (!V2IriValidation.isKnoraGroupIriStr(value)) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriInvalid)) | ||
} else if (isUUID && !V2UuidValidation.isUUIDVersion4Or5(value)) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidInvalid)) | ||
} else { | ||
val validatedValue = Validation( | ||
V2IriValidation.validateAndEscapeIri(value, throw V2.BadRequestException(IriErrorMessages.GroupIriInvalid)) | ||
) | ||
|
||
validatedValue.map(new GroupIri(_) {}) |
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.
eventually it would be nice to be able to simplify these validations. It has nested conditions to the point where it gets hard to read the code. But that's for the validation epic, not for this task.
Also, I general question I have: these validation functions (like uuidHasLength()
etc.), shouldn't they ideally live in the value object?
And things like UUIDs, shouldn't they themselves be value objects with their own validation?
(Again, not scope of this PR, just in terms of the design)
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 for the comment. Value objects refactor it's planned in next steps.
object ListIri { self => | ||
def make(value: String): Validation[Throwable, ListIri] = | ||
if (value.isEmpty) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriMissing)) | ||
} else { | ||
val isUUID: Boolean = V2UuidValidation.hasUUIDLength(value.split("/").last) | ||
|
||
if (!V2IriValidation.isKnoraListIriStr(value)) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriInvalid)) | ||
} else if (isUUID && !V2UuidValidation.isUUIDVersion4Or5(value)) { | ||
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidInvalid)) | ||
} else { | ||
val validatedValue = Validation( | ||
V2IriValidation.validateAndEscapeIri( | ||
value, | ||
throw V2.BadRequestException(IriErrorMessages.ListIriInvalid) | ||
) | ||
) | ||
|
||
validatedValue.map(new ListIri(_) {}) |
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.
And again, these different IRI types share a lot of the same logic, so there probably should be a shared method for checking these things. (in a later PR :) )
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 for the comment. Value objects refactor it's planned in next steps.
webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala
Outdated
Show resolved
Hide resolved
...rc/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala
Show resolved
Hide resolved
...pi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala
Outdated
Show resolved
Hide resolved
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 have only cosmetics to add. Apart from that it looks good to me and I'm curious to use these value objects in the user slice :)
} | ||
|
||
/** | ||
* GroupDescriptions value object. |
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 anymore if we discussed the naming of this already. But I think that "description" should be singular. There is only one description per group (and all other entities), just possibly in multiple languages. What do you think?
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 is indeed a little bit misleading. Since value objects are going to be refactored in next step, this can be a subject of discussion.
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.
agree, only one description.
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.
Fine, but technically it is still the Seq of values, which means that the way how the translations/internationalisation is implemented is wrong. Nothing stops user from adding more than one description in the same language.
/** | ||
* List Position value object. | ||
*/ | ||
sealed abstract case class Position private (value: Int) |
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.
sealed abstract case class Position private (value: Int) | |
sealed abstract case class ListPosition private (value: Int) |
/** | ||
* List Labels value object. | ||
*/ | ||
sealed abstract case class Labels private (value: Seq[V2.StringLiteralV2]) |
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.
sealed abstract case class Labels private (value: Seq[V2.StringLiteralV2]) | |
sealed abstract case class ListLabels private (value: Seq[V2.StringLiteralV2]) |
} | ||
|
||
/** | ||
* List Labels value object. |
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 same thing I mentioned for description. Shouldn't this be singular because there is only one label (just in different languages)?
dsp-value-objects/src/test/scala/dsp/valueobjects/GroupSpec.scala
Outdated
Show resolved
Hide resolved
dsp-value-objects/src/test/scala/dsp/valueobjects/IriSpec.scala
Outdated
Show resolved
Hide resolved
webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala
Show resolved
Hide resolved
webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala
Outdated
Show resolved
Hide resolved
webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala
Outdated
Show resolved
Hide resolved
} | ||
|
||
/** | ||
* GroupDescriptions value object. |
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.
agree, only one description.
|
||
import zio.prelude.Validation | ||
|
||
sealed trait Group |
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.
we don't need to define a trait for Group. We only discussed it in the context of IRI.
/** | ||
* GroupIri value object. | ||
*/ | ||
sealed abstract case class GroupIri private (value: String) |
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.
all IRI case classes need to extend the IRI
sealed trait, or it will not work as intended.
/** | ||
* Project Keywords value object. | ||
*/ | ||
sealed abstract case class Keywords private (value: Seq[String]) |
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.
hmm, can be both. Either a Seq[Keyword]
on the level of the entity, or a Seq[String]
inside a Keywords
value object. Maybe it is easier to go with the Seq[String]
, as this is what is coming through the API? It could even be a Set[String].
/** | ||
* SystemAdmin value object. | ||
*/ | ||
sealed abstract case class SystemAdmin private (value: 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.
yes, but this will probably be (re)moved. Permissions will be given through roles, and shouldn't be a flag on the user (in the future).
webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala
Outdated
Show resolved
Hide resolved
@BalduinLandolt @irinaschubert I only applied some of things you've pointed out. These which are related more to the next part, which would be the real refactor, should be also discussed first. The comments I think are important in the next step I left unresolved which should help to define the way how we shape value objects. |
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 didn't go through it in extreme detail this time... but I'd say it's good now.
One thing though: Leaving conversations unresolved is maybe not the most persistent way of keeping track of what needs to be done in the future refactoring steps.
Would you mind creating a confluence page (or a Jira-Story, if you find that better) where you collect the main points that need to be addressed or discussed?
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-615
What is the new behavior?
Does this PR introduce a breaking change?
Other information