Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public static final class Product {
public static final String PROP_VAT_MODE = "vatMode";

public static final class Discount {
public static final String ENDPOINT_PATH = "discount";
public static final String ENDPOINT_RESOLVE_PATH = "resolve";
public static final String CART_TOTAL = "cartTotal";
public static final String TOTAL_PRICE = "totalPrice";
public static final String AMOUNT_FIX = "amountFix";
public static final String AMOUNT_PERCENT = "amountPercent";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@
*/
package com.labs64.netlicensing.service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.labs64.netlicensing.domain.Constants;
import com.labs64.netlicensing.domain.entity.LicenseTemplate;
import com.labs64.netlicensing.domain.entity.Product;
import com.labs64.netlicensing.domain.vo.Context;
import com.labs64.netlicensing.domain.vo.Page;
import com.labs64.netlicensing.exception.NetLicensingException;
import com.labs64.netlicensing.exception.ServiceException;
import com.labs64.netlicensing.provider.Form;
import com.labs64.netlicensing.util.CheckUtils;
import com.labs64.netlicensing.util.ConvertUtils;

Expand All @@ -38,6 +42,9 @@
*/
public class ProductService {

private static final int HTTP_STATUS_NOT_FOUND = 404;
private static final String NOT_FOUND_EXCEPTION_PREFIX = "NotFoundException:";

/**
* Creates new product with given properties.
*
Expand Down Expand Up @@ -140,4 +147,69 @@ public static void delete(final Context context, final String number, final bool
NetLicensingService.getInstance().delete(context, Constants.Product.ENDPOINT_PATH + "/" + number, params);
}

/**
* Resolves a discount license template by product number and promo code.
*
* @param context
* determines the vendor on whose behalf the call is performed
* @param productNumber
* product number that defines promo code uniqueness scope
* @param promoCode
* promo code to resolve
* @return resolved discount license template or null if discount was not found
* @throws NetLicensingException
* any non-not-found service error
*/
public static LicenseTemplate resolveDiscount(final Context context, final String productNumber,
final String promoCode) throws NetLicensingException {
return resolveDiscount(context, productNumber, promoCode, null);
}

/**
* Resolves a discount license template by product number and promo code.
*
* @param context
* determines the vendor on whose behalf the call is performed
* @param productNumber
* product number that defines promo code uniqueness scope
* @param promoCode
* promo code to resolve
* @param cartTotal
* optional cart total used by the API to validate minimum cart total
* @return resolved discount license template or null if discount was not found
* @throws NetLicensingException
* any non-not-found service error
*/
public static LicenseTemplate resolveDiscount(final Context context, final String productNumber,
final String promoCode, final BigDecimal cartTotal) throws NetLicensingException {
CheckUtils.paramNotEmpty(productNumber, "productNumber");
CheckUtils.paramNotEmpty(promoCode, "promoCode");

final Form form = new Form();
if (cartTotal != null) {
form.param(Constants.Product.Discount.CART_TOTAL, cartTotal.toString());
}

try {
return NetLicensingService.getInstance().post(context, resolveDiscountEndpoint(productNumber, promoCode),
form, LicenseTemplate.class);
} catch (final ServiceException e) {
if (isNotFound(e)) {
return null;
}
throw e;
}
}

private static String resolveDiscountEndpoint(final String productNumber, final String promoCode) {
return Constants.Product.ENDPOINT_PATH + "/" + productNumber + "/"
+ Constants.Product.Discount.ENDPOINT_PATH + "/" + promoCode + "/"
+ Constants.Product.Discount.ENDPOINT_RESOLVE_PATH;
}

private static boolean isNotFound(final ServiceException e) {
return (e.getStatusCode() == HTTP_STATUS_NOT_FOUND)
|| StringUtils.startsWith(e.getMessage(), NOT_FOUND_EXCEPTION_PREFIX);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,36 @@
*/
package com.labs64.netlicensing.service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import com.labs64.netlicensing.domain.Constants;
import com.labs64.netlicensing.domain.entity.LicenseTemplate;
import com.labs64.netlicensing.domain.entity.Product;
import com.labs64.netlicensing.domain.entity.impl.ProductImpl;
import com.labs64.netlicensing.domain.vo.Context;
import com.labs64.netlicensing.domain.vo.LicenseType;
import com.labs64.netlicensing.domain.vo.Page;
import com.labs64.netlicensing.exception.ServiceException;
import com.labs64.netlicensing.schema.SchemaFunction;
import com.labs64.netlicensing.schema.context.InfoEnum;
import com.labs64.netlicensing.schema.context.Item;
import com.labs64.netlicensing.schema.context.Netlicensing;
import com.labs64.netlicensing.schema.context.Property;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand All @@ -43,6 +56,11 @@ public class ProductServiceTest extends BaseServiceTest {

private static final String PRODUCT_CUSTOM_PROPERTY = "CustomProperty";
private static final String PRODUCT_DELETING_PROPERTY = "toBeDeleted";
private static final String PRODUCT_NUMBER = "P001-TEST";
private static final String PROMO_CODE = "PROMO-001";
private static final String LICENSE_TEMPLATE_NUMBER = "LT-DISCOUNT-001";
private static final String MIN_CART_TOTAL = "minCartTotal";
private static final String PROMO_CODE_PROPERTY = "promoCode";

// *** NLIC Tests ***

Expand All @@ -53,6 +71,13 @@ public static void setup() {
context = createContext();
}

@BeforeEach
public void resetResourceState() {
ProductServiceResource.lastProductNumber = null;
ProductServiceResource.lastPromoCode = null;
ProductServiceResource.lastCartTotal = null;
}

@Test
public void testCreate() throws Exception {
final Product newProduct = new ProductImpl();
Expand Down Expand Up @@ -152,6 +177,46 @@ public void testDelete() throws Exception {
assertEquals("NotFoundException: Requested product does not exist", e.getMessage());
}

@Test
public void testResolveDiscount() throws Exception {
final LicenseTemplate discount = ProductService.resolveDiscount(context, PRODUCT_NUMBER, PROMO_CODE);

assertNotNull(discount);
assertEquals(LICENSE_TEMPLATE_NUMBER, discount.getNumber());
assertEquals("Promo discount", discount.getName());
assertEquals(LicenseType.FEATURE, discount.getLicenseType());
assertEquals("25", discount.getProperties().get("discountAmount"));
assertEquals(PROMO_CODE, discount.getProperties().get(PROMO_CODE_PROPERTY));
assertEquals(PRODUCT_NUMBER, ProductServiceResource.lastProductNumber);
assertEquals(PROMO_CODE, ProductServiceResource.lastPromoCode);
assertNull(ProductServiceResource.lastCartTotal);
}

@Test
public void testResolveDiscountWithCartTotal() throws Exception {
final LicenseTemplate discount = ProductService.resolveDiscount(context, PRODUCT_NUMBER, PROMO_CODE,
new BigDecimal("100.50"));

assertNotNull(discount);
assertEquals(LICENSE_TEMPLATE_NUMBER, discount.getNumber());
assertEquals("100.50", ProductServiceResource.lastCartTotal);
}

@Test
public void testResolveDiscountReturnsNullWhenDiscountDoesNotExist() throws Exception {
final LicenseTemplate discount = ProductService.resolveDiscount(context, PRODUCT_NUMBER, "UNKNOWN");

assertNull(discount);
}

@Test
public void testResolveDiscountRethrowsNonNotFoundServiceException() {
final ServiceException ex = assertThrows(ServiceException.class,
() -> ProductService.resolveDiscount(context, PRODUCT_NUMBER, "BROKEN"));

assertEquals("IllegalOperationException: Discount resolve failed", ex.getMessage());
}

// *** NLIC test mock resource ***

@Override
Expand All @@ -162,6 +227,10 @@ protected java.lang.Class<?> getResourceClass() {
@Path(REST_API_PATH + "/product")
public static class ProductServiceResource extends AbstractNLICServiceResource {

private static String lastProductNumber;
private static String lastPromoCode;
private static String lastCartTotal;

public ProductServiceResource() {
super("product");
}
Expand All @@ -183,6 +252,56 @@ public Response create(final MultivaluedMap<String, String> formParams) {
public Response delete(final String productNumber, final UriInfo uriInfo) {
return delete(productNumber, "P001-TEST", uriInfo.getQueryParameters());
}

@POST
@Path("{productNumber}/discount/{promoCode}/resolve")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response resolveDiscount(@PathParam("productNumber") final String productNumber,
@PathParam("promoCode") final String promoCode, final MultivaluedMap<String, String> formParams) {
lastProductNumber = productNumber;
lastPromoCode = promoCode;
lastCartTotal = formParams.getFirst(Constants.Product.Discount.CART_TOTAL);

if ("UNKNOWN".equals(promoCode)) {
return notFoundResponse();
}
if ("BROKEN".equals(promoCode)) {
return errorResponse("IllegalOperationException", "Discount resolve failed");
}

final Netlicensing netlicensing = objectFactory.createNetlicensing();
netlicensing.setItems(objectFactory.createNetlicensingItems());

final Item item = objectFactory.createItem();
item.setType("LicenseTemplate");
netlicensing.getItems().getItem().add(item);

addProperty(item, Constants.NUMBER, LICENSE_TEMPLATE_NUMBER);
addProperty(item, Constants.NAME, "Promo discount");
addProperty(item, Constants.ACTIVE, Boolean.TRUE.toString());
addProperty(item, Constants.LicenseTemplate.LICENSE_TYPE, LicenseType.FEATURE.value());
addProperty(item, Constants.LicenseTemplate.AUTOMATIC, Boolean.FALSE.toString());
addProperty(item, Constants.LicenseTemplate.HIDDEN, Boolean.TRUE.toString());
addProperty(item, Constants.LicenseTemplate.HIDE_LICENSES, Boolean.TRUE.toString());
addProperty(item, Constants.ProductModule.PRODUCT_MODULE_NUMBER, "PM-DISCOUNT-001");
addProperty(item, PROMO_CODE_PROPERTY, promoCode);
addProperty(item, "discountAmount", "25");
addProperty(item, MIN_CART_TOTAL, "100");

return Response.ok(netlicensing).build();
}

private Response notFoundResponse() {
final Netlicensing netlicensing = objectFactory.createNetlicensing();
SchemaFunction.addInfo(netlicensing, "NotFoundException", InfoEnum.ERROR,
"Requested discount does not exist.");
return Response.status(Response.Status.NOT_FOUND).entity(netlicensing).build();
}

private void addProperty(final Item item, final String name, final String value) {
final Property property = new Property(value, name);
item.getProperty().add(property);
}
}

}
Loading