Skip to content

Commit b55eb12

Browse files
authored
refactor: move value objects to separate project (DEV-615) (#2069)
* add new project and deps * move value objects to separate ptoject * move & refactor error messages * copy temporarily + adjust v2 validation related stuff * fix all errors but StringLiteralV2 * fix StringLiteralV2 class related issues * fix admin tests * move tests to new project * rewrite tests to ZIO * fixt tests * refactor IRI type property names * cleanup * review changes * more review changes * even more review changes
1 parent 63b8844 commit b55eb12

60 files changed

Lines changed: 2354 additions & 1650 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎build.sbt‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ lazy val webapi: Project = Project(id = "webapi", base = file("webapi"))
201201
),
202202
buildInfoPackage := "org.knora.webapi.http.version"
203203
)
204+
.dependsOn(valueObjects)
204205

205206
lazy val webapiJavaRunOptions = Seq(
206207
// "-showversion",
@@ -282,3 +283,11 @@ lazy val schemaRepoSearchService = project
282283
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
283284
)
284285
.dependsOn(schemaRepo)
286+
287+
lazy val valueObjects = project
288+
.in(file("dsp-value-objects"))
289+
.settings(
290+
name := "valueObjects",
291+
libraryDependencies ++= Dependencies.valueObjectsLibraryDependencies,
292+
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
293+
)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package dsp.valueobjects
7+
8+
import zio.prelude.Validation
9+
10+
object Group {
11+
12+
/**
13+
* GroupName value object.
14+
*/
15+
sealed abstract case class GroupName private (value: String)
16+
object GroupName { self =>
17+
def make(value: String): Validation[Throwable, GroupName] =
18+
if (value.isEmpty) {
19+
Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupNameMissing))
20+
} else {
21+
val validatedValue = Validation(
22+
V2IriValidation.toSparqlEncodedString(
23+
value,
24+
throw V2.BadRequestException(GroupErrorMessages.GroupNameInvalid)
25+
)
26+
)
27+
28+
validatedValue.map(new GroupName(_) {})
29+
}
30+
31+
def make(value: Option[String]): Validation[Throwable, Option[GroupName]] =
32+
value match {
33+
case Some(v) => self.make(v).map(Some(_))
34+
case None => Validation.succeed(None)
35+
}
36+
}
37+
38+
/**
39+
* GroupDescriptions value object.
40+
*/
41+
sealed abstract case class GroupDescriptions private (value: Seq[V2.StringLiteralV2])
42+
object GroupDescriptions { self =>
43+
def make(value: Seq[V2.StringLiteralV2]): Validation[Throwable, GroupDescriptions] =
44+
if (value.isEmpty) {
45+
Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupDescriptionsMissing))
46+
} else {
47+
val validatedDescriptions = Validation(value.map { description =>
48+
val validatedDescription =
49+
V2IriValidation.toSparqlEncodedString(
50+
description.value,
51+
throw V2.BadRequestException(GroupErrorMessages.GroupDescriptionsInvalid)
52+
)
53+
V2.StringLiteralV2(value = validatedDescription, language = description.language)
54+
})
55+
validatedDescriptions.map(new GroupDescriptions(_) {})
56+
}
57+
58+
def make(value: Option[Seq[V2.StringLiteralV2]]): Validation[Throwable, Option[GroupDescriptions]] =
59+
value match {
60+
case Some(v) => self.make(v).map(Some(_))
61+
case None => Validation.succeed(None)
62+
}
63+
}
64+
65+
/**
66+
* GroupStatus value object.
67+
*/
68+
sealed abstract case class GroupStatus private (value: Boolean)
69+
object GroupStatus { self =>
70+
def make(value: Boolean): Validation[Throwable, GroupStatus] =
71+
Validation.succeed(new GroupStatus(value) {})
72+
def make(value: Option[Boolean]): Validation[Throwable, Option[GroupStatus]] =
73+
value match {
74+
case Some(v) => self.make(v).map(Some(_))
75+
case None => Validation.succeed(None)
76+
}
77+
}
78+
79+
/**
80+
* GroupSelfJoin value object.
81+
*/
82+
sealed abstract case class GroupSelfJoin private (value: Boolean)
83+
object GroupSelfJoin { self =>
84+
def make(value: Boolean): Validation[Throwable, GroupSelfJoin] =
85+
Validation.succeed(new GroupSelfJoin(value) {})
86+
def make(value: Option[Boolean]): Validation[Throwable, Option[GroupSelfJoin]] =
87+
value match {
88+
case Some(v) => self.make(v).map(Some(_))
89+
case None => Validation.succeed(None)
90+
}
91+
}
92+
}
93+
94+
object GroupErrorMessages {
95+
val GroupNameMissing = "Group name cannot be empty."
96+
val GroupNameInvalid = "Group name is invalid."
97+
val GroupDescriptionsMissing = "Group description cannot be empty."
98+
val GroupDescriptionsInvalid = "Group description is invalid."
99+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package dsp.valueobjects
7+
8+
import zio.prelude.Validation
9+
10+
sealed trait Iri
11+
object Iri {
12+
13+
/**
14+
* GroupIri value object.
15+
*/
16+
sealed abstract case class GroupIri private (value: String) extends Iri
17+
object GroupIri { self =>
18+
def make(value: String): Validation[Throwable, GroupIri] =
19+
if (value.isEmpty) {
20+
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriMissing))
21+
} else {
22+
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)
23+
24+
if (!V2IriValidation.isKnoraGroupIriStr(value)) {
25+
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriInvalid))
26+
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
27+
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
28+
} else {
29+
val validatedValue = Validation(
30+
V2IriValidation.validateAndEscapeIri(value, throw V2.BadRequestException(IriErrorMessages.GroupIriInvalid))
31+
)
32+
33+
validatedValue.map(new GroupIri(_) {})
34+
}
35+
}
36+
37+
def make(value: Option[String]): Validation[Throwable, Option[GroupIri]] =
38+
value match {
39+
case Some(v) => self.make(v).map(Some(_))
40+
case None => Validation.succeed(None)
41+
}
42+
}
43+
44+
/**
45+
* ListIri value object.
46+
*/
47+
sealed abstract case class ListIri private (value: String) extends Iri
48+
object ListIri { self =>
49+
def make(value: String): Validation[Throwable, ListIri] =
50+
if (value.isEmpty) {
51+
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriMissing))
52+
} else {
53+
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)
54+
55+
if (!V2IriValidation.isKnoraListIriStr(value)) {
56+
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriInvalid))
57+
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
58+
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
59+
} else {
60+
val validatedValue = Validation(
61+
V2IriValidation.validateAndEscapeIri(
62+
value,
63+
throw V2.BadRequestException(IriErrorMessages.ListIriInvalid)
64+
)
65+
)
66+
67+
validatedValue.map(new ListIri(_) {})
68+
}
69+
}
70+
71+
def make(value: Option[String]): Validation[Throwable, Option[ListIri]] =
72+
value match {
73+
case Some(v) => self.make(v).map(Some(_))
74+
case None => Validation.succeed(None)
75+
}
76+
}
77+
78+
/**
79+
* ProjectIri value object.
80+
*/
81+
sealed abstract case class ProjectIri private (value: String) extends Iri
82+
object ProjectIri { self =>
83+
def make(value: String): Validation[Throwable, ProjectIri] =
84+
if (value.isEmpty) {
85+
Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriMissing))
86+
} else {
87+
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)
88+
89+
if (!V2IriValidation.isKnoraProjectIriStr(value)) {
90+
Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriInvalid))
91+
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
92+
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
93+
} else {
94+
val validatedValue = Validation(
95+
V2IriValidation.validateAndEscapeProjectIri(
96+
value,
97+
throw V2.BadRequestException(IriErrorMessages.ProjectIriInvalid)
98+
)
99+
)
100+
101+
validatedValue.map(new ProjectIri(_) {})
102+
}
103+
}
104+
105+
def make(value: Option[String]): Validation[Throwable, Option[ProjectIri]] =
106+
value match {
107+
case Some(v) => self.make(v).map(Some(_))
108+
case None => Validation.succeed(None)
109+
}
110+
}
111+
112+
/**
113+
* UserIri value object.
114+
*/
115+
sealed abstract case class UserIri private (value: String) extends Iri
116+
object UserIri { self =>
117+
def make(value: String): Validation[Throwable, UserIri] =
118+
if (value.isEmpty) {
119+
Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriMissing))
120+
} else {
121+
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)
122+
123+
if (!V2IriValidation.isKnoraUserIriStr(value)) {
124+
Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriInvalid))
125+
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
126+
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
127+
} else {
128+
val validatedValue = Validation(
129+
V2IriValidation.validateAndEscapeUserIri(
130+
value,
131+
throw V2.BadRequestException(IriErrorMessages.UserIriInvalid)
132+
)
133+
)
134+
135+
validatedValue.map(new UserIri(_) {})
136+
}
137+
}
138+
139+
def make(value: Option[String]): Validation[Throwable, Option[UserIri]] =
140+
value match {
141+
case Some(v) => self.make(v).map(Some(_))
142+
case None => Validation.succeed(None)
143+
}
144+
}
145+
}
146+
147+
object IriErrorMessages {
148+
val GroupIriMissing = "Group IRI cannot be empty."
149+
val GroupIriInvalid = "Group IRI is invalid."
150+
val ListIriMissing = "List IRI cannot be empty."
151+
val ListIriInvalid = "List IRI is invalid"
152+
val ProjectIriMissing = "Project IRI cannot be empty."
153+
val ProjectIriInvalid = "Project IRI is invalid."
154+
val UserIriMissing = "User IRI cannot be empty."
155+
val UserIriInvalid = "User IRI is invalid."
156+
val UuidVersionInvalid = "Invalid UUID used to create IRI. Only versions 4 and 5 are supported."
157+
}

0 commit comments

Comments
 (0)