Skip to content

Commit 5dac09b

Browse files
committed
add DiscountService
1 parent fb92b7f commit 5dac09b

3 files changed

Lines changed: 282 additions & 0 deletions

File tree

NetLicensingClient/src/main/java/com/labs64/netlicensing/domain/Constants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ public static final class Product {
8282
public static final String PROP_VAT_MODE = "vatMode";
8383

8484
public static final class Discount {
85+
public static final String ENDPOINT_PATH = "discount";
86+
public static final String ENDPOINT_RESOLVE_PATH = "resolve";
87+
public static final String CART_TOTAL = "cartTotal";
8588
public static final String TOTAL_PRICE = "totalPrice";
8689
public static final String AMOUNT_FIX = "amountFix";
8790
public static final String AMOUNT_PERCENT = "amountPercent";
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* https://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package com.labs64.netlicensing.service;
14+
15+
import java.math.BigDecimal;
16+
17+
import org.apache.commons.lang3.StringUtils;
18+
19+
import com.labs64.netlicensing.domain.Constants;
20+
import com.labs64.netlicensing.domain.entity.LicenseTemplate;
21+
import com.labs64.netlicensing.domain.vo.Context;
22+
import com.labs64.netlicensing.exception.NetLicensingException;
23+
import com.labs64.netlicensing.exception.ServiceException;
24+
import com.labs64.netlicensing.provider.Form;
25+
import com.labs64.netlicensing.util.CheckUtils;
26+
27+
/**
28+
* Provides discount handling routines.
29+
*/
30+
public class DiscountService {
31+
32+
private static final int HTTP_STATUS_NOT_FOUND = 404;
33+
private static final String NOT_FOUND_EXCEPTION_PREFIX = "NotFoundException:";
34+
35+
/**
36+
* Resolves a discount license template by product number and promo code.
37+
*
38+
* @param context
39+
* determines the vendor on whose behalf the call is performed
40+
* @param productNumber
41+
* product number that defines promo code uniqueness scope
42+
* @param promoCode
43+
* promo code to resolve
44+
* @return resolved discount license template or null if discount was not found
45+
* @throws NetLicensingException
46+
* any non-not-found service error
47+
*/
48+
public static LicenseTemplate resolve(final Context context, final String productNumber, final String promoCode)
49+
throws NetLicensingException {
50+
return resolve(context, productNumber, promoCode, null);
51+
}
52+
53+
/**
54+
* Resolves a discount license template by product number and promo code.
55+
*
56+
* @param context
57+
* determines the vendor on whose behalf the call is performed
58+
* @param productNumber
59+
* product number that defines promo code uniqueness scope
60+
* @param promoCode
61+
* promo code to resolve
62+
* @param cartTotal
63+
* optional cart total used by the API to validate minimum cart total
64+
* @return resolved discount license template or null if discount was not found
65+
* @throws NetLicensingException
66+
* any non-not-found service error
67+
*/
68+
public static LicenseTemplate resolve(final Context context, final String productNumber, final String promoCode,
69+
final BigDecimal cartTotal) throws NetLicensingException {
70+
CheckUtils.paramNotEmpty(productNumber, "productNumber");
71+
CheckUtils.paramNotEmpty(promoCode, "promoCode");
72+
73+
final Form form = new Form();
74+
if (cartTotal != null) {
75+
form.param(Constants.Product.Discount.CART_TOTAL, cartTotal.toString());
76+
}
77+
78+
try {
79+
return NetLicensingService.getInstance().post(context, resolveEndpoint(productNumber, promoCode), form,
80+
LicenseTemplate.class);
81+
} catch (final ServiceException e) {
82+
if (isNotFound(e)) {
83+
return null;
84+
}
85+
throw e;
86+
}
87+
}
88+
89+
private static String resolveEndpoint(final String productNumber, final String promoCode) {
90+
return Constants.Product.ENDPOINT_PATH + "/" + productNumber + "/"
91+
+ Constants.Product.Discount.ENDPOINT_PATH + "/" + promoCode + "/"
92+
+ Constants.Product.Discount.ENDPOINT_RESOLVE_PATH;
93+
}
94+
95+
private static boolean isNotFound(final ServiceException e) {
96+
return (e.getStatusCode() == HTTP_STATUS_NOT_FOUND)
97+
|| StringUtils.startsWith(e.getMessage(), NOT_FOUND_EXCEPTION_PREFIX);
98+
}
99+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* https://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package com.labs64.netlicensing.service;
14+
15+
import java.math.BigDecimal;
16+
17+
import jakarta.ws.rs.Consumes;
18+
import jakarta.ws.rs.POST;
19+
import jakarta.ws.rs.Path;
20+
import jakarta.ws.rs.PathParam;
21+
import jakarta.ws.rs.core.MediaType;
22+
import jakarta.ws.rs.core.MultivaluedMap;
23+
import jakarta.ws.rs.core.Response;
24+
25+
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
29+
import com.labs64.netlicensing.domain.Constants;
30+
import com.labs64.netlicensing.domain.entity.LicenseTemplate;
31+
import com.labs64.netlicensing.domain.vo.Context;
32+
import com.labs64.netlicensing.domain.vo.LicenseType;
33+
import com.labs64.netlicensing.exception.ServiceException;
34+
import com.labs64.netlicensing.schema.SchemaFunction;
35+
import com.labs64.netlicensing.schema.context.InfoEnum;
36+
import com.labs64.netlicensing.schema.context.Item;
37+
import com.labs64.netlicensing.schema.context.Netlicensing;
38+
import com.labs64.netlicensing.schema.context.ObjectFactory;
39+
import com.labs64.netlicensing.schema.context.Property;
40+
41+
import static org.junit.jupiter.api.Assertions.assertEquals;
42+
import static org.junit.jupiter.api.Assertions.assertNotNull;
43+
import static org.junit.jupiter.api.Assertions.assertNull;
44+
import static org.junit.jupiter.api.Assertions.assertThrows;
45+
46+
/**
47+
* Integration tests for {@link DiscountService}.
48+
*/
49+
public class DiscountServiceTest extends BaseServiceTest {
50+
51+
private static final String PRODUCT_NUMBER = "P001-TEST";
52+
private static final String PROMO_CODE = "PROMO-001";
53+
private static final String LICENSE_TEMPLATE_NUMBER = "LT-DISCOUNT-001";
54+
private static final String MIN_CART_TOTAL = "minCartTotal";
55+
private static final String PROMO_CODE_PROPERTY = "promoCode";
56+
57+
private static Context context;
58+
59+
@BeforeAll
60+
public static void setup() {
61+
context = createContext();
62+
}
63+
64+
@BeforeEach
65+
public void resetResourceState() {
66+
DiscountServiceResource.lastProductNumber = null;
67+
DiscountServiceResource.lastPromoCode = null;
68+
DiscountServiceResource.lastCartTotal = null;
69+
}
70+
71+
@Test
72+
public void testResolve() throws Exception {
73+
final LicenseTemplate discount = DiscountService.resolve(context, PRODUCT_NUMBER, PROMO_CODE);
74+
75+
assertNotNull(discount);
76+
assertEquals(LICENSE_TEMPLATE_NUMBER, discount.getNumber());
77+
assertEquals("Promo discount", discount.getName());
78+
assertEquals(LicenseType.FEATURE, discount.getLicenseType());
79+
assertEquals("25", discount.getProperties().get("discountAmount"));
80+
assertEquals(PROMO_CODE, discount.getProperties().get(PROMO_CODE_PROPERTY));
81+
assertEquals(PRODUCT_NUMBER, DiscountServiceResource.lastProductNumber);
82+
assertEquals(PROMO_CODE, DiscountServiceResource.lastPromoCode);
83+
assertNull(DiscountServiceResource.lastCartTotal);
84+
}
85+
86+
@Test
87+
public void testResolveWithCartTotal() throws Exception {
88+
final LicenseTemplate discount = DiscountService.resolve(context, PRODUCT_NUMBER, PROMO_CODE,
89+
new BigDecimal("100.50"));
90+
91+
assertNotNull(discount);
92+
assertEquals(LICENSE_TEMPLATE_NUMBER, discount.getNumber());
93+
assertEquals("100.50", DiscountServiceResource.lastCartTotal);
94+
}
95+
96+
@Test
97+
public void testResolveReturnsNullWhenDiscountDoesNotExist() throws Exception {
98+
final LicenseTemplate discount = DiscountService.resolve(context, PRODUCT_NUMBER, "UNKNOWN");
99+
100+
assertNull(discount);
101+
}
102+
103+
@Test
104+
public void testResolveRethrowsNonNotFoundServiceException() {
105+
final ServiceException ex = assertThrows(ServiceException.class,
106+
() -> DiscountService.resolve(context, PRODUCT_NUMBER, "BROKEN"));
107+
108+
assertEquals("IllegalOperationException: Discount resolve failed", ex.getMessage());
109+
}
110+
111+
@Override
112+
protected Class<?> getResourceClass() {
113+
return DiscountServiceResource.class;
114+
}
115+
116+
@Path(REST_API_PATH + "/product/{productNumber}/discount/{promoCode}/resolve")
117+
public static class DiscountServiceResource {
118+
119+
private static String lastProductNumber;
120+
private static String lastPromoCode;
121+
private static String lastCartTotal;
122+
123+
private final ObjectFactory objectFactory = new ObjectFactory();
124+
125+
@POST
126+
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
127+
public Response resolve(@PathParam("productNumber") final String productNumber,
128+
@PathParam("promoCode") final String promoCode, final MultivaluedMap<String, String> formParams) {
129+
lastProductNumber = productNumber;
130+
lastPromoCode = promoCode;
131+
lastCartTotal = formParams.getFirst(Constants.Product.Discount.CART_TOTAL);
132+
133+
if ("UNKNOWN".equals(promoCode)) {
134+
return notFoundResponse();
135+
}
136+
if ("BROKEN".equals(promoCode)) {
137+
return errorResponse("IllegalOperationException", "Discount resolve failed");
138+
}
139+
140+
final Netlicensing netlicensing = objectFactory.createNetlicensing();
141+
netlicensing.setItems(objectFactory.createNetlicensingItems());
142+
143+
final Item item = objectFactory.createItem();
144+
item.setType("LicenseTemplate");
145+
netlicensing.getItems().getItem().add(item);
146+
147+
addProperty(item, Constants.NUMBER, LICENSE_TEMPLATE_NUMBER);
148+
addProperty(item, Constants.NAME, "Promo discount");
149+
addProperty(item, Constants.ACTIVE, Boolean.TRUE.toString());
150+
addProperty(item, Constants.LicenseTemplate.LICENSE_TYPE, LicenseType.FEATURE.value());
151+
addProperty(item, Constants.LicenseTemplate.AUTOMATIC, Boolean.FALSE.toString());
152+
addProperty(item, Constants.LicenseTemplate.HIDDEN, Boolean.TRUE.toString());
153+
addProperty(item, Constants.LicenseTemplate.HIDE_LICENSES, Boolean.TRUE.toString());
154+
addProperty(item, Constants.ProductModule.PRODUCT_MODULE_NUMBER, "PM-DISCOUNT-001");
155+
addProperty(item, PROMO_CODE_PROPERTY, promoCode);
156+
addProperty(item, "discountAmount", "25");
157+
addProperty(item, MIN_CART_TOTAL, "100");
158+
159+
return Response.ok(netlicensing).build();
160+
}
161+
162+
private Response notFoundResponse() {
163+
final Netlicensing netlicensing = objectFactory.createNetlicensing();
164+
SchemaFunction.addInfo(netlicensing, "NotFoundException", InfoEnum.ERROR,
165+
"Requested discount does not exist.");
166+
return Response.status(Response.Status.NOT_FOUND).entity(netlicensing).build();
167+
}
168+
169+
private Response errorResponse(final String exceptionId, final String errorMessage) {
170+
final Netlicensing netlicensing = objectFactory.createNetlicensing();
171+
SchemaFunction.addInfo(netlicensing, exceptionId, InfoEnum.ERROR, errorMessage);
172+
return Response.status(Response.Status.BAD_REQUEST).entity(netlicensing).build();
173+
}
174+
175+
private void addProperty(final Item item, final String name, final String value) {
176+
final Property property = new Property(value, name);
177+
item.getProperty().add(property);
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)