app/src/main/java/com/sdpteam/connectout/post/model/PostFirebaseDataSource.java
package com.sdpteam.connectout.post.model;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.FirebaseDatabase;
import com.sdpteam.connectout.event.Event;
import com.sdpteam.connectout.event.EventFirebaseDataSource;
import com.sdpteam.connectout.event.nearbyEvents.filter.EventParticipantIdFilter;
import com.sdpteam.connectout.utils.Result;
public class PostFirebaseDataSource implements PostDataSource {
private static final String POSTS = "Posts";
// (MockitoException: Mockito cannot mock this class: class com.google.android.gms.tasks.Task)
public static boolean FORCE_FAIL = false; // workaround because Mocking the isSuccessful() of the Task class is not possible
@Override
public CompletableFuture<Result<String>> savePost(Post post) {
if (post.getId() == null) {
String newId = FirebaseDatabase.getInstance().getReference().child(POSTS).push().getKey();
post = new Post(newId, post.getProfileId(), post.getEventId(), post.getCommentsChatId(), post.getImagesUrls(), post.getNbrLikes(), post.getVisibility(), post.getTitle(),
post.getDescription());
}
final Post finalPost = post;
CompletableFuture<Result<String>> result = new CompletableFuture<>();
FirebaseDatabase.getInstance().getReference().child(POSTS).child(post.getId()).setValue(post)
.addOnCompleteListener(task -> {
if (task.isSuccessful() && !FORCE_FAIL) {
result.complete(new Result<>(finalPost.getId(), true));
} else {
result.complete(new Result<>(null, false, "Post save failed"));
}
});
return result;
}
@Override
public CompletableFuture<Result<String>> deletePost(String postId) {
CompletableFuture<Result<String>> result = new CompletableFuture<>();
FirebaseDatabase.getInstance().getReference().child(POSTS).child(postId).removeValue()
.addOnCompleteListener(task -> {
if (task.isSuccessful() && !FORCE_FAIL) {
result.complete(new Result<>(postId, true));
} else {
result.complete(new Result<>(null, false, "Post save failed"));
}
});
return result;
}
@Override
public CompletableFuture<Result<Post>> fetchPost(String postId) {
final CompletableFuture<Result<Post>> result = new CompletableFuture<>();
final CompletableFuture<Result<Post>> subStep = new CompletableFuture<>();
FirebaseDatabase.getInstance().getReference().child(POSTS).child(postId).get()
.addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult().exists() && !FORCE_FAIL) {
final Post post = task.getResult().getValue(Post.class);
subStep.complete(new Result<>(post, true));
} else {
subStep.complete(new Result<>(null, false, "Error occurred, post may not exist under id " + postId));
}
});
// checking if the user has access to this post :
subStep.thenAcceptAsync(postResult -> {
if (!postResult.isSuccess()) {
result.complete(postResult);
return;
}
final Post post = Objects.requireNonNull(postResult.value());
if (post.getVisibility() == null) {
result.complete(new Result<>(null, false, "Event has visibility set to NULL which is not expected"));
} else if (post.isPublic()) {
result.complete(new Result<>(post, true));
} else if (post.isSemiPrivate()) {
final Event event = (new EventFirebaseDataSource().getEvent(post.getEventId())).join();
if (event == null) {
result.complete(new Result<>(null, false,
"Error the event associated to the post does not exist! (maybe a timeout issue) postId" + postId + " eventId" + post.getEventId()));
} else {
if (event.getOrganizer().equals(post.getProfileId()) || event.getParticipants().contains(post.getProfileId())) {
result.complete(new Result<>(post, true));
} else {
result.complete(new Result<>(null, false, "Error occurred, user has not access to this post"));
}
}
}
}).join();
return result;
}
@Override
public CompletableFuture<Result<List<Post>>> fetchAllPosts(String userId) {
return eventIdsUserCanAccess(userId).thenCompose(allEventsUserCanAccess -> {
final CompletableFuture<Result<List<Post>>> result = new CompletableFuture<>();
FirebaseDatabase.getInstance().getReference().child(POSTS).get().addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult().exists() && !FORCE_FAIL) {
final DataSnapshot dataSnapshot = task.getResult();
final List<Post> postList = StreamSupport.stream(dataSnapshot.getChildren().spliterator(), false)
.map(childSnapshot -> childSnapshot.getValue(Post.class))
.filter(Objects::nonNull)
.filter(post -> post.getProfileId() != null)
.filter(post -> post.getEventId() != null)
.filter(post -> post.getVisibility() != null)
.filter(post -> {
if (post.isSemiPrivate()) {
return allEventsUserCanAccess.contains(post.getEventId());
} else {
return true;
}
})
.collect(Collectors.toList());
result.complete(new Result<>(postList, true));
} else {
result.complete(new Result<>(null, false, "Post fetch failed"));
}
});
return result;
});
}
private CompletableFuture<List<String>> eventIdsUserCanAccess(String userId) {
final EventParticipantIdFilter organizerOrParticipant = new EventParticipantIdFilter(userId);
return new EventFirebaseDataSource()
.getEventsByFilter(organizerOrParticipant)
.thenApply(events -> events.stream().map(Event::getId).filter(Objects::nonNull).collect(Collectors.toList()));
}
@Override
public CompletableFuture<Result<List<Post>>> fetchAllPostsOfEvent(String userId, String eventId) {
return fetchAllPostsFiltered(userId, post -> post.getEventId().equals(eventId));
}
@Override
public CompletableFuture<Result<List<Post>>> fetchPostMadeByUser(String userId, String authorId) {
return fetchAllPostsFiltered(userId, post -> post.getProfileId().equals(authorId));
}
private CompletableFuture<Result<List<Post>>> fetchAllPostsFiltered(String userId, Predicate<Post> postFilter) {
return fetchAllPosts(userId).thenApply(listResult -> {
if (listResult.isSuccess() && !FORCE_FAIL) {
List<Post> postStream = listResult.value().stream()
.filter(postFilter)
.collect(Collectors.toList());
return new Result<>(postStream, true);
} else {
return listResult;
}
});
}
}