Exercise: UserService
The project will emulate a real-life task, typical to backend development, but similar to what is done on the domain layer in Android. We will create a service that will allow us to manage users. It will be a simple service, but it will be a good example of how to use Kotlin to create a domain layer.
All the files for solving this exercise can be found in the MarcinMoskala/kotlin-exercises project on GitHub in the functional/project
package. You should clone this project and solve this exercise locally.
Your task is to implement methods from classes UserService
, UserDtoFactory
, and RealUserKeyGenerator
.
UserService
is a service that will allow us to manage users. It should have the following properties and methods:
userByIdCache
anduserByKeyCache
- properties that define this class caching mechanism. They should usecache
DSL builder to configure a cache with 1 minute of expiration time after both read and write, and it should configure how data should be loaded into the cache. TheuserByIdCache
should load data using theuserRepository.getUser
method, and theuserByKeyCache
should load data using theuserRepository.getUserByKey
method. In both cases, when data is loaded, it should also be stored in the other cache.getUser(id: String): User?
- returns user with the given id ornull
if there is no such user.getUserByKey(key: String): User?
- returns user with the given key ornull
if there is no such user.getToken(email: String, passwordHash: String): String
- returns a token for the user with the given email if the password hash is correct. If there is no such user or the password hash is incorrect, it throws an exception with the message "Wrong email or password".updateUser(token: String, userPatch: UserPatch): User
- updates the user with the given token using the given patch. If there is no such user, it throws an exception with message "User not found".addUser(token: String, addUser: AddUser): User
- adds a new user using the given data. Only admin can add users, so if the user with the given token is not an admin, it throws an exception.userStatistics(token: String): UserStatistics
- returns statistics about users. If the user with the given token is not an admin, it throws an exception.
UserDtoFactory
is a factory that creates UserDto
objects. It should have the method produceUserDto(addUser: AddUser): UserDto
that creates a UserDto
object using the given AddUser
object. It should use the timeProvider
to get the current time, the uuidGenerator
to generate an id and a key, and the userKeyGenerator
to generate a key.
RealUserKeyGenerator
is a generator that creates keys for users. It should have the method findPublicKey(name: String, surname: String): String?
to return a key for the given name and surname. If there is no such key, it shoudld return null
. It should use the userRepository
to check if the key is available. This function should try different combinations of name and surname in a form transformed to a correct key value. Key should be lowercase and only include characters or digits. It should not include any special characters. It should also not be shorter than 4 characters. If the key is not available, it should return null
. It should try to generate a key in the following order, but if none of those keys is available, it should generate a random key using the uuidGenerator
:
"$name$surname"
"$surname$name"
"$name${surname.first()}"
"${name.first()}$surname"
"$surname${name.first()}"
"${surname.first()}$name"
More detailed explanation for each of those functions can be found on their comments. You can also find unit tests for those functions in their files. Remember to run them to check if your solution is correct.
Once you are done with the exercise, you can check your solution here.