diff --git a/.travis.yml b/.travis.yml
index e13823f..eeb37ca 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,6 @@
language: groovy
+jdk:
+ - oraclejdk8
sudo: false
after_success:
- mvn -q -e clean test jacoco:report coveralls:report
diff --git a/pom.xml b/pom.xml
index 8cc59a9..6f548ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
pl.jug.torun
xenia-api
- 0.0.1-SNAPSHOT
+ 2.0.1-SNAPSHOT
jar
Xenia API
diff --git a/src/main/groovy/pl/jug/torun/xenia/draw/DrawController.groovy b/src/main/groovy/pl/jug/torun/xenia/draw/DrawController.groovy
index b652fe6..b42921c 100644
--- a/src/main/groovy/pl/jug/torun/xenia/draw/DrawController.groovy
+++ b/src/main/groovy/pl/jug/torun/xenia/draw/DrawController.groovy
@@ -20,9 +20,12 @@ final class DrawController {
}
@RequestMapping(method = RequestMethod.GET)
- public DrawResult drawWinnerCandidate(@PathVariable("giveAway") GiveAway giveAway, @RequestParam(value = "absent", required = false) Member absentMember) {
+ public DrawResult drawWinnerCandidate(@PathVariable("giveAway") GiveAway giveAway, @RequestParam(value = "absent", required = false) Member absentMember,
+ @RequestParam(value = "skipped", required = false) Member skippedMember) {
if (absentMember) {
drawService.markMemberAsAbsentForCurrentDraw(absentMember, giveAway.event)
+ } else if (skippedMember) {
+ drawService.setGiveAwaySkippedForMember(skippedMember, giveAway)
}
return drawService.drawWinnerCandidate(giveAway)
diff --git a/src/main/groovy/pl/jug/torun/xenia/draw/DrawService.groovy b/src/main/groovy/pl/jug/torun/xenia/draw/DrawService.groovy
index 6786417..fc01aab 100644
--- a/src/main/groovy/pl/jug/torun/xenia/draw/DrawService.groovy
+++ b/src/main/groovy/pl/jug/torun/xenia/draw/DrawService.groovy
@@ -11,16 +11,21 @@ import pl.jug.torun.xenia.events.Event
import pl.jug.torun.xenia.meetup.Member
import pl.jug.torun.xenia.meetup.MemberRepository
+import static org.apache.commons.lang.StringUtils.isNotBlank
+
@Slf4j
@Service
class DrawService {
private final DrawResultRepository drawResultRepository
private final AttendeeRepository attendeeRepository
private final MemberRepository memberRepository
+ private final SkippedGiveAwayContainer skippedGiveAwayContainer
@Autowired
- DrawService(DrawResultRepository drawResultRepository, AttendeeRepository attendeeRepository, MemberRepository memberRepository) {
+ DrawService(DrawResultRepository drawResultRepository, SkippedGiveAwayContainer skippedGiveAwayContainer, AttendeeRepository attendeeRepository,
+ MemberRepository memberRepository) {
this.drawResultRepository = drawResultRepository
+ this.skippedGiveAwayContainer = skippedGiveAwayContainer
this.attendeeRepository = attendeeRepository
this.memberRepository = memberRepository
}
@@ -53,8 +58,12 @@ class DrawService {
log.debug '{} members have won this prize already...', membersWhoHaveWonThisPrizeAlready.size()
members.removeAll(membersWhoHaveWonThisPrizeAlready)
+ Set membersWithSkippedGiveAway = skippedGiveAwayContainer.getMembersByGiveAway(giveAway)
+ log.debug '{} members have skipped this prize already...', membersWithSkippedGiveAway.size()
+ members.removeAll(membersWithSkippedGiveAway)
+
if (giveAway.emailRequired) {
- members = members.findAll { it.email != null && !it.email.empty }
+ members = members.findAll { isNotBlank(it.email) }
}
return members
}
@@ -77,4 +86,10 @@ class DrawService {
attendeeRepository.save(attendee)
}
}
+
+ public void setGiveAwaySkippedForMember(Member member, GiveAway giveAway) {
+ log.debug 'Member {} skipped the draw result for {}', member, giveAway
+ skippedGiveAwayContainer.addSkippedMember(giveAway, member)
+ }
+
}
diff --git a/src/main/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainer.groovy b/src/main/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainer.groovy
new file mode 100644
index 0000000..439309c
--- /dev/null
+++ b/src/main/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainer.groovy
@@ -0,0 +1,23 @@
+package pl.jug.torun.xenia.draw
+
+import org.springframework.stereotype.Component
+import pl.jug.torun.xenia.meetup.Member
+
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentSkipListSet
+
+@Component
+class SkippedGiveAwayContainer {
+
+ private final Map> skippedGiveWays = new ConcurrentHashMap<>()
+
+ Set getMembersByGiveAway(GiveAway giveaway) {
+ (skippedGiveWays[giveaway] ?: new HashSet<>()).asImmutable()
+ }
+
+ void addSkippedMember(GiveAway giveAway, Member member) {
+ skippedGiveWays.computeIfAbsent(giveAway) {
+ new ConcurrentSkipListSet({ m1, m2 -> m1.id.compareTo(m2.id) })
+ }.add(member)
+ }
+}
\ No newline at end of file
diff --git a/src/test/groovy/pl/jug/torun/xenia/draw/DrawServiceSpec.groovy b/src/test/groovy/pl/jug/torun/xenia/draw/DrawServiceSpec.groovy
index d001cd2..b5d49d4 100644
--- a/src/test/groovy/pl/jug/torun/xenia/draw/DrawServiceSpec.groovy
+++ b/src/test/groovy/pl/jug/torun/xenia/draw/DrawServiceSpec.groovy
@@ -4,10 +4,7 @@ import org.joda.time.DateTime
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.test.context.ContextConfiguration
-import pl.jug.torun.xenia.events.Attendee
-import pl.jug.torun.xenia.events.AttendeeRepository
-import pl.jug.torun.xenia.events.Event
-import pl.jug.torun.xenia.events.EventRepository
+import pl.jug.torun.xenia.events.*
import pl.jug.torun.xenia.meetup.Member
import pl.jug.torun.xenia.meetup.MemberRepository
import pl.jug.torun.xenia.prizes.Prize
@@ -18,6 +15,8 @@ import spock.lang.Subject
import java.util.concurrent.atomic.AtomicLong
+import static org.apache.commons.lang.StringUtils.isNotBlank
+
@Stepwise
@DataJpaTest
@ContextConfiguration
@@ -53,7 +52,7 @@ class DrawServiceSpec extends Specification {
private AtomicLong counter = new AtomicLong(0L)
def setup() {
- drawService = new DrawService(drawResultRepository, attendeeRepository, memberRepository)
+ drawService = new DrawService(drawResultRepository, new SkippedGiveAwayContainer(), attendeeRepository, memberRepository)
giveAwayController = new GiveAwayController(giveAwayRepository, prizeRepository, drawResultRepository)
event = event("Test event", DateTime.parse("2015-04-02T20:00:00"))
}
@@ -80,7 +79,7 @@ class DrawServiceSpec extends Specification {
thrown IllegalStateException
}
- def "should confirm that giveaway that requires email can be drawn by attendess with email only"() {
+ def "should confirm that giveaway that requires email can be drawn by attendees with email only"() {
setup:
GiveAway giveAway = giveAwayWithPrizeAndAmount("License", 1, true)
attendee("John")
@@ -94,7 +93,7 @@ class DrawServiceSpec extends Specification {
DrawResult result = drawService.drawWinnerCandidate(giveAway)
then:
- result.member.email != null && result.member.email != ""
+ isNotBlank(result.member.email)
where:
i << (1..RANDOM_TESTS_RUN_LIMIT)
@@ -204,6 +203,21 @@ class DrawServiceSpec extends Specification {
i << (1..RANDOM_TESTS_RUN_LIMIT)
}
+ def "should not allow attendee who skipped a giveaway to win same prize again during the same event"() {
+ setup:
+ GiveAway giveAway = giveAwayWithPrizeAndAmount("Something", 1)
+ attendee("Paul").with {
+ drawService.setGiveAwaySkippedForMember(it.member, giveAway)
+ }
+ attendee("Roger")
+
+ when:
+ Member winner = drawService.drawWinnerCandidate(giveAway).member
+
+ then:
+ winner.name == "Roger"
+ }
+
def "should draw all giveaways and confirm winners"() {
setup:
GiveAway firstGiveAway = giveAwayWithPrizeAndAmount("Spring Boot in Action ebook", 2)
@@ -247,7 +261,18 @@ class DrawServiceSpec extends Specification {
i << (1..RANDOM_TESTS_RUN_LIMIT)
}
+ def "should mark attendee as absent"() {
+ given:
+ Attendee attendee = attendee("Roger")
+ when:
+ drawService.markMemberAsAbsentForCurrentDraw(attendee.member, event)
+ then:
+ storedAttendee(attendee).absent
+ }
+ private Attendee storedAttendee(Attendee attendee) {
+ return attendeeRepository.findAllByMemberId(attendee.member.id)[0]
+ }
private GiveAway giveAwayWithAmount(int amount) {
return giveAwayWithPrizeAndAmount("Test", amount)
@@ -274,7 +299,7 @@ class DrawServiceSpec extends Specification {
}
private Attendee absent(Attendee attendee) {
- attendee.absent = true
- return attendeeRepository.save(attendee)
+ drawService.markMemberAsAbsentForCurrentDraw(attendee.member, event)
+ return attendee
}
}
diff --git a/src/test/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainerSpec.groovy b/src/test/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainerSpec.groovy
new file mode 100644
index 0000000..f214f99
--- /dev/null
+++ b/src/test/groovy/pl/jug/torun/xenia/draw/SkippedGiveAwayContainerSpec.groovy
@@ -0,0 +1,46 @@
+package pl.jug.torun.xenia.draw
+
+import pl.jug.torun.xenia.meetup.Member
+import spock.lang.Specification
+
+class SkippedGiveAwayContainerSpec extends Specification {
+
+ def "should add skipped members for same giveaway"() {
+ given:
+ SkippedGiveAwayContainer container = new SkippedGiveAwayContainer()
+ Member firstMember = new Member(id: 100)
+ Member nextMember = new Member(id: 200)
+ GiveAway giveAway = new GiveAway(id: 1000)
+
+ when:
+ container.addSkippedMember(giveAway, firstMember)
+ container.addSkippedMember(giveAway, nextMember)
+
+ then:
+ container.getMembersByGiveAway(giveAway).with {
+ it.size() == 2 && it[0] == firstMember && it[1] == nextMember
+ }
+ }
+
+ def "should add skipped members for different giveaways"() {
+ given:
+ SkippedGiveAwayContainer container = new SkippedGiveAwayContainer()
+ Member firstMember = new Member(id: 100)
+ Member nextMember = new Member(id: 200)
+ GiveAway firstGiveAway = new GiveAway(id: 1000)
+ GiveAway nextGiveAway = new GiveAway(id: 2000)
+
+ when:
+ container.addSkippedMember(firstGiveAway, firstMember)
+ container.addSkippedMember(nextGiveAway, nextMember)
+
+ then:
+ container.getMembersByGiveAway(firstGiveAway).with {
+ it.size() == 1 && it[0] == firstMember
+ }
+ container.getMembersByGiveAway(nextGiveAway).with {
+ it.size() == 1 && it[0] == nextMember
+ }
+ }
+
+}