Skip to content
This repository has been archived by the owner on Apr 27, 2023. It is now read-only.

Commit

Permalink
Merge pull request #65 from softwaremill/hit-them-all
Browse files Browse the repository at this point in the history
Hit them all
  • Loading branch information
lukaszlenart committed Feb 29, 2016
2 parents 834a2be + dc1274e commit 911b8a4
Show file tree
Hide file tree
Showing 23 changed files with 305 additions and 10 deletions.
22 changes: 21 additions & 1 deletion WEBHOOKS.adoc
Expand Up @@ -12,7 +12,8 @@ These are the supported hooks:
- `like-hook` - occurs when a user likes a given change
- `unlike-hook` - occurs if a user dislikes the previously liked change
- `comment-added-hook` - occurs when a user post a comment
- `commit-reviewed` - occurs when a user hit `Reviewed` button
- `commit-reviewed-hook` - occurs when a user hit `Reviewed` button
- `all-commits-reviewed-hook` - occurs when a user hit a button and marked all commits to be reviewed as reviewed at once in current repo for current branch
- `new-commits-loaded-hook` - occurs after loading by Codebrag new commits from repo
- `new-user-registered-hook` - occurs on registering a new user in the system
Expand Down Expand Up @@ -162,6 +163,25 @@ Right now Codebrag sends hooks as JSON encoded as UTF-8, below is the list of fo
}
----

### all-commits-reviewed-hook

[source,json]
----
{
"repoName": string,
"branchName": string,
"reviewedBy": {
"name": string,
"emailLowerCase": string,
"aliases": [{
"alias": string
}]
},
"hookName": "all-commitc-reviewed-hook",
"hookDate": date
}
----

### comment-added-hook

[source,json]
Expand Down
@@ -0,0 +1,33 @@
package com.softwaremill.codebrag.domain.reactions

import com.softwaremill.codebrag.common._
import org.bson.types.ObjectId
import org.joda.time.DateTime

/**
* Describes event when someone reviewed all commits for given branch at once (hit Mark All Reviewed)
*/
case class AllCommitsReviewedEvent(
repoName: String,
branchName: String,
userIdArg: ObjectId
)(implicit clock: Clock)
extends Event
with StatisticEvent
with Hookable {

val hookName = "all-commits-reviewed-hook"

def eventType = AllCommitsReviewedEvent.EventType

def timestamp: DateTime = clock.nowUtc

def userId = userIdArg

def toEventStream: String = s"User $userIdArg reviewed all commits on branch $branchName"

}

