Skip to content
This repository was archived by the owner on May 9, 2019. It is now read-only.

Commit ef0b3d5

Browse files
yg-apazaTim Moore
authored and
Tim Moore
committed
Set delivery price (#137)
* Add entity tests * Add service integration test * Add entity and service call * Remove unused import, comments and fix typo * Add UI to set delivery price
1 parent 54a9c35 commit ef0b3d5

File tree

18 files changed

+256
-48
lines changed

18 files changed

+256
-48
lines changed

transaction-api/src/main/java/com/example/auction/transaction/api/TransactionInfo.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ public final class TransactionInfo {
1616
private final UUID winner;
1717
private final ItemData itemData;
1818
private final int itemPrice;
19-
private final int deliveryPrice;
19+
private final Optional<Integer> deliveryPrice;
2020
private final Optional<DeliveryInfo> deliveryInfo;
2121
private final TransactionInfoStatus status;
2222
//private final PaymentInfo paymentInfo;
2323

2424
@JsonCreator
25-
public TransactionInfo(UUID itemId, UUID creator, UUID winner, ItemData itemData, int itemPrice, int deliveryPrice, Optional<DeliveryInfo> deliveryInfo, TransactionInfoStatus status) {
25+
public TransactionInfo(UUID itemId, UUID creator, UUID winner, ItemData itemData, int itemPrice, Optional<Integer> deliveryPrice, Optional<DeliveryInfo> deliveryInfo, TransactionInfoStatus status) {
2626
this.itemId = itemId;
2727
this.creator = creator;
2828
this.winner = winner;

transaction-api/src/main/java/com/example/auction/transaction/api/TransactionService.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public interface TransactionService extends Service {
3030

3131
ServiceCall<DeliveryInfo, Done> submitDeliveryDetails(UUID itemId);
3232

33-
//ServiceCall<Integer, Done> setDeliveryPrice(UUID itemId);
33+
ServiceCall<Integer, Done> setDeliveryPrice(UUID itemId);
3434

3535
//ServiceCall<PaymentInfo, Done> submitPaymentDetails(UUID itemId);
3636

@@ -53,7 +53,8 @@ ServiceCall<NotUsed, PaginatedSequence<TransactionSummary>> getTransactionsForUs
5353
@Override
5454
default Descriptor descriptor() {
5555
return named("transaction").withCalls(
56-
pathCall("/api/transaction/:id", this::submitDeliveryDetails),
56+
pathCall("/api/transaction/:id/deliverydetails", this::submitDeliveryDetails),
57+
pathCall("/api/transaction/:id/deliveryprice", this::setDeliveryPrice),
5758
pathCall("/api/transaction/:id", this::getTransaction),
5859
pathCall("/api/transaction?status&pageNo&pageSize", this::getTransactionsForUser)
5960
).withPathParamSerializer(

transaction-impl/src/main/java/com/example/auction/transaction/impl/Transaction.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public class Transaction implements Jsonable {
1616
private final UUID winner;
1717
private final ItemData itemData;
1818
private final int itemPrice;
19-
private final int deliveryPrice;
19+
private final Optional<Integer> deliveryPrice;
2020
private final Optional<DeliveryData> deliveryData;
2121

2222
@JsonCreator
23-
private Transaction(UUID itemId, UUID creator, UUID winner, ItemData itemData, int itemPrice, int deliveryPrice, Optional<DeliveryData> deliveryData) {
23+
private Transaction(UUID itemId, UUID creator, UUID winner, ItemData itemData, int itemPrice, Optional<Integer> deliveryPrice, Optional<DeliveryData> deliveryData) {
2424
this.itemId = itemId;
2525
this.creator = creator;
2626
this.winner = winner;
@@ -36,11 +36,15 @@ public Transaction(UUID itemId, UUID creator, UUID winner, ItemData itemData, in
3636
this.winner = winner;
3737
this.itemData = itemData;
3838
this.itemPrice = itemPrice;
39-
this.deliveryPrice = 0;
39+
this.deliveryPrice = Optional.empty();
4040
this.deliveryData = Optional.empty();
4141
}
4242

43-
public Transaction withDeliveryData(DeliveryData deliveryData){
43+
public Transaction withDeliveryData(DeliveryData deliveryData) {
4444
return new Transaction(itemId, creator, winner, itemData, itemPrice, deliveryPrice, Optional.of(deliveryData));
4545
}
46+
47+
public Transaction withDeliveryPrice(int deliveryPrice) {
48+
return new Transaction(itemId, creator, winner, itemData, itemPrice, Optional.of(deliveryPrice), deliveryData);
49+
}
4650
}

transaction-impl/src/main/java/com/example/auction/transaction/impl/TransactionCommand.java

+12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ public SubmitDeliveryDetails(UUID userId, DeliveryData deliveryData) {
3636
}
3737
}
3838

39+
@Value
40+
final class SetDeliveryPrice implements TransactionCommand, ReplyType<Done> {
41+
private final UUID userId;
42+
private final int deliveryPrice;
43+
44+
@JsonCreator
45+
public SetDeliveryPrice(UUID userId, int deliveryPrice) {
46+
this.userId = userId;
47+
this.deliveryPrice = deliveryPrice;
48+
}
49+
}
50+
3951
@Value
4052
final class GetTransaction implements TransactionCommand, ReplyType<TransactionState> {
4153
private final UUID userId;

transaction-impl/src/main/java/com/example/auction/transaction/impl/TransactionEntity.java

+14
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ private Behavior negotiatingDelivery(TransactionState state) {
6969
state().updateDeliveryData(evt.getDeliveryData())
7070
);
7171

72+
builder.setCommandHandler(SetDeliveryPrice.class, (cmd, ctx) -> {
73+
if(cmd.getUserId().equals(state().getTransaction().get().getCreator())) {
74+
return ctx.thenPersist(new DeliveryPriceUpdated(entityUUID(), cmd.getDeliveryPrice()), (e) ->
75+
ctx.reply(Done.getInstance())
76+
);
77+
}
78+
else
79+
throw new Forbidden("Only the item creator can set the delivery price");
80+
});
81+
82+
builder.setEventHandler(DeliveryPriceUpdated.class, evt ->
83+
state().updateDeliveryPrice(evt.getDeliveryPrice())
84+
);
85+
7286
addGetTransactionHandler(builder);
7387

7488
return builder.build();

transaction-impl/src/main/java/com/example/auction/transaction/impl/TransactionEvent.java

+12
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,16 @@ public DeliveryDetailsSubmitted(UUID itemId, DeliveryData deliveryData) {
4343
this.deliveryData = deliveryData;
4444
}
4545
}
46+
47+
@Value
48+
final class DeliveryPriceUpdated implements TransactionEvent {
49+
private final UUID itemId;
50+
private final int deliveryPrice;
51+
52+
@JsonCreator
53+
public DeliveryPriceUpdated(UUID itemId, int deliveryPrice) {
54+
this.itemId = itemId;
55+
this.deliveryPrice = deliveryPrice;
56+
}
57+
}
4658
}

transaction-impl/src/main/java/com/example/auction/transaction/impl/TransactionServiceImpl.java

+8
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ public ServiceCall<DeliveryInfo, Done> submitDeliveryDetails(UUID itemId) {
6262
});
6363
}
6464

65+
@Override
66+
public ServiceCall<Integer, Done> setDeliveryPrice(UUID itemId) {
67+
return authenticated(userId -> deliveryPrice -> {
68+
SetDeliveryPrice setDeliveryPrice = new SetDeliveryPrice(userId, deliveryPrice);
69+
return entityRef(itemId).ask(setDeliveryPrice);
70+
});
71+
}
72+
6573
@Override
6674
public ServiceCall<NotUsed, TransactionInfo> getTransaction(UUID itemId) {
6775
return authenticated(userId -> request -> {

transaction-impl/src/main/java/com/example/auction/transaction/impl/TransactionState.java

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ public TransactionState updateDeliveryData(DeliveryData deliveryData) {
3434
return update(i -> i.withDeliveryData(deliveryData), status);
3535
}
3636

37+
public TransactionState updateDeliveryPrice(int deliveryPrice) {
38+
return update(i -> i.withDeliveryPrice(deliveryPrice), status);
39+
}
40+
3741
private TransactionState update(Function<Transaction, Transaction> updateFunction, TransactionStatus status) {
3842
assert transaction.isPresent();
3943
return new TransactionState(transaction.map(updateFunction), status);

transaction-impl/src/test/java/com/example/auction/transaction/impl/TransactionEntityTest.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ public static void shutdownActorSystem() {
4040
private final UUID winner = UUID.randomUUID();
4141
private final ItemData itemData = new ItemData("title", "desc", "EUR", 1, 10, Duration.ofMinutes(10), Optional.empty());
4242
private final DeliveryData deliveryData = new DeliveryData("Addr1", "Addr2", "City", "State", 27, "Country");
43+
private final int deliveryPrice = 500;
4344

4445
private final Transaction transaction = new Transaction(itemId, creator, winner, itemData, 2000);
4546

4647
private final StartTransaction startTransaction = new StartTransaction(transaction);
4748
private final SubmitDeliveryDetails submitDeliveryDetails = new SubmitDeliveryDetails(winner, deliveryData);
49+
private final SetDeliveryPrice setDeliveryPrice = new SetDeliveryPrice(creator, deliveryPrice);
4850
private final GetTransaction getTransaction = new GetTransaction(creator);
4951

5052
@Before
@@ -93,11 +95,28 @@ public void shouldAllowSeeTransactionByItemCreator() {
9395
}
9496

9597
@Test(expected = Forbidden.class)
96-
public void shouldForbidSeeTransactionByNonWinnerNonCreator() throws Throwable{
98+
public void shouldForbidSeeTransactionByNonWinnerNonCreator() throws Throwable {
9799
driver.run(startTransaction);
98100
UUID hacker = UUID.randomUUID();
99101
GetTransaction invalid = new GetTransaction(hacker);
100102
driver.run(invalid);
101103
}
104+
105+
@Test
106+
public void shouldEmitEventWhenSettingDeliveryPrice() {
107+
driver.run(startTransaction);
108+
Outcome<TransactionEvent, TransactionState> outcome = driver.run(setDeliveryPrice);
109+
assertThat(outcome.state().getStatus(), equalTo(TransactionStatus.NEGOTIATING_DELIVERY));
110+
assertThat(outcome.state().getTransaction().get().getDeliveryPrice().get(), equalTo(deliveryPrice));
111+
assertThat(outcome.events(), hasItem(new DeliveryPriceUpdated(itemId, deliveryPrice)));
112+
}
113+
114+
@Test(expected = Forbidden.class)
115+
public void shouldForbidSettingDeliveryPriceByNonSeller() {
116+
driver.run(startTransaction);
117+
UUID hacker = UUID.randomUUID();
118+
SetDeliveryPrice invalid = new SetDeliveryPrice(hacker, deliveryPrice);
119+
driver.run(invalid);
120+
}
102121
}
103122

transaction-impl/src/test/java/com/example/auction/transaction/impl/TransactionServiceImplIntegrationTest.java

+31-11
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ public static void afterAll() {
6565
private final ItemEvent.AuctionFinished auctionFinished = new ItemEvent.AuctionFinished(itemId, item);
6666

6767
private final DeliveryInfo deliveryInfo = new DeliveryInfo("ADDR1", "ADDR2", "CITY", "STATE", 27, "COUNTRY");
68+
private final int deliveryPrice = 500;
6869

69-
private final TransactionInfo transactionInfoStarted = new TransactionInfo(itemId, creatorId, winnerId, itemData, item.getPrice(), 0, Optional.empty(), TransactionInfoStatus.NEGOTIATING_DELIVERY);
70-
private final TransactionInfo transactionInfoWithDelivery = new TransactionInfo(itemId, creatorId, winnerId, itemData, item.getPrice(), 0, Optional.of(deliveryInfo), TransactionInfoStatus.NEGOTIATING_DELIVERY);
71-
70+
private final TransactionInfo transactionInfoStarted = new TransactionInfo(itemId, creatorId, winnerId, itemData, item.getPrice(), Optional.empty(), Optional.empty(), TransactionInfoStatus.NEGOTIATING_DELIVERY);
71+
private final TransactionInfo transactionInfoWithDeliveryInfo = new TransactionInfo(itemId, creatorId, winnerId, itemData, item.getPrice(), Optional.empty(), Optional.of(deliveryInfo), TransactionInfoStatus.NEGOTIATING_DELIVERY);
72+
private final TransactionInfo transactionInfoWithDeliveryPrice = new TransactionInfo(itemId, creatorId, winnerId, itemData, item.getPrice(), Optional.of(deliveryPrice), Optional.empty(), TransactionInfoStatus.NEGOTIATING_DELIVERY);
7273

7374
@Test
7475
public void shouldCreateTransactionOnAuctionFinished() {
@@ -101,17 +102,19 @@ public void shouldSubmitDeliveryDetails() throws Throwable {
101102

102103
eventually(new FiniteDuration(15, SECONDS), () -> {
103104
TransactionInfo retrievedTransaction = retrieveTransaction(itemId, creatorId);
104-
assertEquals(retrievedTransaction, transactionInfoWithDelivery);
105+
assertEquals(retrievedTransaction, transactionInfoWithDeliveryInfo);
105106
});
106107
}
107108

108-
private TransactionInfo retrieveTransaction(UUID itemId, UUID creatorId) {
109-
return Await.result(
110-
transactionService
111-
.getTransaction(itemId)
112-
.handleRequestHeader(authenticate(creatorId))
113-
.invoke()
114-
);
109+
@Test
110+
public void shouldSetDeliveryPrice() throws Throwable {
111+
itemProducerStub.send(auctionFinished);
112+
setDeliveryPrice(itemId, creatorId, deliveryPrice);
113+
114+
eventually(new FiniteDuration(15, SECONDS), () -> {
115+
TransactionInfo retrievedTransaction = retrieveTransaction(itemId, creatorId);
116+
assertEquals(retrievedTransaction, transactionInfoWithDeliveryPrice);
117+
});
115118
}
116119

117120
private Done submitDeliveryDetails(UUID itemId, UUID winnerId, DeliveryInfo deliveryInfo){
@@ -122,6 +125,23 @@ private Done submitDeliveryDetails(UUID itemId, UUID winnerId, DeliveryInfo deli
122125
);
123126
}
124127

128+
private Done setDeliveryPrice(UUID itemId, UUID creatorId, int deliveryPrice){
129+
return Await.result(
130+
transactionService.setDeliveryPrice(itemId)
131+
.handleRequestHeader(authenticate(creatorId))
132+
.invoke(deliveryPrice)
133+
);
134+
}
135+
136+
private TransactionInfo retrieveTransaction(UUID itemId, UUID creatorId) {
137+
return Await.result(
138+
transactionService
139+
.getTransaction(itemId)
140+
.handleRequestHeader(authenticate(creatorId))
141+
.invoke()
142+
);
143+
}
144+
125145
private static class ItemStub implements ItemService {
126146

127147
@Inject
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package controllers;
2+
3+
import lombok.Data;
4+
import play.data.validation.Constraints;
5+
6+
@Data
7+
public class DeliveryPriceForm {
8+
@Constraints.Required
9+
private int deliveryPrice;
10+
}

web-gateway/src/main/java/controllers/TransactionController.java

+65-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package controllers;
22

3-
import com.example.auction.item.api.ItemStatus;
43
import com.example.auction.pagination.PaginatedSequence;
54
import com.example.auction.transaction.api.*;
65
import com.example.auction.user.api.User;
76
import com.example.auction.user.api.UserService;
8-
import com.lightbend.lagom.javadsl.api.transport.TransportException;
97
import com.typesafe.config.Config;
108
import play.data.Form;
119
import play.data.FormFactory;
@@ -96,7 +94,7 @@ public CompletionStage<Result> getTransaction(String id) {
9694
Currency currency = Currency.valueOf(transaction.getItemData().getCurrencyId());
9795
return ok(views.html.transaction.render(showInlineInstruction, Optional.of(transaction), seller, winner, Optional.of(currency), Optional.empty(), nav));
9896
} else {
99-
String msg = ((TransportException) exception.getCause()).exceptionMessage().detail();
97+
String msg = exception.getCause().getMessage();
10098
return ok(views.html.transaction.render(showInlineInstruction, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(msg), nav));
10199
}
102100
});
@@ -132,7 +130,7 @@ public CompletionStage<Result> submitDeliveryDetailsForm(String id) {
132130
nav)
133131
);
134132
} else {
135-
String msg = ((TransportException) exception.getCause()).exceptionMessage().detail();
133+
String msg = exception.getCause().getMessage();
136134
return ok(views.html.deliveryDetails.render(showInlineInstruction, false, itemId, formFactory.form(DeliveryDetailsForm.class), TransactionInfoStatus.NEGOTIATING_DELIVERY, Optional.of(msg), nav));
137135
}
138136
});
@@ -162,7 +160,7 @@ public CompletionStage<Result> submitDeliveryDetails(String id, String transacti
162160
if (exception == null) {
163161
return CompletableFuture.completedFuture(redirect(routes.TransactionController.getTransaction(id)));
164162
} else {
165-
String msg = ((TransportException) exception.getCause()).exceptionMessage().detail();
163+
String msg = exception.getCause().getMessage();
166164
return loadNav(user).thenApplyAsync(nav ->
167165
ok(views.html.deliveryDetails.render(showInlineInstruction, isBuyer, itemId, form, status, Optional.of(msg), nav)),
168166
ec.current());
@@ -182,4 +180,66 @@ private DeliveryInfo fromForm(DeliveryDetailsForm deliveryForm) {
182180
deliveryForm.getCountry()
183181
);
184182
}
183+
184+
public CompletionStage<Result> setDeliveryPriceForm(String id) {
185+
return requireUser(ctx(), user ->
186+
loadNav(user).thenComposeAsync(nav -> {
187+
UUID itemId = UUID.fromString(id);
188+
CompletionStage<TransactionInfo> transactionFuture = transactionService.getTransaction(itemId).handleRequestHeader(authenticate(user)).invoke();
189+
return transactionFuture.handle((transaction, exception) -> {
190+
if (exception == null) {
191+
DeliveryPriceForm form = new DeliveryPriceForm();
192+
Optional<Integer> maybeDeliveryPrice = transaction.getDeliveryPrice();
193+
if (maybeDeliveryPrice.isPresent())
194+
form.setDeliveryPrice(maybeDeliveryPrice.get());
195+
return ok(
196+
views.html.deliveryPrice.render(
197+
showInlineInstruction,
198+
transaction.getCreator().equals(user),
199+
itemId,
200+
formFactory.form(DeliveryPriceForm.class).fill(form),
201+
transaction.getStatus(),
202+
Optional.empty(),
203+
nav)
204+
);
205+
} else {
206+
String msg = exception.getCause().getMessage();
207+
return ok(views.html.deliveryPrice.render(showInlineInstruction, false, itemId, formFactory.form(DeliveryPriceForm.class), TransactionInfoStatus.NEGOTIATING_DELIVERY, Optional.of(msg), nav));
208+
}
209+
});
210+
},
211+
ec.current())
212+
);
213+
}
214+
215+
public CompletionStage<Result> setDeliveryPrice(String id, String transactionStatus, boolean isSeller) {
216+
Http.Context ctx = ctx();
217+
return requireUser(ctx(), user -> {
218+
219+
Form<DeliveryPriceForm> form = formFactory.form(DeliveryPriceForm.class).bindFromRequest(ctx.request());
220+
UUID itemId = UUID.fromString(id);
221+
TransactionInfoStatus status = TransactionInfoStatus.valueOf(transactionStatus);
222+
223+
if (form.hasErrors()) {
224+
return loadNav(user).thenApplyAsync(nav ->
225+
ok(views.html.deliveryPrice.render(showInlineInstruction, isSeller, itemId, form, status, Optional.empty(), nav)),
226+
ec.current()
227+
);
228+
} else {
229+
return transactionService.setDeliveryPrice(itemId)
230+
.handleRequestHeader(authenticate(user))
231+
.invoke(form.get().getDeliveryPrice())
232+
.handle((done, exception) -> {
233+
if (exception == null) {
234+
return CompletableFuture.completedFuture(redirect(routes.TransactionController.getTransaction(id)));
235+
} else {
236+
String msg = exception.getCause().getMessage();
237+
return loadNav(user).thenApplyAsync(nav ->
238+
ok(views.html.deliveryPrice.render(showInlineInstruction, isSeller, itemId, form, status, Optional.of(msg), nav)),
239+
ec.current());
240+
}
241+
}).thenComposeAsync(x -> x, ec.current());
242+
}
243+
});
244+
}
185245
}

0 commit comments

Comments
 (0)