package com.alorma.github.sdk.services.pullrequest.story; import android.util.Pair; import com.alorma.github.sdk.bean.dto.response.PullRequest; import com.alorma.github.sdk.bean.dto.response.ReviewComment; import com.alorma.github.sdk.bean.info.IssueInfo; import com.alorma.github.sdk.bean.issue.IssueEvent; import com.alorma.github.sdk.bean.issue.IssueStoryComment; import com.alorma.github.sdk.bean.issue.IssueStoryComparators; import com.alorma.github.sdk.bean.issue.IssueStoryDetail; import com.alorma.github.sdk.bean.issue.IssueStoryEvent; import com.alorma.github.sdk.bean.issue.IssueStoryReviewComments; import com.alorma.github.sdk.bean.issue.PullRequestStory; import com.alorma.github.sdk.services.client.BaseInfiniteCallback; import com.alorma.github.sdk.services.client.GithubClient; import com.alorma.github.sdk.services.issues.story.GithubCommentReactionsIssueMapper; import com.alorma.github.sdk.services.issues.story.GithubPRReactionsIssueMapper; import com.alorma.github.sdk.services.issues.story.IssueStoryService; import com.alorma.github.sdk.services.pullrequest.PullRequestsService; import core.GithubComment; import core.issues.Label; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import retrofit.RestAdapter; import rx.Observable; import rx.functions.Func1; public class PullRequestStoryLoader extends GithubClient<PullRequestStory> { private final IssueInfo issueInfo; private final String owner; private final String repo; private final int num; private final IssueStoryService issueStoryService; private final PullRequestsService pullRequestsService; private PullRequestStoryService pullRequestStoryService; public PullRequestStoryLoader(IssueInfo info) { super(); this.issueInfo = info; this.owner = issueInfo.repoInfo.owner; this.repo = issueInfo.repoInfo.name; this.num = issueInfo.num; pullRequestStoryService = getRestAdapter().create(PullRequestStoryService.class); issueStoryService = getRestAdapter().create(IssueStoryService.class); pullRequestsService = getRestAdapter().create(PullRequestsService.class); } @Override protected Observable<PullRequestStory> getApiObservable(RestAdapter restAdapter) { return getPullrequestStory(); } private Observable<PullRequestStory> getPullrequestStory() { return Observable.zip(getPullRequestObs(), getIssueDetailsObservable(), (pullRequest, details) -> { PullRequestStory pullRequestStory = new PullRequestStory(); pullRequestStory.item = pullRequest; pullRequestStory.details = details; Collections.sort(pullRequestStory.details, IssueStoryComparators.ISSUE_STORY_DETAIL_COMPARATOR); return pullRequestStory; }); } private Observable<PullRequest> getPullRequestObs() { Observable<PullRequest> pullRequestObservable = pullRequestStoryService.detailObs(owner, repo, num) .map(new GithubPRReactionsIssueMapper()); return Observable.zip(pullRequestObservable, getLabelsObs(), (pullRequest, labels) -> { pullRequest.labels = labels; return pullRequest; }); } private Observable<List<IssueStoryDetail>> getIssueDetailsObservable() { Observable<IssueStoryDetail> commentsDetailsObs = getCommentsDetailsObs(); Observable<IssueStoryDetail> eventDetailsObs = getEventDetailsObs(); Observable<IssueStoryDetail> reviewCommentsObs = getReviewCommentsDetailsObs(); Observable<IssueStoryDetail> details = Observable.mergeDelayError(eventDetailsObs, reviewCommentsObs); return Observable.mergeDelayError(commentsDetailsObs, details) .toSortedList((issueStoryDetail, issueStoryDetail2) -> { return ((Long) issueStoryDetail.createdAt()).compareTo(issueStoryDetail2.createdAt()); }); } private Observable<List<GithubComment>> getCommentsObs() { return Observable.create(new BaseInfiniteCallback<List<GithubComment>>() { @Override public void execute() { issueStoryService.comments(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, this); } @Override protected void executePaginated(int nextPage) { issueStoryService.comments(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, nextPage, this); } }); } private Observable<IssueStoryDetail> getCommentsDetailsObs() { return getCommentsObs().flatMap(githubComments -> Observable.from(githubComments) .map(new GithubCommentReactionsIssueMapper()) .map((Func1<GithubComment, IssueStoryDetail>) githubComment -> { long time = getMilisFromDateClearDay(githubComment.created_at); IssueStoryComment detail = new IssueStoryComment(githubComment); detail.created_at = time; return detail; })); } private Observable<List<IssueEvent>> getEventsObs() { return Observable.create(new BaseInfiniteCallback<List<IssueEvent>>() { @Override public void execute() { issueStoryService.events(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, this); } @Override protected void executePaginated(int nextPage) { issueStoryService.events(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, nextPage, this); } }); } private Observable<IssueStoryDetail> getEventDetailsObs() { return getEventsObs().flatMap(issueEvents -> Observable.from(issueEvents) .filter(issueEvent -> validEvent(issueEvent.event)) .map((Func1<IssueEvent, IssueStoryDetail>) issueEvent -> { long time = getMilisFromDateClearDay(issueEvent.created_at); IssueStoryEvent detail = new IssueStoryEvent(issueEvent); detail.created_at = time; return detail; })); } private Observable<List<ReviewComment>> getReviewCommentsObs() { return Observable.create(new BaseInfiniteCallback<List<ReviewComment>>() { @Override public void execute() { pullRequestsService.reviewComments(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, this); } @Override protected void executePaginated(int nextPage) { pullRequestsService.reviewComments(issueInfo.repoInfo.owner, issueInfo.repoInfo.name, issueInfo.num, nextPage, this); } }); } private Observable<IssueStoryDetail> getReviewCommentsDetailsObs() { return getReviewCommentsObs().flatMap(reviewComments1 -> { Map<String, Pair<String, List<ReviewComment>>> comments = new HashMap<>(); for (ReviewComment reviewComment : reviewComments1) { String key = reviewComment.path + reviewComment.position; if (comments.get(key) == null) { comments.put(key, new Pair<>(reviewComment.diff_hunk, new ArrayList<>())); } comments.get(key).second.add(reviewComment); } return Observable.from(comments.values()); }).map(pair -> { ReviewComment reviewComment = pair.second.get(0); long time = getMilisFromDateClearDay(reviewComment.created_at); return new IssueStoryReviewComments(pair, time, reviewComment.user); }); } private Observable<List<Label>> getLabelsObs() { return Observable.create(new BaseInfiniteCallback<List<Label>>() { @Override public void execute() { issueStoryService.labels(owner, repo, num, this); } @Override protected void executePaginated(int nextPage) { issueStoryService.labels(owner, repo, num, nextPage, this); } }); } private long getMilisFromDateClearDay(String createdAt) { DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); DateTime dt = formatter.parseDateTime(createdAt); return dt.minuteOfDay().roundFloorCopy().getMillis(); } private boolean validEvent(String event) { return !(event.equals("mentioned") || event.equals("subscribed") || event.equals("unsubscribed") || event.equals("labeled") || event.equals("unlabeled")); } @Override public String getAcceptHeader() { return "application/vnd.github.v3.full+json"; } }