-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
529 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
scheduler/src/main/java/com/schedutn/scheduler/domain/Exceptions.kt
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,5 @@ | ||
package com.schedutn.scheduler.domain | ||
|
||
class IllegalVoteException(message: String) : Exception(message) | ||
|
||
class IllegalScheduleException(message: String) : Exception(message) |
20 changes: 20 additions & 0 deletions
20
scheduler/src/main/java/com/schedutn/scheduler/domain/models/Meeting.kt
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,20 @@ | ||
package com.schedutn.scheduler.domain.models | ||
|
||
import jakarta.validation.constraints.NotBlank | ||
|
||
/** | ||
* Meeting. | ||
* | ||
* @property title name of the meeting | ||
* @property description brief description of the meeting | ||
* @property location where this meeting will be held | ||
*/ | ||
data class Meeting( | ||
|
||
@field:NotBlank(message = "Schedule title must not be blank") | ||
val title: String, | ||
|
||
val description: String?, | ||
|
||
val location: String?, | ||
) |
70 changes: 70 additions & 0 deletions
70
scheduler/src/main/java/com/schedutn/scheduler/domain/models/MeetingOption.kt
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 @@ | ||
package com.schedutn.scheduler.domain.models | ||
|
||
import jakarta.validation.constraints.Max | ||
import jakarta.validation.constraints.Min | ||
import java.time.LocalDate | ||
import java.time.LocalDateTime | ||
|
||
/** | ||
* Event option is a possible date for a Schedule to be held. | ||
* | ||
* @property date tentative date for the event | ||
* @property hour tentative hour for the event | ||
* @property minute tentative minute for the event | ||
*/ | ||
data class MeetingOption( | ||
|
||
val date: LocalDate, | ||
|
||
@field:Max(value = 23, message = "Hour must be less than 24") | ||
@field:Min(value = 0, message = "Hour must be greater or equal to 0") | ||
val hour: Int = 0, | ||
|
||
@field:Max(value = 59, message = "Minute must be less than 60") | ||
@field:Min(value = 0, message = "Minute must be greater or equal to 0") | ||
val minute: Int = 0, | ||
|
||
val votes: Set<String> = setOf(), | ||
) { | ||
|
||
/** | ||
* Adds or removes a vote from the option. | ||
* | ||
* @param username username of the user that voted | ||
* @return a new instance of [MeetingOption] with the vote added or removed | ||
*/ | ||
fun vote(username: String): MeetingOption { | ||
|
||
val newVotes = votes.toMutableSet() | ||
|
||
if (newVotes.contains(username)) { | ||
newVotes.remove(username) | ||
} else { | ||
newVotes.add(username) | ||
} | ||
|
||
return copy(votes = newVotes) | ||
} | ||
|
||
/** | ||
* Obtains the date time of the option. | ||
* | ||
* @return date time of the option | ||
*/ | ||
fun dateTime(): LocalDateTime = LocalDateTime.of(date, java.time.LocalTime.of(hour, minute)) | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (other == null) return false | ||
|
||
if (other !is MeetingOption) return false | ||
|
||
return date == other.date && hour == other.hour && minute == other.minute | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = date.hashCode() | ||
result = 31 * result + hour | ||
result = 31 * result + minute | ||
return result | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
scheduler/src/main/java/com/schedutn/scheduler/domain/models/PersistentEntity.java
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,20 @@ | ||
package com.schedutn.scheduler.domain.models; | ||
|
||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* Base class for all entities. | ||
*/ | ||
interface PersistentEntity extends Serializable { | ||
|
||
long serialVersionUID = 1L; | ||
|
||
/** | ||
* Obtains an unique identifier for the entity. | ||
* | ||
* @return the id of the entity. | ||
*/ | ||
String getIdentifier(); | ||
|
||
} |
119 changes: 119 additions & 0 deletions
119
scheduler/src/main/java/com/schedutn/scheduler/domain/models/Schedule.kt
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,119 @@ | ||
package com.schedutn.scheduler.domain.models | ||
|
||
import com.schedutn.scheduler.domain.IllegalScheduleException | ||
import com.schedutn.scheduler.domain.IllegalVoteException | ||
import jakarta.validation.Valid | ||
import jakarta.validation.constraints.Min | ||
import jakarta.validation.constraints.NotBlank | ||
import jakarta.validation.constraints.NotEmpty | ||
import java.time.LocalDateTime | ||
|
||
/** | ||
* Schedule | ||
* | ||
* Aggregate all the information about a Schedule. | ||
* | ||
* @property version version of the schedule | ||
* @property organizer username of the organizer | ||
* @property voting whether voting is enabled or not | ||
* @property event meeting information | ||
* @property guests list of guests | ||
* @property options list of options | ||
* @property date voted date of the event | ||
*/ | ||
data class Schedule( | ||
|
||
val id: String? = null, | ||
|
||
@field:Min(value = 0, message = "Version must be greater or equal to 0") | ||
val version: Int = 0, | ||
|
||
@field:NotBlank(message = "Organizer username must not be blank") | ||
val organizer: String, | ||
|
||
val voting: Boolean = false, | ||
|
||
@field:Valid | ||
val event: Meeting, | ||
|
||
val guests: Set<String> = setOf(), | ||
|
||
@field:NotEmpty | ||
@field:Valid | ||
val options: Set<MeetingOption>, | ||
|
||
val date: LocalDateTime? = null, | ||
) : PersistentEntity { | ||
|
||
/** | ||
* Votes or revokes a vote for an option. | ||
* | ||
* @param username username of the guest | ||
* @param option option to vote | ||
* @return new schedule with the option voted | ||
* @throws IllegalVoteException if voting is not enabled, user is not a guest or option is not valid | ||
*/ | ||
fun vote(username: String, option: MeetingOption): Schedule { | ||
|
||
if (!voting) | ||
throw IllegalVoteException("Voting is not enabled") | ||
|
||
if (!guests.contains(username) && username != organizer) | ||
throw IllegalVoteException("User is not a guest") | ||
|
||
if (!options.contains(option)) | ||
throw IllegalVoteException("Option is not valid") | ||
|
||
|
||
return copy(options = options.map { if (it == option) it.vote(username) else it }.toSet(), | ||
version = version + 1 | ||
) | ||
} | ||
|
||
/** | ||
* Returns the option with the most votes. | ||
* | ||
* @return option with the most votes | ||
*/ | ||
private fun getMostVotedOption(): MeetingOption = | ||
options.maxByOrNull { it.votes.size } ?: options.first() | ||
|
||
/** | ||
* Sets the date of the event to the most voted option. | ||
* | ||
* @param username user who wants to set the date | ||
* @return new schedule with the date set | ||
*/ | ||
fun schedule(username: String): Schedule { | ||
|
||
if (username != organizer) | ||
throw IllegalScheduleException("Only the organizer can schedule the event") | ||
|
||
return copy( | ||
date = getMostVotedOption().dateTime(), | ||
voting = false, | ||
version = version + 1 | ||
) | ||
} | ||
|
||
/** | ||
* Enables or disables voting. | ||
* | ||
* @param username user who wants to enable or disable voting | ||
* @param enabledVotes whether voting is enabled or not | ||
* @return new schedule with the new voting policy | ||
*/ | ||
fun toggleVoting(username: String, enabledVotes: Boolean): Schedule { | ||
|
||
if (username != organizer) | ||
throw IllegalScheduleException("Only the organizer can enable or disable voting") | ||
|
||
return copy( | ||
voting = enabledVotes, | ||
version = version + 1 | ||
) | ||
} | ||
|
||
override fun getIdentifier(): String? = id | ||
|
||
} |
66 changes: 66 additions & 0 deletions
66
scheduler/src/test/java/com/schedutn/scheduler/domain/models/MeetingOptionTest.kt
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,66 @@ | ||
package com.schedutn.scheduler.domain.models | ||
|
||
import org.junit.jupiter.api.Test | ||
import java.time.LocalDate | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertNotEquals | ||
import kotlin.test.assertTrue | ||
|
||
internal class MeetingOptionTest { | ||
|
||
|
||
@Test | ||
fun `options with same date, hour and minute are equals `() { | ||
|
||
val option1 = MeetingOption(date = LocalDate.now(), hour = 0, minute = 0, votes = setOf("user1")) | ||
val option2 = MeetingOption(date = LocalDate.now(), hour = 0, minute = 0, votes = setOf("user2")) | ||
|
||
assertEquals(option1, option2) | ||
} | ||
|
||
@Test | ||
fun `options with different date, hour and minute are not equals `() { | ||
|
||
val option1 = MeetingOption( | ||
date = LocalDate.now(), | ||
hour = 0, | ||
minute = 0, | ||
votes = setOf("user1")) | ||
|
||
val option2 = MeetingOption( | ||
date = LocalDate.now().plusDays(1), | ||
hour = 0, | ||
minute = 0, | ||
votes = setOf("user1") | ||
) | ||
|
||
assertNotEquals(option1, option2) | ||
} | ||
|
||
@Test | ||
fun `an option can be voted`() { | ||
// Given | ||
val username = "user1" | ||
val option = MeetingOption(date = LocalDate.now(), hour = 0, minute = 0) | ||
|
||
// When | ||
val votedOption = option.vote(username) | ||
|
||
// Then | ||
assertEquals(setOf(username), votedOption.votes) | ||
} | ||
|
||
@Test | ||
fun `an option can have voting revoked`() { | ||
// Given | ||
val username = "user1" | ||
val option = MeetingOption(date = LocalDate.now(), hour = 0, minute = 0, votes = setOf(username)) | ||
|
||
// When | ||
val votedOption = option.vote(username) | ||
|
||
// Then | ||
assertTrue { votedOption.votes.isEmpty() } | ||
} | ||
|
||
} |
Oops, something went wrong.