diff options
21 files changed, 359 insertions, 61 deletions
| @@ -2,12 +2,9 @@ | |||
| 2 | bin/ | 2 | bin/ |
| 3 | stage/ | 3 | stage/ |
| 4 | dist/ | 4 | dist/ |
| 5 | test-data/ | ||
| 6 | *.class | 5 | *.class |
| 7 | 6 | ||
| 8 | # Ignore OS-specific files | 7 | # Ignore OS-specific files |
| 9 | .DS_Store | 8 | .DS_Store |
| 10 | Thumbs.db | 9 | Thumbs.db |
| 11 | 10 | ||
| 12 | config.properties | ||
| 13 | |||
diff --git a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java index 16ee1b8..19c7297 100644 --- a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java +++ b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java | |||
| @@ -41,7 +41,7 @@ public final class LocalGuessMarketContext implements GuessMarketContext { | |||
| 41 | 41 | ||
| 42 | @Override | 42 | @Override |
| 43 | public List<EventSummaryDTO> listEvents() { | 43 | public List<EventSummaryDTO> listEvents() { |
| 44 | return eventRepository.getAllEvents().stream().map(Event::toEventSummary).toList(); | 44 | return eventRepository.getAll().stream().map(Event::toEventSummary).toList(); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | @Override | 47 | @Override |
| @@ -58,9 +58,14 @@ public final class LocalGuessMarketContext implements GuessMarketContext { | |||
| 58 | public PurchaseReceiptDTO buyShares( | 58 | public PurchaseReceiptDTO buyShares( |
| 59 | String userName, String eventKey, String optionKey, int quantity) | 59 | String userName, String eventKey, String optionKey, int quantity) |
| 60 | throws GuessMarketException { | 60 | throws GuessMarketException { |
| 61 | var event = eventRepository.getEvent(eventKey); | 61 | var optional = eventRepository.get(eventKey); |
| 62 | |||
| 63 | var userOptional = userRepository.get(userName); | ||
| 64 | if (optional.isEmpty() || userOptional.isEmpty()) throw new GuessMarketException(); | ||
| 65 | |||
| 66 | var event = optional.get(); | ||
| 67 | var user = userOptional.get(); | ||
| 62 | var option = event.getOption(optionKey); | 68 | var option = event.getOption(optionKey); |
| 63 | var user = userRepository.getUser(userName); | ||
| 64 | var trade = tradingOperation.buy(event, user, option, quantity); | 69 | var trade = tradingOperation.buy(event, user, option, quantity); |
| 65 | return new PurchaseReceiptDTO(); | 70 | return new PurchaseReceiptDTO(); |
| 66 | } | 71 | } |
diff --git a/service/src/main/java/market/guess/service/domain/Trade.java b/service/src/main/java/market/guess/service/domain/Trade.java index cb6ed93..0c07749 100644 --- a/service/src/main/java/market/guess/service/domain/Trade.java +++ b/service/src/main/java/market/guess/service/domain/Trade.java | |||
| @@ -4,12 +4,12 @@ import java.math.BigDecimal; | |||
| 4 | import java.time.Instant; | 4 | import java.time.Instant; |
| 5 | 5 | ||
| 6 | public record Trade( | 6 | public record Trade( |
| 7 | long seq, | 7 | int id, |
| 8 | Instant at, | 8 | Instant at, |
| 9 | String userName, | 9 | String userName, |
| 10 | String optionKey, | 10 | String optionKey, |
| 11 | String optionName, | 11 | String optionName, |
| 12 | long quantity, | 12 | int uantity, |
| 13 | BigDecimal sharesCost, | 13 | BigDecimal sharesCost, |
| 14 | BigDecimal commission) { | 14 | BigDecimal commission) { |
| 15 | 15 | ||
diff --git a/service/src/main/java/market/guess/service/domain/User.java b/service/src/main/java/market/guess/service/domain/User.java index af9ac78..b2084b4 100644 --- a/service/src/main/java/market/guess/service/domain/User.java +++ b/service/src/main/java/market/guess/service/domain/User.java | |||
| @@ -6,6 +6,10 @@ public final class User { | |||
| 6 | private final String name; | 6 | private final String name; |
| 7 | private final Account account; | 7 | private final Account account; |
| 8 | 8 | ||
| 9 | public User(String name, int initialBalance) { | ||
| 10 | this(name, BigDecimalOptions.toMoney(initialBalance)); | ||
| 11 | } | ||
| 12 | |||
| 9 | public User(String name, BigDecimal initialBalance) { | 13 | public User(String name, BigDecimal initialBalance) { |
| 10 | this.name = name; | 14 | this.name = name; |
| 11 | this.account = new Account(name, initialBalance); | 15 | this.account = new Account(name, initialBalance); |
diff --git a/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java b/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java index 0e8a0e0..bc5ddb0 100644 --- a/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java +++ b/service/src/main/java/market/guess/service/infrastructure/adapter/InstantTypeAdapter.java | |||
| @@ -4,7 +4,8 @@ import com.google.gson.*; | |||
| 4 | import java.lang.reflect.Type; | 4 | import java.lang.reflect.Type; |
| 5 | import java.time.Instant; | 5 | import java.time.Instant; |
| 6 | 6 | ||
| 7 | public class InstantTypeAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> { | 7 | public final class InstantTypeAdapter |
| 8 | implements JsonSerializer<Instant>, JsonDeserializer<Instant> { | ||
| 8 | @Override | 9 | @Override |
| 9 | public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { | 10 | public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { |
| 10 | return new JsonPrimitive(src.toString()); | 11 | return new JsonPrimitive(src.toString()); |
diff --git a/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java new file mode 100644 index 0000000..687ec33 --- /dev/null +++ b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | package market.guess.service.infrastructure.mapper.v2; | ||
| 2 | |||
| 3 | import java.util.ArrayList; | ||
| 4 | import java.util.List; | ||
| 5 | import market.guess.api.CommissionTiming; | ||
| 6 | import market.guess.api.EventStatus; | ||
| 7 | import market.guess.service.domain.Event; | ||
| 8 | import market.guess.service.domain.Option; | ||
| 9 | import market.guess.service.infrastructure.mapper.Mapper; | ||
| 10 | import market.guess.service.mechanism.LmsrTradingMechanism; | ||
| 11 | import market.guess.service.mechanism.OrderBookTradingMechanism; | ||
| 12 | import market.guess.service.mechanism.TradingMechanism; | ||
| 13 | import market.guess.service.model.v2.GMEvent; | ||
| 14 | import market.guess.service.model.v2.GMLMSR; | ||
| 15 | import market.guess.service.model.v2.GMOrderBook; | ||
| 16 | |||
| 17 | public final class EventMapperV2 implements Mapper<GMEvent, Event> { | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public Event toDomain(GMEvent source) { | ||
| 21 | return new Event( | ||
| 22 | getEventKey(source), | ||
| 23 | source.getId(), | ||
| 24 | source.getName(), | ||
| 25 | source.getDescription(), | ||
| 26 | source.getCommission().getValue(), | ||
| 27 | getTiming(source.getCommission().getType()), | ||
| 28 | getMechanism(source), | ||
| 29 | EventStatus.NOT_STARTED, | ||
| 30 | getOptions(source), | ||
| 31 | ""); | ||
| 32 | } | ||
| 33 | |||
| 34 | private static String getEventKey(GMEvent event) { | ||
| 35 | return String.valueOf(event.getId()); | ||
| 36 | } | ||
| 37 | |||
| 38 | private static CommissionTiming getTiming(String type) throws IllegalArgumentException { | ||
| 39 | return switch (type) { | ||
| 40 | case "on-close" -> CommissionTiming.ON_CLOSE; | ||
| 41 | case "on-purchase" -> CommissionTiming.ON_PURCHASE; | ||
| 42 | default -> throw new IllegalArgumentException("Unknown commission type: " + type); | ||
| 43 | }; | ||
| 44 | } | ||
| 45 | |||
| 46 | private static TradingMechanism getMechanism(GMEvent source) { | ||
| 47 | if (source.getGMMethod().getGMLMSR() instanceof GMLMSR lmsr) { | ||
| 48 | return new LmsrTradingMechanism(lmsr.getB()); | ||
| 49 | } | ||
| 50 | if (source.getGMMethod().getGMOrderBook() instanceof GMOrderBook orderBook) { | ||
| 51 | return new OrderBookTradingMechanism( | ||
| 52 | orderBook.getAllowMint().equalsIgnoreCase("true"), | ||
| 53 | orderBook.getInitial(), | ||
| 54 | orderBook.getD()); | ||
| 55 | } | ||
| 56 | throw new IllegalArgumentException(); | ||
| 57 | } | ||
| 58 | |||
| 59 | private static List<Option> getOptions(GMEvent x) { | ||
| 60 | var ret = new ArrayList<Option>(x.getGMOptions().getGMOption().size()); | ||
| 61 | for (var i = 0; i < x.getGMOptions().getGMOption().size(); i++) { | ||
| 62 | ret.add(new Option(getEventKey(x) + ":" + i, x.getGMOptions().getGMOption().get(i))); | ||
| 63 | } | ||
| 64 | return ret; | ||
| 65 | } | ||
| 66 | } | ||
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java b/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java new file mode 100644 index 0000000..c1c3895 --- /dev/null +++ b/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | package market.guess.service.infrastructure.provider; | ||
| 2 | |||
| 3 | import java.util.ArrayList; | ||
| 4 | import java.util.Collections; | ||
| 5 | import java.util.List; | ||
| 6 | import market.guess.api.ProblemDTO; | ||
| 7 | |||
| 8 | public final class LoadValidator { | ||
| 9 | private final List<ProblemDTO> problems = new ArrayList<>(); | ||
| 10 | |||
| 11 | public void append(String code, String humanizedMessage, String location) { | ||
| 12 | problems.add(new ProblemDTO(code, humanizedMessage, location)); | ||
| 13 | } | ||
| 14 | |||
| 15 | public boolean isValid() { | ||
| 16 | return problems.isEmpty(); | ||
| 17 | } | ||
| 18 | |||
| 19 | public List<ProblemDTO> getIssues() { | ||
| 20 | return Collections.unmodifiableList(problems); | ||
| 21 | } | ||
| 22 | } | ||
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoader.java b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java index 9333350..7af22ea 100644 --- a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoader.java +++ b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java | |||
| @@ -4,28 +4,24 @@ import jakarta.xml.bind.JAXBContext; | |||
| 4 | import jakarta.xml.bind.JAXBException; | 4 | import jakarta.xml.bind.JAXBException; |
| 5 | import java.nio.file.Files; | 5 | import java.nio.file.Files; |
| 6 | import java.nio.file.Path; | 6 | import java.nio.file.Path; |
| 7 | import java.util.List; | ||
| 8 | import market.guess.api.LoadResultDTO; | 7 | import market.guess.api.LoadResultDTO; |
| 9 | import market.guess.service.domain.BigDecimalOptions; | 8 | import market.guess.service.domain.User; |
| 10 | import market.guess.service.domain.Event; | 9 | import market.guess.service.infrastructure.mapper.v1.EventMapperV1; |
| 11 | import market.guess.service.infrastructure.mapper.Mapper; | 10 | import market.guess.service.infrastructure.provider.LoadValidator; |
| 12 | import market.guess.service.infrastructure.provider.Loader; | 11 | import market.guess.service.infrastructure.provider.Loader; |
| 13 | import market.guess.service.model.v1.GMEvent; | ||
| 14 | import market.guess.service.model.v1.GuessMarket; | 12 | import market.guess.service.model.v1.GuessMarket; |
| 15 | import market.guess.service.repository.EventRepository; | 13 | import market.guess.service.repository.EventRepository; |
| 16 | import market.guess.service.repository.UserRepository; | 14 | import market.guess.service.repository.UserRepository; |
| 17 | 15 | ||
| 18 | public final class XMLLoader implements Loader { | 16 | public final class XMLLoaderV1 implements Loader { |
| 19 | private final JAXBContext context; | 17 | private final JAXBContext context; |
| 20 | 18 | ||
| 21 | private final EventRepository eventRepository; | 19 | private final EventRepository eventRepository; |
| 22 | private final UserRepository userRepository; | 20 | private final UserRepository userRepository; |
| 23 | private final Mapper<GMEvent, Event> mapper; | 21 | private final EventMapperV1 mapper; |
| 24 | 22 | ||
| 25 | public XMLLoader( | 23 | public XMLLoaderV1( |
| 26 | EventRepository eventRepository, | 24 | EventRepository eventRepository, UserRepository userRepository, EventMapperV1 mapper) { |
| 27 | UserRepository userRepository, | ||
| 28 | Mapper<GMEvent, Event> mapper) { | ||
| 29 | super(); | 25 | super(); |
| 30 | this.eventRepository = eventRepository; | 26 | this.eventRepository = eventRepository; |
| 31 | this.userRepository = userRepository; | 27 | this.userRepository = userRepository; |
| @@ -39,22 +35,27 @@ public final class XMLLoader implements Loader { | |||
| 39 | 35 | ||
| 40 | @Override | 36 | @Override |
| 41 | public LoadResultDTO load(Path path) { | 37 | public LoadResultDTO load(Path path) { |
| 38 | var validator = new LoadValidator(); | ||
| 39 | if (path.getFileName() == null | ||
| 40 | || !path.getFileName().toString().toLowerCase().endsWith(".xml")) { | ||
| 41 | validator.append("NOT_XML", "The file must be an XML.", path.toString()); | ||
| 42 | } | ||
| 42 | 43 | ||
| 43 | try (var stream = Files.newInputStream(path)) { | 44 | try (var stream = Files.newInputStream(path)) { |
| 44 | 45 | ||
| 45 | var doc = (GuessMarket) context.createUnmarshaller().unmarshal(stream); | 46 | var doc = (GuessMarket) context.createUnmarshaller().unmarshal(stream); |
| 46 | 47 | ||
| 47 | for (var event : doc.getGMEvents().getGMEvent()) { | 48 | for (var event : doc.getGMEvents().getGMEvent()) { |
| 48 | eventRepository.addEvent(mapper.toDomain(event)); | 49 | eventRepository.add(mapper.toDomain(event)); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | userRepository.addUser("Tester", BigDecimalOptions.toMoney(9_999_999)); | 52 | userRepository.add(new User("Tester", 9_999_999)); |
| 52 | 53 | ||
| 53 | return LoadResultDTO.success("udk", 1); | 54 | return LoadResultDTO.success(path.toString(), eventRepository.getAll().size()); |
| 54 | } catch (Exception e) { | 55 | } catch (Exception e) { |
| 55 | 56 | validator.append("UNREADABLE", "The file could not be read.", path.toString()); | |
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | return LoadResultDTO.failure(path.toFile().getName(), List.of()); | 59 | return LoadResultDTO.failure(path.toString(), validator.getIssues()); |
| 59 | } | 60 | } |
| 60 | } | 61 | } |
diff --git a/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java new file mode 100644 index 0000000..62feb02 --- /dev/null +++ b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | package market.guess.service.mechanism; | ||
| 2 | |||
| 3 | import java.math.BigDecimal; | ||
| 4 | import market.guess.api.MechanismType; | ||
| 5 | |||
| 6 | public final class OrderBookTradingMechanism implements TradingMechanism { | ||
| 7 | private final boolean allowMint; | ||
| 8 | private final int initial; | ||
| 9 | private final int d; | ||
| 10 | |||
| 11 | public OrderBookTradingMechanism(boolean allowMint, int initial, int d) { | ||
| 12 | super(); | ||
| 13 | this.allowMint = allowMint; | ||
| 14 | this.initial = initial; | ||
| 15 | this.d = d; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | public MechanismType type() { | ||
| 20 | return MechanismType.ORDER_BOOK; | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public BigDecimal openingCost(int optionCount) { | ||
| 25 | // TODO Auto-generated method stub | ||
| 26 | throw new UnsupportedOperationException("Unimplemented method 'openingCost'"); | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public BigDecimal costOfBuying(long[] q, int optionIndex, long quantity) { | ||
| 31 | // TODO Auto-generated method stub | ||
| 32 | throw new UnsupportedOperationException("Unimplemented method 'costOfBuying'"); | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public double[] prices(long[] q) { | ||
| 37 | // TODO Auto-generated method stub | ||
| 38 | throw new UnsupportedOperationException("Unimplemented method 'prices'"); | ||
| 39 | } | ||
| 40 | } | ||
diff --git a/service/src/main/java/market/guess/service/repository/EventRepository.java b/service/src/main/java/market/guess/service/repository/EventRepository.java index 94999cb..afc5371 100644 --- a/service/src/main/java/market/guess/service/repository/EventRepository.java +++ b/service/src/main/java/market/guess/service/repository/EventRepository.java | |||
| @@ -1,12 +1,5 @@ | |||
| 1 | package market.guess.service.repository; | 1 | package market.guess.service.repository; |
| 2 | 2 | ||
| 3 | import java.util.List; | ||
| 4 | import market.guess.service.domain.Event; | 3 | import market.guess.service.domain.Event; |
| 5 | 4 | ||
| 6 | public interface EventRepository { | 5 | public interface EventRepository extends Repository<Event> {} |
| 7 | Event getEvent(String id); | ||
| 8 | |||
| 9 | List<Event> getAllEvents(); | ||
| 10 | |||
| 11 | void addEvent(Event event); | ||
| 12 | } | ||
diff --git a/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java b/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java index 81eba74..eb7763e 100644 --- a/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java +++ b/service/src/main/java/market/guess/service/repository/InMemoryEventRepository.java | |||
| @@ -1,25 +1,27 @@ | |||
| 1 | package market.guess.service.repository; | 1 | package market.guess.service.repository; |
| 2 | 2 | ||
| 3 | import java.util.ArrayList; | ||
| 4 | import java.util.Collections; | ||
| 3 | import java.util.List; | 5 | import java.util.List; |
| 6 | import java.util.Optional; | ||
| 4 | import market.guess.service.domain.Event; | 7 | import market.guess.service.domain.Event; |
| 5 | 8 | ||
| 6 | public final class InMemoryEventRepository implements EventRepository { | 9 | public final class InMemoryEventRepository implements EventRepository { |
| 10 | private final List<Event> events = new ArrayList<>(); | ||
| 7 | 11 | ||
| 8 | @Override | 12 | @Override |
| 9 | public Event getEvent(String id) { | 13 | public Event add(Event element) { |
| 10 | // TODO Auto-generated method stub | 14 | events.add(element); |
| 11 | throw new UnsupportedOperationException("Unimplemented method 'getEvent'"); | 15 | return element; |
| 12 | } | 16 | } |
| 13 | 17 | ||
| 14 | @Override | 18 | @Override |
| 15 | public List<Event> getAllEvents() { | 19 | public Optional<Event> get(String id) { |
| 16 | // TODO Auto-generated method stub | 20 | return events.stream().filter(event -> event.getEventKey().equalsIgnoreCase(id)).findFirst(); |
| 17 | throw new UnsupportedOperationException("Unimplemented method 'getAllEvents'"); | ||
| 18 | } | 21 | } |
| 19 | 22 | ||
| 20 | @Override | 23 | @Override |
| 21 | public void addEvent(Event event) { | 24 | public List<Event> getAll() { |
| 22 | // TODO Auto-generated method stub | 25 | return Collections.unmodifiableList(events); |
| 23 | throw new UnsupportedOperationException("Unimplemented method 'addEvent'"); | ||
| 24 | } | 26 | } |
| 25 | } | 27 | } |
diff --git a/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java b/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java index 951dea8..0b5cfe7 100644 --- a/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java +++ b/service/src/main/java/market/guess/service/repository/InMemoryUserRepository.java | |||
| @@ -1,19 +1,27 @@ | |||
| 1 | package market.guess.service.repository; | 1 | package market.guess.service.repository; |
| 2 | 2 | ||
| 3 | import java.math.BigDecimal; | 3 | import java.util.ArrayList; |
| 4 | import java.util.Collections; | ||
| 5 | import java.util.List; | ||
| 6 | import java.util.Optional; | ||
| 4 | import market.guess.service.domain.User; | 7 | import market.guess.service.domain.User; |
| 5 | 8 | ||
| 6 | public final class InMemoryUserRepository implements UserRepository { | 9 | public final class InMemoryUserRepository implements UserRepository { |
| 10 | private final List<User> users = new ArrayList<>(); | ||
| 7 | 11 | ||
| 8 | @Override | 12 | @Override |
| 9 | public User getUser(String name) { | 13 | public User add(User element) { |
| 10 | // TODO Auto-generated method stub | 14 | users.add(element); |
| 11 | throw new UnsupportedOperationException("Unimplemented method 'getUser'"); | 15 | return element; |
| 12 | } | 16 | } |
| 13 | 17 | ||
| 14 | @Override | 18 | @Override |
| 15 | public void addUser(String name, BigDecimal initialBalance) { | 19 | public Optional<User> get(String id) { |
| 16 | // TODO Auto-generated method stub | 20 | return users.stream().filter(u -> u.getName().equalsIgnoreCase(id)).findFirst(); |
| 17 | throw new UnsupportedOperationException("Unimplemented method 'addUser'"); | 21 | } |
| 22 | |||
| 23 | @Override | ||
| 24 | public List<User> getAll() { | ||
| 25 | return Collections.unmodifiableList(users); | ||
| 18 | } | 26 | } |
| 19 | } | 27 | } |
diff --git a/service/src/main/java/market/guess/service/repository/Repository.java b/service/src/main/java/market/guess/service/repository/Repository.java new file mode 100644 index 0000000..8ea1817 --- /dev/null +++ b/service/src/main/java/market/guess/service/repository/Repository.java | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | package market.guess.service.repository; | ||
| 2 | |||
| 3 | import java.util.List; | ||
| 4 | import java.util.Optional; | ||
| 5 | |||
| 6 | public interface Repository<T> { | ||
| 7 | T add(T element); | ||
| 8 | |||
| 9 | Optional<T> get(String id); | ||
| 10 | |||
| 11 | List<T> getAll(); | ||
| 12 | } | ||
diff --git a/service/src/main/java/market/guess/service/repository/UserRepository.java b/service/src/main/java/market/guess/service/repository/UserRepository.java index 78d620d..86cc9a6 100644 --- a/service/src/main/java/market/guess/service/repository/UserRepository.java +++ b/service/src/main/java/market/guess/service/repository/UserRepository.java | |||
| @@ -1,11 +1,5 @@ | |||
| 1 | package market.guess.service.repository; | 1 | package market.guess.service.repository; |
| 2 | 2 | ||
| 3 | import java.math.BigDecimal; | ||
| 4 | import market.guess.service.domain.User; | 3 | import market.guess.service.domain.User; |
| 5 | 4 | ||
| 6 | public interface UserRepository { | 5 | public interface UserRepository extends Repository<User> {} |
| 7 | |||
| 8 | User getUser(String name); | ||
| 9 | |||
| 10 | void addUser(String name, BigDecimal initialBalance); | ||
| 11 | } | ||
diff --git a/test-data/ex-1/multiple.xml b/test-data/ex-1/multiple.xml new file mode 100644 index 0000000..3f2432d --- /dev/null +++ b/test-data/ex-1/multiple.xml | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <Guess-Market xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="GM-EX1-schema.xsd"> | ||
| 3 | <GM-events> | ||
| 4 | <GM-event name="Mujtaba is Dead"> | ||
| 5 | <id>1</id> | ||
| 6 | <description>This event gambles if Mujtaba is a live or not. it will be determined if he will be shown in public until 31.8.26</description> | ||
| 7 | <comision type="on-purchase">5</comision> | ||
| 8 | <GM-options> | ||
| 9 | <GM-option>Hell Yea !</GM-option> | ||
| 10 | <GM-option>No way !</GM-option> | ||
| 11 | </GM-options> | ||
| 12 | <GM-method> | ||
| 13 | <GM-LMSR> | ||
| 14 | <b>100</b> | ||
| 15 | </GM-LMSR> | ||
| 16 | </GM-method> | ||
| 17 | </GM-event> | ||
| 18 | <GM-event name="World Cap Winner"> | ||
| 19 | <id>2</id> | ||
| 20 | <description>Who do you think will win the world cap ?</description> | ||
| 21 | <comision type="on-close">15</comision> | ||
| 22 | <GM-options> | ||
| 23 | <GM-option>Argentina</GM-option> | ||
| 24 | <GM-option>Spain</GM-option> | ||
| 25 | </GM-options> | ||
| 26 | <GM-method> | ||
| 27 | <GM-LMSR> | ||
| 28 | <b>50</b> | ||
| 29 | </GM-LMSR> | ||
| 30 | </GM-method> | ||
| 31 | </GM-event> | ||
| 32 | <GM-event name="Earth Quake on Dead Sea"> | ||
| 33 | <id>3</id> | ||
| 34 | <description>Will there be an earth quake on the dead sea until the December 31th 2026</description> | ||
| 35 | <comision type="on-purchase">50</comision> | ||
| 36 | <GM-options> | ||
| 37 | <GM-option>Yes</GM-option> | ||
| 38 | <GM-option>No</GM-option> | ||
| 39 | </GM-options> | ||
| 40 | <GM-method> | ||
| 41 | <GM-LMSR> | ||
| 42 | <b>400</b> | ||
| 43 | </GM-LMSR> | ||
| 44 | </GM-method> | ||
| 45 | </GM-event> | ||
| 46 | </GM-events> | ||
| 47 | </Guess-Market> | ||
diff --git a/ui-console/src/main/java/market/guess/ui/console/App.java b/ui-console/src/main/java/market/guess/ui/console/App.java index 2bff741..34638ac 100644 --- a/ui-console/src/main/java/market/guess/ui/console/App.java +++ b/ui-console/src/main/java/market/guess/ui/console/App.java | |||
| @@ -4,12 +4,15 @@ import market.guess.api.GuessMarketContext; | |||
| 4 | import market.guess.service.LocalGuessMarketContext; | 4 | import market.guess.service.LocalGuessMarketContext; |
| 5 | import market.guess.service.TradingOperation; | 5 | import market.guess.service.TradingOperation; |
| 6 | import market.guess.service.TradingOperationImpl; | 6 | import market.guess.service.TradingOperationImpl; |
| 7 | import market.guess.service.infrastructure.mapper.v1.EventMapperV1; | ||
| 7 | import market.guess.service.infrastructure.provider.Loader; | 8 | import market.guess.service.infrastructure.provider.Loader; |
| 8 | import market.guess.service.infrastructure.provider.v1.XMLLoader; | 9 | import market.guess.service.infrastructure.provider.v1.XMLLoaderV1; |
| 9 | import market.guess.service.repository.EventRepository; | 10 | import market.guess.service.repository.EventRepository; |
| 10 | import market.guess.service.repository.InMemoryEventRepository; | 11 | import market.guess.service.repository.InMemoryEventRepository; |
| 11 | import market.guess.service.repository.InMemoryUserRepository; | 12 | import market.guess.service.repository.InMemoryUserRepository; |
| 12 | import market.guess.service.repository.UserRepository; | 13 | import market.guess.service.repository.UserRepository; |
| 14 | import market.guess.ui.console.command.ExitMenuCommand; | ||
| 15 | import market.guess.ui.console.command.ListEventsMenuCommand; | ||
| 13 | import market.guess.ui.console.command.LoadFileMenuCommand; | 16 | import market.guess.ui.console.command.LoadFileMenuCommand; |
| 14 | import market.guess.ui.console.io.ConsoleInputProvider; | 17 | import market.guess.ui.console.io.ConsoleInputProvider; |
| 15 | import market.guess.ui.console.io.ConsoleOutputProvider; | 18 | import market.guess.ui.console.io.ConsoleOutputProvider; |
| @@ -27,10 +30,13 @@ public class App { | |||
| 27 | pico.addComponent(UserRepository.class, InMemoryUserRepository.class); | 30 | pico.addComponent(UserRepository.class, InMemoryUserRepository.class); |
| 28 | 31 | ||
| 29 | pico.addComponent(TradingOperation.class, TradingOperationImpl.class); | 32 | pico.addComponent(TradingOperation.class, TradingOperationImpl.class); |
| 30 | pico.addComponent(Loader.class, XMLLoader.class); | 33 | pico.addComponent(EventMapperV1.class); |
| 34 | pico.addComponent(Loader.class, XMLLoaderV1.class); | ||
| 31 | pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class); | 35 | pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class); |
| 32 | 36 | ||
| 33 | pico.addComponent(LoadFileMenuCommand.class); | 37 | pico.addComponent(LoadFileMenuCommand.class); |
| 38 | pico.addComponent(ListEventsMenuCommand.class); | ||
| 39 | pico.addComponent(ExitMenuCommand.class); | ||
| 34 | 40 | ||
| 35 | pico.addComponent(InputProvider.class, ConsoleInputProvider.class); | 41 | pico.addComponent(InputProvider.class, ConsoleInputProvider.class); |
| 36 | pico.addComponent(OutputProvider.class, ConsoleOutputProvider.class); | 42 | pico.addComponent(OutputProvider.class, ConsoleOutputProvider.class); |
diff --git a/ui-console/src/main/java/market/guess/ui/console/Menu.java b/ui-console/src/main/java/market/guess/ui/console/Menu.java index 0521e5f..c1762dc 100644 --- a/ui-console/src/main/java/market/guess/ui/console/Menu.java +++ b/ui-console/src/main/java/market/guess/ui/console/Menu.java | |||
| @@ -19,12 +19,12 @@ public final class Menu implements Startable { | |||
| 19 | 19 | ||
| 20 | @Override | 20 | @Override |
| 21 | public void start() { | 21 | public void start() { |
| 22 | var max = commands.stream().mapToInt(MenuCommand::getIndex).max().orElse(0); | ||
| 22 | out.splashScreen(); | 23 | out.splashScreen(); |
| 23 | |||
| 24 | while (true) { | 24 | while (true) { |
| 25 | printMenu(); | 25 | printMenu(); |
| 26 | 26 | ||
| 27 | var selection = in.readInt("Choose a command: ", 1, commands.size()); | 27 | var selection = in.readInt("Choose a command: ", 1, max); |
| 28 | var commandOptional = commands.stream().filter(c -> c.getIndex() == selection).findFirst(); | 28 | var commandOptional = commands.stream().filter(c -> c.getIndex() == selection).findFirst(); |
| 29 | 29 | ||
| 30 | if (commandOptional.isEmpty()) { | 30 | if (commandOptional.isEmpty()) { |
| @@ -34,6 +34,8 @@ public final class Menu implements Startable { | |||
| 34 | var command = commandOptional.get(); | 34 | var command = commandOptional.get(); |
| 35 | 35 | ||
| 36 | command.execute(); | 36 | command.execute(); |
| 37 | |||
| 38 | if (command.isExit()) return; | ||
| 37 | } | 39 | } |
| 38 | } | 40 | } |
| 39 | 41 | ||
| @@ -45,7 +47,7 @@ public final class Menu implements Startable { | |||
| 45 | private void printMenu() { | 47 | private void printMenu() { |
| 46 | out.newLine(); | 48 | out.newLine(); |
| 47 | for (var command : commands) { | 49 | for (var command : commands) { |
| 48 | out.println(" %d -- %s".formatted(command.getIndex(), command.getName())); | 50 | out.println(" [%d]->> %s".formatted(command.getIndex(), command.getName())); |
| 49 | } | 51 | } |
| 50 | } | 52 | } |
| 51 | } | 53 | } |
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java new file mode 100644 index 0000000..372c686 --- /dev/null +++ b/ui-console/src/main/java/market/guess/ui/console/command/ExitMenuCommand.java | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | package market.guess.ui.console.command; | ||
| 2 | |||
| 3 | import market.guess.ui.console.io.OutputProvider; | ||
| 4 | |||
| 5 | public final class ExitMenuCommand implements MenuCommand { | ||
| 6 | private final OutputProvider out; | ||
| 7 | |||
| 8 | public ExitMenuCommand(OutputProvider out) { | ||
| 9 | super(); | ||
| 10 | this.out = out; | ||
| 11 | } | ||
| 12 | |||
| 13 | @Override | ||
| 14 | public int getIndex() { | ||
| 15 | return 6; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | public String getName() { | ||
| 20 | return "Exit"; | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public boolean allowed() { | ||
| 25 | return true; | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public boolean isExit() { | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 34 | public void execute() { | ||
| 35 | out.println("Goodbye."); | ||
| 36 | } | ||
| 37 | } | ||
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java new file mode 100644 index 0000000..6e6ace9 --- /dev/null +++ b/ui-console/src/main/java/market/guess/ui/console/command/ListEventsMenuCommand.java | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | package market.guess.ui.console.command; | ||
| 2 | |||
| 3 | import market.guess.api.CommissionTiming; | ||
| 4 | import market.guess.api.GuessMarketContext; | ||
| 5 | import market.guess.ui.console.io.OutputProvider; | ||
| 6 | |||
| 7 | public final class ListEventsMenuCommand implements MenuCommand { | ||
| 8 | private final GuessMarketContext context; | ||
| 9 | private final OutputProvider out; | ||
| 10 | |||
| 11 | public ListEventsMenuCommand(GuessMarketContext context, OutputProvider out) { | ||
| 12 | super(); | ||
| 13 | this.context = context; | ||
| 14 | this.out = out; | ||
| 15 | } | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public int getIndex() { | ||
| 19 | return 2; | ||
| 20 | } | ||
| 21 | |||
| 22 | @Override | ||
| 23 | public String getName() { | ||
| 24 | return "List events"; | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public boolean allowed() { | ||
| 29 | return !context.listEvents().isEmpty(); | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public void execute() { | ||
| 34 | var events = context.listEvents(); | ||
| 35 | |||
| 36 | for (var event : events) { | ||
| 37 | out.newLine(); | ||
| 38 | out.println(" %s".formatted(event.name())); | ||
| 39 | out.println(" %s".formatted(event.description())); | ||
| 40 | out.println( | ||
| 41 | " Commission: %d%% (%s)" | ||
| 42 | .formatted(event.commissionPercent(), timing(event.commissionTiming()))); | ||
| 43 | out.println(" Options: " + String.join(" | ", event.optionNames())); | ||
| 44 | out.println(" Status: " + event.status()); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | private static String timing(CommissionTiming timing) { | ||
| 49 | return timing == CommissionTiming.ON_CLOSE ? "charged on close" : "charged on purchase"; | ||
| 50 | } | ||
| 51 | } | ||
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java index 5a91fe0..4a56483 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java | |||
| @@ -40,5 +40,11 @@ public final class LoadFileMenuCommand implements MenuCommand { | |||
| 40 | out.println("Loaded %d event(s) from %s.".formatted(result.eventsLoaded(), result.source())); | 40 | out.println("Loaded %d event(s) from %s.".formatted(result.eventsLoaded(), result.source())); |
| 41 | return; | 41 | return; |
| 42 | } | 42 | } |
| 43 | |||
| 44 | out.println( | ||
| 45 | "The file was not loaded. %d problem(s) were found: ".formatted(result.issues().size())); | ||
| 46 | for (var issue : result.issues()) { | ||
| 47 | out.println(" - %s: %s".formatted(issue.location(), issue.humanizedMessage())); | ||
| 48 | } | ||
| 43 | } | 49 | } |
| 44 | } | 50 | } |
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java index 8a28dfe..50acdac 100644 --- a/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java +++ b/ui-console/src/main/java/market/guess/ui/console/command/MenuCommand.java | |||
| @@ -8,4 +8,8 @@ public interface MenuCommand { | |||
| 8 | boolean allowed(); | 8 | boolean allowed(); |
| 9 | 9 | ||
| 10 | void execute(); | 10 | void execute(); |
| 11 | |||
| 12 | default boolean isExit() { | ||
| 13 | return false; | ||
| 14 | } | ||
| 11 | } | 15 | } |