Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* add user project * add simple UserHandler methods * add unit test for user repo * add new structure for tests * refactoring some code * clean up code * run UserRepoSpec for all implementations * add more methods and tests to user handler * restructure code * fix test * update structure of user slice * test: fix race condition by providing different environments to each test * wip * fix failing tests * update dependencies for shared project * add method to check if username or email is taken * improve UserUserHandlerSpec * organize imports Co-authored-by: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com> Co-authored-by: Marcin Procyk <marcin.procyk@dasch.swiss>
- Loading branch information
1 parent
96362f4
commit 0c5ec03
Showing
19 changed files
with
924 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package dsp.user.api | ||
|
||
import dsp.errors._ | ||
import dsp.user.domain._ | ||
import zio._ | ||
import zio.macros.accessible | ||
|
||
import java.util.UUID | ||
|
||
/** | ||
* The trait (interface) for the user repository. The user repository is responsible for storing and retrieving users. | ||
* Needs to be used by the user repository implementations. | ||
*/ | ||
@accessible // with this annotation we don't have to write the companion object ourselves | ||
trait UserRepo { | ||
|
||
/** | ||
* Writes a user to the repository (used for both create and update). | ||
* If this fails (e.g. the triplestore is not available), it's a non-recovable error. That's why we need UIO. | ||
* When used, we should do it like: ...store(...).orDie | ||
* | ||
* @param user the user to write | ||
* @return Unit | ||
*/ | ||
def storeUser(user: User): UIO[UserId] | ||
|
||
/** | ||
* Gets all users from the repository. | ||
* | ||
* @return a list of [[User]] | ||
*/ | ||
def getUsers(): UIO[List[User]] | ||
|
||
/** | ||
* Retrieves the user from the repository by ID. | ||
* | ||
* @param id the user's ID | ||
* @return an optional [[User]] | ||
*/ | ||
def getUserById(id: UserId): IO[Option[Nothing], User] | ||
|
||
/** | ||
* Retrieves the user from the repository by username or email. | ||
* | ||
* @param usernameOrEmail username or email of the user. | ||
* @return an optional [[User]]. | ||
*/ | ||
def getUserByUsernameOrEmail(usernameOrEmail: String): IO[Option[Nothing], User] | ||
|
||
/** | ||
* Checks if a username or email exists in the repo. | ||
* | ||
* @param usernameOrEmail username or email of the user. | ||
* @return Unit in case of success | ||
*/ | ||
def checkUsernameOrEmailExists(usernameOrEmail: String): IO[Option[Nothing], Unit] | ||
|
||
/** | ||
* Deletes a [[User]] from the repository by its [[UserId]]. | ||
* | ||
* @param id the user ID | ||
* @return Unit or None if not found | ||
*/ | ||
def deleteUser(id: UserId): IO[Option[Nothing], UserId] | ||
} |
142 changes: 142 additions & 0 deletions
142
dsp-user/core/src/main/scala/dsp/user/domain/UserDomain.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package dsp.user.domain | ||
|
||
import dsp.valueobjects.User._ | ||
import zio.prelude.Validation | ||
|
||
import java.util.UUID | ||
|
||
// move this to shared value objects project once we have it | ||
sealed trait Iri | ||
object Iri { | ||
|
||
/** | ||
* UserIri value object. | ||
*/ | ||
sealed abstract case class UserIri private (value: String) extends Iri | ||
object UserIri { self => | ||
|
||
def make(value: String): UserIri = new UserIri(value) {} | ||
} | ||
// ... | ||
|
||
} | ||
|
||
/** | ||
* Stores the user ID, i.e. UUID and IRI of the user | ||
* | ||
* @param uuid the UUID of the user | ||
* @param iri the IRI of the user | ||
*/ | ||
abstract case class UserId private ( | ||
uuid: UUID, | ||
iri: Iri.UserIri | ||
) | ||
|
||
/** | ||
* Companion object for UserId. Contains factory methods for creating UserId instances. | ||
*/ | ||
object UserId { | ||
|
||
/** | ||
* Generates a UserId instance from a given string (either UUID or IRI). | ||
* | ||
* @param value the string to parse (either UUID or IRI) | ||
* @return a new UserId instance | ||
*/ | ||
// TODO not sure if we need this | ||
// def fromString(value: String): UserId = { | ||
// val uuidPattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$".r | ||
// val iriPattern = "^http*".r | ||
|
||
// value match { | ||
// case uuidPattern(value) => new UserId(UUID.fromString(value), Iri.UserIri.make(value)) {} | ||
// case iriPattern(value) => | ||
// new UserId(UUID.fromString(value.substring(value.lastIndexOf("/") + 1)), Iri.UserIri.make(value)) {} | ||
// //case _ => ??? | ||
// } | ||
// } | ||
|
||
/** | ||
* Generates a UserId instance with a new (random) UUID and an IRI which is created from a prefix and the UUID. | ||
* | ||
* @return a new UserId instance | ||
*/ | ||
def fromIri(iri: Iri.UserIri): UserId = { | ||
val uuid: UUID = UUID.fromString(iri.value.split("/").last) | ||
new UserId(uuid, iri) {} | ||
} | ||
|
||
/** | ||
* Generates a UserId instance with a new (random) UUID and an IRI which is created from a prefix and the UUID. | ||
* | ||
* @return a new UserId instance | ||
*/ | ||
def fromUuid(uuid: UUID): UserId = { | ||
val iri: Iri.UserIri = Iri.UserIri.make("http://rdfh.ch/users/" + uuid.toString) | ||
new UserId(uuid, iri) {} | ||
} | ||
|
||
/** | ||
* Generates a UserId instance with a new (random) UUID and an IRI which is created from a prefix and the UUID. | ||
* | ||
* @return a new UserId instance | ||
*/ | ||
// TODO should this return a Validation[Throwable, UserId] | ||
def make(): UserId = { | ||
val uuid: UUID = UUID.randomUUID() | ||
val iri: Iri.UserIri = Iri.UserIri.make("http://rdfh.ch/users/" + uuid.toString) | ||
new UserId(uuid, iri) {} | ||
} | ||
} | ||
|
||
/** | ||
* Represents the user domain object. | ||
* | ||
* @param id the ID of the user | ||
* @param givenName the given name of the user | ||
* @param familyName the family name of the user | ||
* @param username the username of the user | ||
* @param email the email of the user | ||
* @param password the password of the user | ||
* @param language the user's preferred language | ||
* @param role the user's role | ||
*/ | ||
sealed abstract case class User private ( | ||
id: UserId, | ||
givenName: GivenName, | ||
familyName: FamilyName, | ||
username: Username, | ||
email: Email, | ||
password: Option[Password], | ||
language: LanguageCode | ||
//role: Role | ||
) extends Ordered[User] { self => | ||
|
||
/** | ||
* Allows to sort collections of [[User]]s. Sorting is done by the IRI. | ||
*/ | ||
def compare(that: User): Int = self.id.iri.toString().compareTo(that.id.iri.toString()) | ||
|
||
def updateUsername(value: Username): User = | ||
new User(self.id, self.givenName, self.familyName, value, self.email, self.password, self.language) {} | ||
} | ||
object User { | ||
def make( | ||
givenName: GivenName, | ||
familyName: FamilyName, | ||
username: Username, | ||
email: Email, | ||
password: Password, | ||
language: LanguageCode | ||
//role: Role | ||
): User = { | ||
val id = UserId.make() | ||
new User(id, givenName, familyName, username, email, Some(password), language) {} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package dsp.user.api |
6 changes: 6 additions & 0 deletions
6
dsp-user/core/src/test/scala/dsp/user/domain/UserDomainSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* | ||
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package dsp.user.domain |
Oops, something went wrong.