Skip to content
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

documentation for Repository, RemoteDataSource, .... #378

Merged
merged 6 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.github.swent.echo.data.model.Event
import com.github.swent.echo.data.model.Tag
import com.github.swent.echo.data.repository.Repository
import com.github.swent.echo.data.repository.SimpleRepository
import com.github.swent.echo.data.repository.SimpleRepository.Companion.ROOT_TAG_ID
import com.github.swent.echo.viewmodels.event.EventViewModel
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
Expand Down Expand Up @@ -185,7 +186,7 @@ class EventScreenTest {
}
setCompose(eventViewModel)
val addTagButton = composeTestRule.onNodeWithTag("add-tag-button-$categoryName")
val tag1 = Tag("1", "Sport", Repository.ROOT_TAG_ID) // first tag of simple repository
val tag1 = Tag("1", "Sport", ROOT_TAG_ID) // first tag of simple repository
val firstTag = composeTestRule.onNodeWithTag("${tag1.name}-select-button")
addTagButton.performClick()
composeTestRule.onNodeWithTag("tag-dialog").assertIsDisplayed()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ package com.github.swent.echo.data.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* A simplified version of the Association data class, used to embed into other data classes
* avoiding to embed the complete heavy Association data class.
*
* @property associationId the unique id of the association
* @property name the name of the association
*/
@Serializable
data class AssociationHeader(
@SerialName("association_id") val associationId: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.github.swent.echo.data.model

/**
* Supertype for other data classes having a unique id. Provides a unified way to get the unique id
* of the object.
*/
sealed class DataModel {
fun getId(): String =
when (this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* A creator of an event.
* A creator of an event. A simplified version of the Event data class, used to embed into other
* data classes avoiding to embed the complete heavy Event data class.
*
* @property userId The ID of the user who created the event.
* @property name The name of the creator. This same name as in the [UserProfile] corresponding to
Expand Down
115 changes: 108 additions & 7 deletions app/src/main/java/com/github/swent/echo/data/repository/Repository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import com.github.swent.echo.data.model.Event
import com.github.swent.echo.data.model.Tag
import com.github.swent.echo.data.model.UserProfile

/**
* The repository is the single point of access for all data consumed by the app. It communicates
* with the RemoteDataSource and manages synchronisation/caching with the LocalDataSource.
*/
interface Repository {

companion object {
/** The root tag of the tag tree. Note the sub-tags of this tag are the top-level tags. */
const val ROOT_TAG_ID = "1d253a7e-eb8c-4546-bc98-1d3adadcffe8"
}

/**
* Gets the association info details. If the association does not exist, returns null.
*
Expand All @@ -21,8 +20,20 @@ interface Repository {
*/
suspend fun getAssociation(associationId: String): Association?

/**
* Gets the associations info details. If the associations do not exist, returns an emptyList().
*
* @param associationIds The ids of the associations to query.
* @return The Associations with the given associationIds or emptyList() if the associations do
* not exist
*/
suspend fun getAssociations(associationIds: List<String>): List<Association>

/**
* Gets all associations info details.
*
* @return The Associations or emptyList() if no association exists
*/
suspend fun getAllAssociations(): List<Association>

/**
Expand All @@ -34,22 +45,73 @@ interface Repository {
suspend fun getEvent(eventId: String): Event?

/**
* Creates a new event. The [Event.eventId] will be generated by the server and returned.
* Creates a new event. The [Event.eventId] will be generated by the server and returned. The
* creatorId of the event needs to correspond to the userId of the currently authenticated user.
* If set on the event, the organizerId needs to be a valid associationId the creatorId is a
* committeeMember of.
*
* @param event The id of the created event.
* @param event The event to save.
* @return eventId The id of the created event.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue.
*/
suspend fun createEvent(event: Event): String

/**
* Sets the attributes of an existing event. The [Event.eventId] will be generated by the server
* and returned. The creatorId of the event needs to correspond to the userId of the currently
* authenticated user. If set on the event, the organizerId needs to be a valid associationId
* the creatorId is a committeeMember of.
*
* @param event The event to be updated.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue.
*/
suspend fun setEvent(event: Event)

/**
* Deletes an event. The creatorId of the event needs to correspond to the userId of the
* currently authenticated user.
*
* @param event The event to be deleted.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue.
*/
suspend fun deleteEvent(event: Event)

/**
* Gets all event info details.
*
* @return The Events or emptyList() if no event exists.
*/
suspend fun getAllEvents(): List<Event>

/**
* Registers a user as a participant on a given event. The userId needs to correspond to the
* currently authenticated user.
*
* @param userId of the user to register as participant.
* @param event the event the user wants to participate in.
* @return Boolean whether the user has been successfully subscribed.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue.
*/
suspend fun joinEvent(userId: String, event: Event): Boolean

/**
* Registers a user as not being a participant on a given event anymore. The userId needs to
* correspond to the currently authenticated user.
*
* @param userId of the user to unregister as participant.
* @param event the event the user wants not to participate in anymore.
* @return Boolean whether the user has been successfully unsubscribed.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue.
*/
suspend fun leaveEvent(userId: String, event: Event): Boolean

/**
* Get all events a given user is registered as a participant of. The userId needs to correspond
* to the currently authenticated user.
*
* @param userId the user for which to query joined events
* @return List of joined Events
*/
suspend fun getJoinedEvents(userId: String): List<Event>

/**
Expand All @@ -68,6 +130,11 @@ interface Repository {
*/
suspend fun getSubTags(tagId: String): List<Tag>

/**
* Get all tags.
*
* @return All the existing tags.
violoncelloCH marked this conversation as resolved.
Show resolved Hide resolved
*/
suspend fun getAllTags(): List<Tag>

/**
Expand All @@ -78,8 +145,22 @@ interface Repository {
*/
suspend fun getUserProfile(userId: String): UserProfile?

/**
* Changes or creates the profile for a given user. The [userProfile.userId] needs to correspond
* to the authenticated user.
*
* @param userProfile the modified profile of the user
* @throws RepositoryStoreWhileNoInternetException if there is no internet or a network issue
*/
suspend fun setUserProfile(userProfile: UserProfile)

/**
* Deletes the profile for a given user. The [userProfile.userId] needs to correspond to the
* authenticated user.
*
* @param userProfile the modified profile of the user
* @throws RepositoryStoreWhileNoInternetException if there is no internet or a network issue
*/
suspend fun deleteUserProfile(userProfile: UserProfile)

/**
Expand All @@ -90,11 +171,31 @@ interface Repository {
*/
suspend fun getUserProfilePicture(userId: String): ByteArray?

/**
* Set the picture of a user's profile. The userId needs to correspond to the currently
* authenticated user.
*
* @param userId, the id of the user for which to set the profile picture
* @param picture a ByteArray of the data of the image.
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue
*/
suspend fun setUserProfilePicture(userId: String, picture: ByteArray)

/**
* Delete the picture of a user's profile. The userId needs to correspond to the currently
* authenticated user.
*
* @param userId, the id of the user for which to set the profile picture
* @throws RepositoryStoreWhileNoInternetException in case of no internet or a network issue
*/
suspend fun deleteUserProfilePicture(userId: String)
}

/**
* Exception class in case a store operation fails due to network conditions.
*
* @param objectTryingToStore The object type that has been tried to be stored.
*/
class RepositoryStoreWhileNoInternetException(objectTryingToStore: String) :
Exception(
"Storing/Deleting a " + objectTryingToStore + " while the App is offline is not supported"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ import com.github.swent.echo.data.repository.datasources.RemoteDataSource
import com.github.swent.echo.data.repository.datasources.RemoteDataSourceRequestMaxRetryExceededException
import java.time.ZonedDateTime

/**
* Implementation of [Repository] that uses a [RemoteDataSource], a [LocalDataSource] as well as a
* [FileCache].
*
* @param remoteDataSource The RemoteDataSource implementation used by the repository.
* @param localDataSource The LocalDataSource implementation used by the repository for
* caching/synchronisation of structured data.
* @param networkService The NetworkService implementation determining whether the device is
* connected to the internet.
* @param fileCache The FileCache implementation used by the repository for caching files/media.
*/
class RepositoryImpl(
private val remoteDataSource: RemoteDataSource,
private val localDataSource: LocalDataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import com.github.swent.echo.data.model.UserProfile
class SimpleRepository(authenticationService: AuthenticationService) : Repository {

companion object {
/** The root tag of the tag tree. Note the sub-tags of this tag are the top-level tags. */
const val ROOT_TAG_ID = "1d253a7e-eb8c-4546-bc98-1d3adadcffe8"

const val NUM_OF_TOP_LEVEL_TAGS = 3
const val NUM_OF_HARDCODED_TAGS = 6
}
Expand All @@ -30,13 +33,13 @@ class SimpleRepository(authenticationService: AuthenticationService) : Repositor
private val eventJoins = mutableMapOf<String, List<Event>>()
private val tags =
mutableSetOf(
Tag("1", "Sport", Repository.ROOT_TAG_ID),
Tag("2", "Culture", Repository.ROOT_TAG_ID),
Tag("3", "Music", Repository.ROOT_TAG_ID),
Tag("1", "Sport", ROOT_TAG_ID),
Tag("2", "Culture", ROOT_TAG_ID),
Tag("3", "Music", ROOT_TAG_ID),
Tag("4", "Football", "1"),
Tag("5", "Basketball", "1"),
Tag("6", "Theatre", "2"),
Tag(Repository.ROOT_TAG_ID, "ROOT TAG: DO NOT DELETE"),
Tag(ROOT_TAG_ID, "ROOT TAG: DO NOT DELETE"),
Tag(SECTION_ROOT_TAG_ID, "Section"),
Tag(SEMESTER_ROOT_TAG_ID, "Semester")
)
Expand Down
Loading