From e16bd73565901bc0f907fe162fd7e33b7333a3d7 Mon Sep 17 00:00:00 2001 From: Priyank Srivastava Date: Wed, 6 Nov 2019 03:22:34 +0530 Subject: [PATCH] BAEL 3320 JCommander (#7971) * init jcommander * add model layer jcommander app * service scaffolding * init jcommander cli layer * wire up services and commands * splitter impl; validator impl; tests and cleanup * cleanup pom * integration tests * fix uuid validator example * optimise uuid regex; if-else to switch * review comments * fix builder formatting * change list assertion in fetch charges tests * missing minor edit * move to new module libraries-3 * rm unwanted files --- libraries-3/README.md | 9 ++ libraries-3/pom.xml | 51 ++++++++++ .../jcommander/helloworld/HelloWorldApp.java | 37 +++++++ .../usagebilling/UsageBasedBillingApp.java | 23 +++++ .../cli/FetchCurrentChargesCommand.java | 67 +++++++++++++ .../usagebilling/cli/SubmitUsageCommand.java | 96 +++++++++++++++++++ .../usagebilling/cli/UsageBasedBilling.java | 80 ++++++++++++++++ .../converter/ISO8601TimestampConverter.java | 33 +++++++ .../cli/splitter/ColonParameterSplitter.java | 15 +++ .../cli/validator/UUIDValidator.java | 26 +++++ .../model/CurrentChargesRequest.java | 16 ++++ .../model/CurrentChargesResponse.java | 56 +++++++++++ .../usagebilling/model/UsageRequest.java | 50 ++++++++++ .../DefaultFetchCurrentChargesService.java | 68 +++++++++++++ .../service/DefaultSubmitUsageService.java | 16 ++++ .../service/FetchCurrentChargesService.java | 13 +++ .../service/SubmitUsageService.java | 12 +++ .../helloworld/HelloWorldAppUnitTest.java | 28 ++++++ .../FetchCurrentChargesCommandUnitTest.java | 61 ++++++++++++ .../cli/SubmitUsageCommandUnitTest.java | 64 +++++++++++++ pom.xml | 2 + 21 files changed, 823 insertions(+) create mode 100644 libraries-3/README.md create mode 100644 libraries-3/pom.xml create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/helloworld/HelloWorldApp.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/UsageBasedBillingApp.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommand.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommand.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/UsageBasedBilling.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/converter/ISO8601TimestampConverter.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/splitter/ColonParameterSplitter.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/validator/UUIDValidator.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesRequest.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesResponse.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/UsageRequest.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultFetchCurrentChargesService.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultSubmitUsageService.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/FetchCurrentChargesService.java create mode 100644 libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/SubmitUsageService.java create mode 100644 libraries-3/src/test/java/com/baeldung/jcommander/helloworld/HelloWorldAppUnitTest.java create mode 100644 libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommandUnitTest.java create mode 100644 libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommandUnitTest.java diff --git a/libraries-3/README.md b/libraries-3/README.md new file mode 100644 index 0000000000..a6c6b190ab --- /dev/null +++ b/libraries-3/README.md @@ -0,0 +1,9 @@ +## Libraries-3 + +This module contains articles about various Java libraries. +These are small libraries that are relatively easy to use and do not require any separate module of their own. + +The code examples related to different libraries are each in their own module. + +Remember, for advanced libraries like [Jackson](/jackson) and [JUnit](/testing-modules) we already have separate modules. Please make sure to have a look at the existing modules in such cases. + diff --git a/libraries-3/pom.xml b/libraries-3/pom.xml new file mode 100644 index 0000000000..214e87287d --- /dev/null +++ b/libraries-3/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + libraries-3 + libraries-3 + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + jboss-public-repository-group + JBoss Public Repository Group + http://repository.jboss.org/nexus/content/groups/public/ + + true + never + + + true + daily + + + + + + + com.beust + jcommander + ${jcommander.version} + + + org.projectlombok + lombok + ${lombok.version} + + + + + 1.78 + 1.18.6 + UTF-8 + 1.8 + 1.8 + + diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/helloworld/HelloWorldApp.java b/libraries-3/src/main/java/com/baeldung/jcommander/helloworld/HelloWorldApp.java new file mode 100644 index 0000000000..56a74ecf21 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/helloworld/HelloWorldApp.java @@ -0,0 +1,37 @@ +package com.baeldung.jcommander.helloworld; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +public class HelloWorldApp { + + /* + * Execute: + * mvn exec:java -Dexec.mainClass=com.baeldung.jcommander.helloworld.HelloWorldApp -q \ + * -Dexec.args="--name JavaWorld" + */ + public static void main(String[] args) { + HelloWorldArgs jArgs = new HelloWorldArgs(); + JCommander helloCmd = JCommander + .newBuilder() + .addObject(jArgs) + .build(); + + helloCmd.parse(args); + System.out.println("Hello " + jArgs.getName()); + } +} + +class HelloWorldArgs { + + @Parameter( + names = "--name", + description = "User name", + required = true + ) + private String name; + + public String getName() { + return name; + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/UsageBasedBillingApp.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/UsageBasedBillingApp.java new file mode 100644 index 0000000000..029e7eed01 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/UsageBasedBillingApp.java @@ -0,0 +1,23 @@ +package com.baeldung.jcommander.usagebilling; + +import com.baeldung.jcommander.usagebilling.cli.UsageBasedBilling; + +public class UsageBasedBillingApp { + + /* + * Entry-point: invokes the cli passing the command-line args + * + * Invoking "Submit" sub-command: + * mvn exec:java \ + -Dexec.mainClass=com.baeldung.jcommander.usagebilling.UsageBasedBillingApp -q \ + -Dexec.args="submit --customer cb898e7a-f2a0-46d2-9a09-531f1cee1839 --subscription subscriptionPQRMN001 --pricing-type PRE_RATED --timestamp 2019-10-03T10:58:00 --quantity 7 --price 24.56" + * + * Invoking "Fetch" sub-command: + * mvn exec:java \ + -Dexec.mainClass=com.baeldung.jcommander.usagebilling.UsageBasedBillingApp -q \ + -Dexec.args="fetch --customer cb898e7a-f2a0-46d2-9a09-531f1cee1839 --subscription subscriptionPQRMN001 subscriptionPQRMN002 subscriptionPQRMN003 --itemized" + */ + public static void main(String[] args) { + new UsageBasedBilling().run(args); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommand.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommand.java new file mode 100644 index 0000000000..ea6b18af53 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommand.java @@ -0,0 +1,67 @@ +package com.baeldung.jcommander.usagebilling.cli; + +import com.baeldung.jcommander.usagebilling.cli.splitter.ColonParameterSplitter; +import com.baeldung.jcommander.usagebilling.cli.validator.UUIDValidator; +import com.baeldung.jcommander.usagebilling.model.CurrentChargesRequest; +import com.baeldung.jcommander.usagebilling.model.CurrentChargesResponse; +import com.baeldung.jcommander.usagebilling.service.FetchCurrentChargesService; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import lombok.Getter; + +import java.util.List; + +import static com.baeldung.jcommander.usagebilling.cli.UsageBasedBilling.*; +import static com.baeldung.jcommander.usagebilling.service.FetchCurrentChargesService.getDefault; + +@Parameters( + commandNames = { FETCH_CMD }, + commandDescription = "Fetch charges for a customer in the current month, can be itemized or aggregated" +) +@Getter +class FetchCurrentChargesCommand { + + FetchCurrentChargesCommand() { + } + + private FetchCurrentChargesService service = getDefault(); + + @Parameter(names = "--help", help = true) + private boolean help; + + @Parameter( + names = { "--customer", "-C" }, + description = "Id of the Customer who's using the services", + validateWith = UUIDValidator.class, + order = 1, + required = true + ) + private String customerId; + + @Parameter( + names = { "--subscription", "-S" }, + description = "Filter charges for specific subscription Ids, includes all subscriptions if no value is specified", + variableArity = true, + splitter = ColonParameterSplitter.class, + order = 2 + ) + private List subscriptionIds; + + @Parameter( + names = { "--itemized" }, + description = "Whether the response should contain breakdown by subscription, only aggregate values are returned by default", + order = 3 + ) + private boolean itemized; + + void fetch() { + CurrentChargesRequest req = CurrentChargesRequest.builder() + .customerId(customerId) + .subscriptionIds(subscriptionIds) + .itemized(itemized) + .build(); + + CurrentChargesResponse response = service.fetch(req); + System.out.println(response); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommand.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommand.java new file mode 100644 index 0000000000..d3516b19d3 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommand.java @@ -0,0 +1,96 @@ +package com.baeldung.jcommander.usagebilling.cli; + +import com.baeldung.jcommander.usagebilling.cli.converter.ISO8601TimestampConverter; +import com.baeldung.jcommander.usagebilling.cli.validator.UUIDValidator; +import com.baeldung.jcommander.usagebilling.model.UsageRequest; +import com.baeldung.jcommander.usagebilling.model.UsageRequest.PricingType; +import com.baeldung.jcommander.usagebilling.service.SubmitUsageService; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import lombok.Getter; + +import java.math.BigDecimal; +import java.time.Instant; + +import static com.baeldung.jcommander.usagebilling.cli.UsageBasedBilling.*; +import static com.baeldung.jcommander.usagebilling.service.SubmitUsageService.getDefault; + +@Parameters( + commandNames = { SUBMIT_CMD }, + commandDescription = "Submit usage for a given customer and subscription, accepts one usage item" +) +@Getter +class SubmitUsageCommand { + + SubmitUsageCommand() { + } + + private SubmitUsageService service = getDefault(); + + @Parameter(names = "--help", help = true) + private boolean help; + + @Parameter( + names = { "--customer", "-C" }, + description = "Id of the Customer who's using the services", + validateWith = UUIDValidator.class, + order = 1, + required = true + ) + private String customerId; + + @Parameter( + names = { "--subscription", "-S" }, + description = "Id of the Subscription that was purchased", + order = 2, + required = true + ) + private String subscriptionId; + + @Parameter( + names = { "--pricing-type", "-P" }, + description = "Pricing type of the usage reported", + order = 3, + required = true + ) + private PricingType pricingType; + + @Parameter( + names = { "--quantity" }, + description = "Used quantity; reported quantity is added over the billing period", + order = 3, + required = true + ) + private Integer quantity; + + @Parameter( + names = { "--timestamp" }, + description = "Timestamp of the usage event, must lie in the current billing period", + converter = ISO8601TimestampConverter.class, + order = 4, + required = true + ) + private Instant timestamp; + + @Parameter( + names = { "--price" }, + description = "If PRE_RATED, unit price to be applied per unit of usage quantity reported", + order = 5 + ) + private BigDecimal price; + + void submit() { + + UsageRequest req = UsageRequest.builder() + .customerId(customerId) + .subscriptionId(subscriptionId) + .pricingType(pricingType) + .quantity(quantity) + .timestamp(timestamp) + .price(price) + .build(); + + String reqId = service.submit(req); + System.out.println("Generated Request Id for reference: " + reqId); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/UsageBasedBilling.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/UsageBasedBilling.java new file mode 100644 index 0000000000..a531a68644 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/UsageBasedBilling.java @@ -0,0 +1,80 @@ +package com.baeldung.jcommander.usagebilling.cli; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.UnixStyleUsageFormatter; + +public class UsageBasedBilling { + + static final String SUBMIT_CMD = "submit"; + static final String FETCH_CMD = "fetch"; + + private JCommander jCommander; + private SubmitUsageCommand submitUsageCmd; + private FetchCurrentChargesCommand fetchChargesCmd; + + public UsageBasedBilling() { + this.submitUsageCmd = new SubmitUsageCommand(); + this.fetchChargesCmd = new FetchCurrentChargesCommand(); + jCommander = JCommander.newBuilder() + .addObject(this) + .addCommand(submitUsageCmd) + .addCommand(fetchChargesCmd) + .build(); + + setUsageFormatter(SUBMIT_CMD); + setUsageFormatter(FETCH_CMD); + } + + public void run(String[] args) { + String parsedCmdStr; + try { + jCommander.parse(args); + parsedCmdStr = jCommander.getParsedCommand(); + + switch (parsedCmdStr) { + case SUBMIT_CMD: + if (submitUsageCmd.isHelp()) { + getSubCommandHandle(SUBMIT_CMD).usage(); + } + System.out.println("Parsing usage request..."); + submitUsageCmd.submit(); + break; + + case FETCH_CMD: + if (fetchChargesCmd.isHelp()) { + getSubCommandHandle(SUBMIT_CMD).usage(); + } + System.out.println("Preparing fetch query..."); + fetchChargesCmd.fetch(); + + break; + + default: + System.err.println("Invalid command: " + parsedCmdStr); + } + } catch (ParameterException e) { + System.err.println(e.getLocalizedMessage()); + parsedCmdStr = jCommander.getParsedCommand(); + if (parsedCmdStr != null) { + getSubCommandHandle(parsedCmdStr).usage(); + } else { + jCommander.usage(); + } + } + } + + private JCommander getSubCommandHandle(String command) { + JCommander cmd = jCommander.getCommands().get(command); + + if (cmd == null) { + System.err.println("Invalid command: " + command); + } + return cmd; + } + + private void setUsageFormatter(String subCommand) { + JCommander cmd = getSubCommandHandle(subCommand); + cmd.setUsageFormatter(new UnixStyleUsageFormatter(cmd)); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/converter/ISO8601TimestampConverter.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/converter/ISO8601TimestampConverter.java new file mode 100644 index 0000000000..e54865a811 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/converter/ISO8601TimestampConverter.java @@ -0,0 +1,33 @@ +package com.baeldung.jcommander.usagebilling.cli.converter; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import static java.lang.String.format; + +public class ISO8601TimestampConverter extends BaseConverter { + + private static final DateTimeFormatter TS_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss"); + + public ISO8601TimestampConverter(String optionName) { + super(optionName); + } + + @Override + public Instant convert(String value) { + try { + return LocalDateTime + .parse(value, TS_FORMATTER) + .atOffset(ZoneOffset.UTC) + .toInstant(); + } catch (DateTimeParseException e) { + throw new ParameterException(getErrorString(value, format("an ISO-8601 formatted timestamp (%s)", TS_FORMATTER.toString()))); + } + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/splitter/ColonParameterSplitter.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/splitter/ColonParameterSplitter.java new file mode 100644 index 0000000000..f24c028123 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/splitter/ColonParameterSplitter.java @@ -0,0 +1,15 @@ +package com.baeldung.jcommander.usagebilling.cli.splitter; + +import com.beust.jcommander.converters.IParameterSplitter; + +import java.util.List; + +import static java.util.Arrays.asList; + +public class ColonParameterSplitter implements IParameterSplitter { + + @Override + public List split(String value) { + return asList(value.split(":")); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/validator/UUIDValidator.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/validator/UUIDValidator.java new file mode 100644 index 0000000000..a72912f7d0 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/cli/validator/UUIDValidator.java @@ -0,0 +1,26 @@ +package com.baeldung.jcommander.usagebilling.cli.validator; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.ParameterException; + +import java.util.regex.Pattern; + +public class UUIDValidator implements IParameterValidator { + + private static final String UUID_REGEX = + "[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"; + + @Override + public void validate(String name, String value) throws ParameterException { + if (!isValidUUID(value)) { + throw new ParameterException( + "String parameter " + value + " is not a valid UUID."); + } + } + + private boolean isValidUUID(String value) { + return Pattern + .compile(UUID_REGEX) + .matcher(value).matches(); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesRequest.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesRequest.java new file mode 100644 index 0000000000..93dd7a5732 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesRequest.java @@ -0,0 +1,16 @@ +package com.baeldung.jcommander.usagebilling.model; + +import lombok.*; + +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@Builder +@Getter +public class CurrentChargesRequest { + + private String customerId; + private List subscriptionIds; + private boolean itemized; +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesResponse.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesResponse.java new file mode 100644 index 0000000000..865a6e4a3d --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/CurrentChargesResponse.java @@ -0,0 +1,56 @@ +package com.baeldung.jcommander.usagebilling.model; + +import lombok.*; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@Builder +@Getter +public class CurrentChargesResponse { + + private String customerId; + private BigDecimal amountDue; + private List lineItems; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb + .append("Current Month Charges: {") + .append("\n\tcustomer: ") + .append(this.customerId) + .append("\n\ttotalAmountDue: ") + .append(this.amountDue.setScale(2, RoundingMode.HALF_UP)) + .append("\n\tlineItems: ["); + + for (LineItem li : this.lineItems) { + sb + .append("\n\t\t{") + .append("\n\t\t\tsubscription: ") + .append(li.subscriptionId) + .append("\n\t\t\tamount: ") + .append(li.amount.setScale(2, RoundingMode.HALF_UP)) + .append("\n\t\t\tquantity: ") + .append(li.quantity) + .append("\n\t\t},"); + } + + sb.append("\n\t]\n}\n"); + return sb.toString(); + } + + @NoArgsConstructor(access = AccessLevel.PACKAGE) + @AllArgsConstructor(access = AccessLevel.PACKAGE) + @Builder + @Getter + public static class LineItem { + + private String subscriptionId; + private BigDecimal amount; + private Integer quantity; + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/UsageRequest.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/UsageRequest.java new file mode 100644 index 0000000000..2785474acf --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/model/UsageRequest.java @@ -0,0 +1,50 @@ +package com.baeldung.jcommander.usagebilling.model; + +import lombok.*; + +import java.math.BigDecimal; +import java.time.Instant; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@Builder +@Getter +public class UsageRequest { + + private String customerId; + private String subscriptionId; + private PricingType pricingType; + private Integer quantity; + private BigDecimal price; + private Instant timestamp; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb + .append("\nUsage: {") + .append("\n\tcustomer: ") + .append(this.customerId) + .append("\n\tsubscription: ") + .append(this.subscriptionId) + .append("\n\tquantity: ") + .append(this.quantity) + .append("\n\ttimestamp: ") + .append(this.timestamp) + .append("\n\tpricingType: ") + .append(this.pricingType); + + if (PricingType.PRE_RATED == this.pricingType) { + sb + .append("\n\tpreRatedAt: ") + .append(this.price); + } + + sb.append("\n}\n"); + return sb.toString(); + } + + public enum PricingType { + PRE_RATED, UNRATED + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultFetchCurrentChargesService.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultFetchCurrentChargesService.java new file mode 100644 index 0000000000..6436d49875 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultFetchCurrentChargesService.java @@ -0,0 +1,68 @@ +package com.baeldung.jcommander.usagebilling.service; + +import com.baeldung.jcommander.usagebilling.model.CurrentChargesRequest; +import com.baeldung.jcommander.usagebilling.model.CurrentChargesResponse; +import com.baeldung.jcommander.usagebilling.model.CurrentChargesResponse.LineItem; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; + +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Arrays.fill; +import static java.util.Collections.emptyList; +import static java.util.concurrent.ThreadLocalRandom.current; +import static java.util.stream.Collectors.toList; + +class DefaultFetchCurrentChargesService implements FetchCurrentChargesService { + + @Override + public CurrentChargesResponse fetch(CurrentChargesRequest request) { + List subscriptions = request.getSubscriptionIds(); + + if (subscriptions == null || subscriptions.isEmpty()) { + System.out.println("Fetching ALL charges for customer: " + request.getCustomerId()); + subscriptions = mockSubscriptions(); + + } else { + System.out.println(format("Fetching charges for customer: %s and subscriptions: %s", request.getCustomerId(), subscriptions)); + } + + CurrentChargesResponse charges = mockCharges(request.getCustomerId(), subscriptions, request.isItemized()); + System.out.println("Fetched charges..."); + return charges; + } + + private CurrentChargesResponse mockCharges(String customerId, List subscriptions, boolean itemized) { + List lineItems = mockLineItems(subscriptions); + BigDecimal amountDue = lineItems + .stream() + .map(li -> li.getAmount()) + .reduce(new BigDecimal("0"), BigDecimal::add); + + return CurrentChargesResponse + .builder() + .customerId(customerId) + .lineItems(itemized ? lineItems : emptyList()) + .amountDue(amountDue) + .build(); + } + + private List mockLineItems(List subscriptions) { + return subscriptions + .stream() + .map(subscription -> LineItem.builder() + .subscriptionId(subscription) + .quantity(current().nextInt(20)) + .amount(new BigDecimal(current().nextDouble(1_000))) + .build()) + .collect(toList()); + } + + private List mockSubscriptions() { + String[] subscriptions = new String[5]; + fill(subscriptions, UUID.randomUUID().toString()); + return asList(subscriptions); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultSubmitUsageService.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultSubmitUsageService.java new file mode 100644 index 0000000000..44ac9e9ed7 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/DefaultSubmitUsageService.java @@ -0,0 +1,16 @@ +package com.baeldung.jcommander.usagebilling.service; + +import com.baeldung.jcommander.usagebilling.model.UsageRequest; + +import java.util.UUID; + +class DefaultSubmitUsageService implements SubmitUsageService { + + @Override + public String submit(UsageRequest request) { + System.out.println("Submitting usage..." + request); + + System.out.println("Submitted usage successfully..."); + return UUID.randomUUID().toString(); + } +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/FetchCurrentChargesService.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/FetchCurrentChargesService.java new file mode 100644 index 0000000000..2cc56658ff --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/FetchCurrentChargesService.java @@ -0,0 +1,13 @@ +package com.baeldung.jcommander.usagebilling.service; + +import com.baeldung.jcommander.usagebilling.model.CurrentChargesRequest; +import com.baeldung.jcommander.usagebilling.model.CurrentChargesResponse; + +public interface FetchCurrentChargesService { + + static FetchCurrentChargesService getDefault() { + return new DefaultFetchCurrentChargesService(); + } + + CurrentChargesResponse fetch(CurrentChargesRequest request); +} diff --git a/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/SubmitUsageService.java b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/SubmitUsageService.java new file mode 100644 index 0000000000..2a29e6e474 --- /dev/null +++ b/libraries-3/src/main/java/com/baeldung/jcommander/usagebilling/service/SubmitUsageService.java @@ -0,0 +1,12 @@ +package com.baeldung.jcommander.usagebilling.service; + +import com.baeldung.jcommander.usagebilling.model.UsageRequest; + +public interface SubmitUsageService { + + static SubmitUsageService getDefault() { + return new DefaultSubmitUsageService(); + } + + String submit(UsageRequest request); +} diff --git a/libraries-3/src/test/java/com/baeldung/jcommander/helloworld/HelloWorldAppUnitTest.java b/libraries-3/src/test/java/com/baeldung/jcommander/helloworld/HelloWorldAppUnitTest.java new file mode 100644 index 0000000000..48b3ac2896 --- /dev/null +++ b/libraries-3/src/test/java/com/baeldung/jcommander/helloworld/HelloWorldAppUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.jcommander.helloworld; + +import com.beust.jcommander.JCommander; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class HelloWorldAppUnitTest { + + @Test + public void whenJCommanderInvokedWithArgs_thenArgsParsed() { + + HelloWorldArgs jArgs = new HelloWorldArgs(); + JCommander helloCmd = JCommander + .newBuilder() + .addObject(jArgs) + .build(); + + // when + String[] argv = new String[] { + "--name", "JavaWorld" + }; + helloCmd.parse(argv); + + // then + assertEquals("JavaWorld", jArgs.getName()); + } +} diff --git a/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommandUnitTest.java b/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommandUnitTest.java new file mode 100644 index 0000000000..b639661c39 --- /dev/null +++ b/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/FetchCurrentChargesCommandUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.jcommander.usagebilling.cli; + +import com.beust.jcommander.JCommander; +import org.junit.Test; + +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; +import static org.junit.Assert.assertThat; + +public class FetchCurrentChargesCommandUnitTest { + + private JCommander jc = JCommander.newBuilder() + .addObject(new FetchCurrentChargesCommand()) + .build(); + + @Test + public void whenParsedMultipleSubscriptionsParameter_thenParameterSubscriptionsIsPopulated() { + FetchCurrentChargesCommand cmd = (FetchCurrentChargesCommand) jc + .getObjects() + .get(0); + + jc.parse(new String[] { + "-C", "cb898e7a-f2a0-46d2-9a09-531f1cee1839", + "-S", "subscriptionA001", + "-S", "subscriptionA002", + "-S", "subscriptionA003", + }); + + assertThat(cmd.getSubscriptionIds(), + contains("subscriptionA001", "subscriptionA002", "subscriptionA003")); + } + + @Test + public void whenParsedSubscriptionsColonSeparatedParameter_thenParameterSubscriptionsIsPopulated() { + FetchCurrentChargesCommand cmd = (FetchCurrentChargesCommand) jc + .getObjects() + .get(0); + + jc.parse(new String[] { + "-C", "cb898e7a-f2a0-46d2-9a09-531f1cee1839", + "-S", "subscriptionA001:subscriptionA002:subscriptionA003", + }); + + assertThat(cmd.getSubscriptionIds(), + contains("subscriptionA001", "subscriptionA002", "subscriptionA003")); + } + + @Test + public void whenParsedSubscriptionsWithVariableArity_thenParameterSubscriptionsIsPopulated() { + FetchCurrentChargesCommand cmd = (FetchCurrentChargesCommand) jc + .getObjects() + .get(0); + + jc.parse(new String[] { + "-C", "cb898e7a-f2a0-46d2-9a09-531f1cee1839", + "-S", "subscriptionA001", "subscriptionA002", "subscriptionA003", + }); + + assertThat(cmd.getSubscriptionIds(), + contains("subscriptionA001", "subscriptionA002", "subscriptionA003")); + } +} diff --git a/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommandUnitTest.java b/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommandUnitTest.java new file mode 100644 index 0000000000..d6ce132053 --- /dev/null +++ b/libraries-3/src/test/java/com/baeldung/jcommander/usagebilling/cli/SubmitUsageCommandUnitTest.java @@ -0,0 +1,64 @@ +package com.baeldung.jcommander.usagebilling.cli; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SubmitUsageCommandUnitTest { + + private JCommander jc = JCommander.newBuilder() + .addObject(new SubmitUsageCommand()) + .build(); + + @Test + public void whenParsedCustomerParameter_thenParameterOfTypeStringIsPopulated() { + jc.parse(new String[] { + "--customer", "cb898e7a-f2a0-46d2-9a09-531f1cee1839", + "--subscription", "subscriptionPQRMN001", + "--pricing-type", "PRE_RATED", + "--timestamp", "2019-10-03T10:58:00", + "--quantity", "7", + "--price", "24.56" + }); + + SubmitUsageCommand cmd = (SubmitUsageCommand) jc + .getObjects() + .get(0); + assertEquals("cb898e7a-f2a0-46d2-9a09-531f1cee1839", cmd.getCustomerId()); + } + + @Test + public void whenParsedTimestampParameter_thenParameterOfTypeInstantIsPopulated() { + jc.parse(new String[] { + "--customer", "cb898e7a-f2a0-46d2-9a09-531f1cee1839", + "--subscription", "subscriptionPQRMN001", + "--pricing-type", "PRE_RATED", + "--timestamp", "2019-10-03T10:58:00", + "--quantity", "7", + "--price", "24.56" + }); + + SubmitUsageCommand cmd = (SubmitUsageCommand) jc + .getObjects() + .get(0); + assertEquals("2019-10-03T10:58:00Z", cmd + .getTimestamp() + .toString()); + } + + @Test(expected = ParameterException.class) + public void whenParsedCustomerIdNotUUID_thenParameterException() { + jc.parse(new String[] { + "--customer", "customer001", + "--subscription", "subscriptionPQRMN001", + "--pricing-type", "PRE_RATED", + "--timestamp", "2019-10-03T10:58:00", + "--quantity", "7", + "--price", "24.56" + }); + } +} diff --git a/pom.xml b/pom.xml index d2433d11b3..d692e507f9 100644 --- a/pom.xml +++ b/pom.xml @@ -541,6 +541,7 @@ libraries libraries-2 + libraries-3 libraries-data libraries-data-2 libraries-data-db @@ -1307,6 +1308,7 @@ libraries + libraries-3 libraries-data libraries-data-2 libraries-data-db