object AllCommitsReviewedEvent {
val EventType = "AllCommitsReviewed"
}
2 changes: 1 addition & 1 deletion codebrag-rest/src/main/scala/ScalatraBootstrap.scala
Expand Up @@ -73,7 +73,7 @@ class ScalatraBootstrap extends LifeCycle with Logging {
context.mount(new UserAliasesEndpoint(authenticator, addUserAliasUseCase, deleteUserAliasUseCase), Prefix + UserAliasesEndpoint.MappingPath)
context.mount(new UsersSettingsServlet(authenticator, userDao, changeUserSettingsUseCase), Prefix + "users/settings")
context.mount(new CommitsServlet(authenticator, toReviewCommitsFinder, allCommitsFinder, reactionFinder, addCommentUseCase,
reviewCommitUseCase, userReactionService, userDao, diffWithCommentsService, unlikeUseCaseFactory, likeUseCase), Prefix + CommitsServlet.MAPPING_PATH)
reviewCommitUseCase, reviewAllCommitsUseCase, userReactionService, userDao, diffWithCommentsService, unlikeUseCaseFactory, likeUseCase), Prefix + CommitsServlet.MAPPING_PATH)
context.mount(new FollowupsServlet(authenticator, followupFinder, followupDoneUseCase), Prefix + FollowupsServlet.MappingPath)
context.mount(new VersionServlet, Prefix + "version")
context.mount(new ConfigServlet(config, authenticator), Prefix + "config")
Expand Down
10 changes: 10 additions & 0 deletions codebrag-rest/src/main/scala/com/softwaremill/codebrag/Beans.scala
Expand Up @@ -68,6 +68,16 @@ trait Beans extends ActorSystemSupport with Daos {
lazy val loginUserUseCase = new LoginUserUseCase(userDao, userFinder)
lazy val addCommentUseCase = new AddCommentUseCase(userReactionService, followupService, eventBus)
lazy val reviewCommitUseCase = new ReviewCommitUseCase(commitInfoDao, reviewedCommitsCache, eventBus)

lazy val reviewAllCommitsUseCase = new ReviewAllCommitsUseCase(
userDao,
eventBus,
commitInfoDao,
repositoriesCache,
new ToReviewBranchCommitsFilter(reviewedCommitsCache, config),
reviewedCommitsCache
)

lazy val unlikeUseCaseFactory = new UnlikeUseCase(likeValidator, userReactionService)
lazy val likeUseCase = new LikeUseCase(userReactionService)
lazy val changeUserSettingsUseCase = new ChangeUserSettingsUseCase(userDao)
Expand Down
Expand Up @@ -2,15 +2,14 @@ package com.softwaremill.codebrag.rest

import org.scalatra.{BadRequest, NotFound}
import com.softwaremill.codebrag.service.diff.DiffWithCommentsService
import org.bson.types.ObjectId
import CommitsEndpoint._
import com.softwaremill.codebrag.common.paging.PagingCriteria
import PagingCriteria.Direction
import com.softwaremill.codebrag.common.paging.PagingCriteria
import com.softwaremill.codebrag.finders.commits.toreview.ToReviewCommitsFinder
import com.softwaremill.codebrag.finders.commits.all.AllCommitsFinder
import com.softwaremill.codebrag.finders.browsingcontext.UserBrowsingContext
import com.softwaremill.codebrag.usecases.reactions.ReviewCommitUseCase
import com.softwaremill.codebrag.usecases.reactions.{ReviewAllCommitsUseCase, ReviewCommitUseCase}

trait CommitsEndpoint extends JsonServletWithAuthentication {

Expand All @@ -20,6 +19,7 @@ trait CommitsEndpoint extends JsonServletWithAuthentication {
def allCommitsFinder: AllCommitsFinder

def reviewCommitUseCase: ReviewCommitUseCase
def reviewAllCommitsUseCase: ReviewAllCommitsUseCase

before() {
haltIfNotAuthenticated()
Expand All @@ -40,6 +40,13 @@ trait CommitsEndpoint extends JsonServletWithAuthentication {
}
}

delete("/:repo", commitsToReview) {
val context = extractBrowsingContext
reviewAllCommitsUseCase.execute(context.repoName, context.branchName, context.userId).left.map { err =>
BadRequest(Map("err" -> err))
}
}

get("/:repo", allCommits) {
val paging = extractPagingCriteria
val context = extractBrowsingContext
Expand Down
Expand Up @@ -10,14 +10,15 @@ import com.softwaremill.codebrag.dao.user.UserDAO
import com.softwaremill.codebrag.dao.finders.reaction.ReactionFinder
import com.softwaremill.codebrag.finders.commits.toreview.ToReviewCommitsFinder
import com.softwaremill.codebrag.finders.commits.all.AllCommitsFinder
import com.softwaremill.codebrag.usecases.reactions.{ReviewCommitUseCase, UnlikeUseCase, LikeUseCase, AddCommentUseCase}
import com.softwaremill.codebrag.usecases.reactions._

class CommitsServlet(val authenticator: Authenticator,
val reviewableCommitsListFinder: ToReviewCommitsFinder,
val allCommitsFinder: AllCommitsFinder,
val reactionFinder: ReactionFinder,
val addCommentUseCase: AddCommentUseCase,
val reviewCommitUseCase: ReviewCommitUseCase,
val reviewAllCommitsUseCase: ReviewAllCommitsUseCase,
val userReactionService: UserReactionService,
val userDao: UserDAO,
val diffService: DiffWithCommentsService,
Expand Down
Expand Up @@ -18,7 +18,7 @@ import com.softwaremill.codebrag.finders.commits.all.AllCommitsFinder
import com.softwaremill.codebrag.finders.browsingcontext.UserBrowsingContext
import com.softwaremill.codebrag.domain.builder.UserAssembler
import com.softwaremill.codebrag.domain.User
import com.softwaremill.codebrag.usecases.reactions.{ReviewCommitUseCase, UnlikeUseCase, LikeUseCase, AddCommentUseCase}
import com.softwaremill.codebrag.usecases.reactions._

class CommitsServletSpec extends AuthenticatableServletSpec {

Expand All @@ -34,6 +34,7 @@ class CommitsServletSpec extends AuthenticatableServletSpec {
var userReactionFinder = mock[ReactionFinder]
var userDao = mock[UserDAO]
var reviewCommitUseCase = mock[ReviewCommitUseCase]
var reviewAllCommitsUseCase = mock[ReviewAllCommitsUseCase]
val User = UserAssembler.randomUser.get
val userReactionService = mock[UserReactionService]
val unlikeUseCaseFactory = mock[UnlikeUseCase]
Expand Down Expand Up @@ -68,6 +69,14 @@ class CommitsServletSpec extends AuthenticatableServletSpec {
}
}

"DELETE /:repo?branch=:branch&filter=to_review" should "remove all commits from to review tasks" in {
val userId = givenStandardAuthenticatedUser()

delete(s"/$repoName?branch=master&filter=to_review") {
verify(reviewAllCommitsUseCase).execute(repoName, "master", userId)
}
}

"GET /:repo with filter=all" should "load all commits" in {
val userId = givenStandardAuthenticatedUser()
val criteria = PagingCriteria.fromBeginning[String](CommitsEndpoint.DefaultPageLimit)
Expand Down Expand Up @@ -130,7 +139,7 @@ class CommitsServletSpec extends AuthenticatableServletSpec {

class TestableCommitsServlet(fakeAuthenticator: Authenticator, fakeScentry: Scentry[User])
extends CommitsServlet(fakeAuthenticator, toReviewCommitsFinder, allCommitsFinder, userReactionFinder, commentActivity,
reviewCommitUseCase, userReactionService, userDao, diffService, unlikeUseCaseFactory, likeUseCase) {
reviewCommitUseCase, reviewAllCommitsUseCase, userReactionService, userDao, diffService, unlikeUseCaseFactory, likeUseCase) {
override def scentry(implicit request: javax.servlet.http.HttpServletRequest) = fakeScentry
}

Expand Down
Expand Up @@ -44,6 +44,10 @@ class EventHookPropagator(
val user = userDao.findById(event.userId)
notifyListeners(CommitReviewedHook(event.commit, user, event.hookName))

case (event: AllCommitsReviewedEvent) if hasListeners(event) =>
val user = userDao.findById(event.userId)
notifyListeners(AllCommitsReviewedHook(event.repoName, event.branchName, user, event.hookName))

case (event: NewCommitsLoadedEvent) if hasListeners(event) =>
val user = event.userId match {
case Some(userId) => userDao.findById(userId)
Expand Down
Expand Up @@ -52,6 +52,17 @@ case class CommitReviewedHook(
override val hookDate: DateTime = clock.nowUtc
}

case class AllCommitsReviewedHook(
repoName: String,
branchName: String,
reviewedBy: Option[User],
override val hookName: String
) (implicit clock: Clock)
extends Hook {

override val hookDate: DateTime = clock.nowUtc
}

case class NewCommitsLoadedHook(
user: Option[User],
repoName: String,
Expand Down
@@ -0,0 +1,55 @@
package com.softwaremill.codebrag.usecases.reactions

import com.softwaremill.codebrag.cache.{RepositoriesCache, UserReviewedCommitsCache}
import com.softwaremill.codebrag.common.{EventBus, Clock}
import com.softwaremill.codebrag.dao.commitinfo.CommitInfoDAO
import com.softwaremill.codebrag.dao.user.UserDAO
import com.softwaremill.codebrag.domain.ReviewedCommit
import com.softwaremill.codebrag.domain.reactions.AllCommitsReviewedEvent
import com.softwaremill.codebrag.finders.commits.toreview.ToReviewBranchCommitsFilter
import com.typesafe.scalalogging.slf4j.Logging
import org.bson.types.ObjectId

/**
* Handles user activity when user wants to mark all commits as reviewed
*/
class ReviewAllCommitsUseCase(
userDao: UserDAO,
eventBus: EventBus,
commitInfoDAO: CommitInfoDAO,
repoCache: RepositoriesCache,
toReviewCommitsFilter: ToReviewBranchCommitsFilter,
reviewedCommitsCache: UserReviewedCommitsCache
)
(implicit clock: Clock) extends Logging {

type ReviewAllCommitsResult = Either[String, Unit]

def execute(repoName: String, branchName: String, userId: ObjectId): ReviewAllCommitsResult = {

val result = for {
user <- userDao.findById(userId)
} yield {
logger.debug(s"Using $user to find all commits to review")

val allBranchCommits = repoCache.getBranchCommits(repoName, branchName)
val toReviewBranchCommits = toReviewCommitsFilter.filterCommitsToReview(allBranchCommits, user, repoName)

logger.debug(s"Found ${toReviewBranchCommits.size} commits to mark as reviewed")

commitInfoDAO.findByShaList(repoName, toReviewBranchCommits).foreach { commit =>
val reviewedCommit = ReviewedCommit(commit.sha, user.id, repoName, clock.nowUtc)

logger.info(s"Marking commit ${reviewedCommit.sha} as reviewed by user $user")
reviewedCommitsCache.markCommitAsReviewed(reviewedCommit)
}

eventBus.publish(AllCommitsReviewedEvent(repoName, branchName, userId))
}

result.toRight {
s"Cannot mark all commits in branch $branchName as reviewed by user $userId"
}
}

}
Expand Up @@ -52,11 +52,15 @@ class EventHookPropagatorSpec
val mockComment = Comment(id, id, id, nowUtc, "test-comment", Some("test2.txt"), Some(321))
val mockPartialCommit = PartialCommitInfo(id, "szach", "commit-loaded", "mocher", "mocher@domain.com", nowUtc)

val repoName = "codebrag"
val branchName = "master"

val hooks = Map(
"like-hook" -> List("http://localhost:8000/"),
"unlike-hook" -> List("http://localhost:8000/"),
"comment-added-hook" -> List("http://localhost:8000/"),
"commit-reviewed-hook" -> List("http://localhost:8000/"),
"all-commits-reviewed-hook" -> List("http://localhost:8000/"),
"new-commits-loaded-hook" -> List("http://localhost:8000/"),
"new-user-registered-hook" -> List("http://localhost:8000/")
)
Expand Down Expand Up @@ -209,6 +213,28 @@ class EventHookPropagatorSpec
remoteHook.toString must include(expected)
}

"propagate AllCommitsReviewed to remote host" in {
actorRef ! AllCommitsReviewedEvent(repoName, branchName, userId)

awaitCond(done)

val expected = s"""
{
"repoName": "codebrag",
"branchName": "master",
"reviewedBy": {
"name": "test",
"emailLowerCase": "test@domain.com",
"aliases": null
},
"hookName": "all-commits-reviewed-hook",
"hookDate": "$nowUtcStr"
}
""".replaceAll(" ", "").replaceAll("\n", "")

remoteHook.toString must include(expected)
}

"propagate NewCommitsLoadedEvent to remote host" in {
actorRef ! NewCommitsLoadedEvent(firstTime = false, "test", "szach", List(mockPartialCommit))

Expand Down
@@ -0,0 +1,74 @@
package com.softwaremill.codebrag.usecases.reactions

import com.softwaremill.codebrag.cache.{BranchCommitCacheEntry, RepositoriesCache, UserReviewedCommitsCache}
import com.softwaremill.codebrag.common.{ClockSpec, EventBus}
import com.softwaremill.codebrag.dao.commitinfo.CommitInfoDAO
import com.softwaremill.codebrag.dao.user.UserDAO
import com.softwaremill.codebrag.domain.{User, PartialCommitInfo, ReviewedCommit}
import com.softwaremill.codebrag.domain.builder.CommitInfoAssembler
import com.softwaremill.codebrag.domain.reactions.AllCommitsReviewedEvent
import com.softwaremill.codebrag.finders.commits.toreview.ToReviewBranchCommitsFilter
import org.bson.types.ObjectId
import org.mockito.Mockito._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.mock.MockitoSugar
import org.scalatest.{BeforeAndAfterEach, FlatSpec}

class ReviewAllCommitsUseCaseSpec
extends FlatSpec with MockitoSugar with ShouldMatchers with BeforeAndAfterEach with ClockSpec {

var eventBus: EventBus = _
var userDAO: UserDAO = _
var commitInfoDao: CommitInfoDAO = _
var repositoriesCache: RepositoriesCache = _
var toReviewBranchCommitsFilter: ToReviewBranchCommitsFilter = _
var reviewedCommitsCache: UserReviewedCommitsCache = _

val userId = ObjectId.get
var user: User = _

var useCase: ReviewAllCommitsUseCase = _

val RepoName = "codebrag"
val BranchName = "master"

val commit = CommitInfoAssembler.randomCommit.withRepo(RepoName).get
val commitCache = BranchCommitCacheEntry(commit.sha, commit.authorName, commit.authorEmail, commit.authorDate)

override def beforeEach() {
eventBus = mock[EventBus]
userDAO = mock[UserDAO]
commitInfoDao = mock[CommitInfoDAO]
repositoriesCache = mock[RepositoriesCache]
toReviewBranchCommitsFilter = mock[ToReviewBranchCommitsFilter]
reviewedCommitsCache = mock[UserReviewedCommitsCache]

user = mock[User]
when(user.id).thenReturn(userId)

useCase = new ReviewAllCommitsUseCase(userDAO, eventBus, commitInfoDao, repositoriesCache, toReviewBranchCommitsFilter, reviewedCommitsCache)

// given
when(userDAO.findById(userId)).thenReturn(Option(user))
when(repositoriesCache.getBranchCommits(RepoName, BranchName)).thenReturn(List(commitCache))
when(toReviewBranchCommitsFilter.filterCommitsToReview(List(commitCache), user, RepoName)).thenReturn(List(commit.sha))
when(commitInfoDao.findByShaList(RepoName, List(commit.sha))).thenReturn(List(PartialCommitInfo(commit)))
}

it should "generate all commits reviewed event" in {
// when
useCase.execute(RepoName, BranchName, userId)

// then
verify(eventBus).publish(AllCommitsReviewedEvent(RepoName, BranchName, user.id))
}

it should "mark all commits as reviewed if commits were found" in {
// when
useCase.execute(RepoName, BranchName, userId)

// then
val expectedCommitReviewed = ReviewedCommit(commit.sha, user.id, RepoName, clock.nowUtc)
verify(reviewedCommitsCache).markCommitAsReviewed(expectedCommitReviewed)
}
}

0 comments on commit 911b8a4

Please sign in to comment.