Commit 4fe5b34f authored by Sumaiyya Burney's avatar Sumaiyya Burney

Merge branch 'dev' into 'master'

Done!

See merge request !12
parents 5adc9113 40d6b69a
File added
...@@ -31,3 +31,4 @@ build/ ...@@ -31,3 +31,4 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
/src/main/resources/secret.properties
FROM openjdk:11-jre-slim
COPY target/promotions-0.0.1-SNAPSHOT.jar /usr/local/lib/promotions.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/promotions.jar"]
# For local testing, comment the top part out and uncomment the bottom
# DO NOT commit to GL with the top part commented and/or the bottom part uncommnted!!!!!!
#FROM maven:3.6.0-jdk-11-slim AS build
#COPY src /home/app/src
#COPY pom.xml /home/app
#RUN mvn -f /home/app/pom.xml clean package -DskipTests
#
#FROM openjdk:11-jre-slim
#COPY --from=build /home/app/target/promotions-0.0.1-SNAPSHOT.jar /usr/local/lib/promotions.jar
#EXPOSE 8081
#ENTRYPOINT ["java","-jar","/usr/local/lib/promotions.jar"]
...@@ -29,7 +29,11 @@ ...@@ -29,7 +29,11 @@
<groupId>org.springframework.kafka</groupId> <groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId> <artifactId>spring-kafka</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
......
File added
package com.nisum.ascend.promotions.controller;
import com.nisum.ascend.promotions.exception.PromotionNotFoundException;
import com.nisum.ascend.promotions.exception.PromotionAlreadyExistsException;
import com.nisum.ascend.promotions.model.Promotion;
import com.nisum.ascend.promotions.service.PromotionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api/promos")
public class PromotionsController {
@Autowired
PromotionService promotionService;
@GetMapping()
public ResponseEntity<Flux<Promotion>> getAllPromotions(@RequestParam(required = false) String sku){
if (sku != null){
return ResponseEntity.ok(promotionService.findPromotionsByProductSku(sku));
}
else return ResponseEntity.ok(promotionService.findAll());
}
@GetMapping("/{id}")
public ResponseEntity<Mono<Promotion>> getPromotionById(@PathVariable String id){
Mono<Promotion> responseData = promotionService
.findPromoById(id)
.switchIfEmpty(Mono.error(new PromotionNotFoundException(id)));
return ResponseEntity.ok().body(responseData);
}
@PostMapping("")
public ResponseEntity<Mono<Promotion>> createPromotion(@RequestBody Promotion newPromotion){
Mono<Promotion> responseData = promotionService.createPromotion(newPromotion)
.onErrorResume(throwable -> Mono.error(new PromotionAlreadyExistsException()));
return ResponseEntity.ok(responseData);
}
@PostMapping("/bulkSearch")
public ResponseEntity<Flux<Promotion>> bulkSearchPromotionsByItemSku(@RequestBody List<String> skus){
return ResponseEntity.ok(promotionService.bulkSearchSku(skus));
}
@PutMapping("/{promoId}")
public Mono<ResponseEntity<Promotion>> updatePromotionById(@PathVariable("promoId") String promoId, @RequestBody Promotion newPromotion){
return promotionService.updatePromotion(promoId,newPromotion)
.map(updatedProduct -> ResponseEntity.ok(updatedProduct))
.defaultIfEmpty(ResponseEntity.badRequest().build());
}
@DeleteMapping("/{promoId}")
public Mono<ResponseEntity<Void>> deletePromotionById(@PathVariable("promoId") String promoId){
return promotionService.deletePromotion(promoId)
.map(res -> ResponseEntity.ok().<Void>build())
.defaultIfEmpty(ResponseEntity.notFound().build());
}
}
package com.nisum.ascend.promotions.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE)
public class PromotionAlreadyExistsException extends RuntimeException{
public PromotionAlreadyExistsException(){
super("promotion already existed");
}
}
package com.nisum.ascend.promotions.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class PromotionNotFoundException extends RuntimeException {
public PromotionNotFoundException(String id) {
super("The promotion with the ID of " + id + " was not found.");
}
}
package com.nisum.ascend.promotions.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{
HttpStatus status;
public ResourceNotFoundException(HttpStatus status, String message) {
super(message);
this.status = status;
}
}
package com.nisum.ascend.promotions.exception;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import springfox.documentation.spring.web.json.Json;
import java.nio.charset.StandardCharsets;
@Component
@Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof PromotionNotFoundException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(ex.getMessage().getBytes(StandardCharsets.UTF_8));
// marks the response as complete and forbids writing to it
return exchange.getResponse().writeWith(Flux.just(buffer));
}
if (ex instanceof PromotionAlreadyExistsException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
}
package com.nisum.ascend.promotions.model;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Getter
@Setter
@Document(collection="promotions")
public class Promotion {
@Id
private String id;
@Indexed(unique=true)
private String promotionId;
private String productSku;
private float discountPercentage;
private int minimumQuantity;
public Promotion(String promotionId, String productSku, float discountPercentage, int minimumQuantity) {
this.promotionId = promotionId;
this.productSku = productSku;
this.discountPercentage = discountPercentage;
this.minimumQuantity = minimumQuantity;
}
}
package com.nisum.ascend.promotions.repository;
import com.nisum.ascend.promotions.model.Promotion;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Repository
public interface PromotionRepository extends ReactiveMongoRepository<Promotion, String> {
Flux<Promotion> findByProductSku(String sku);
Mono<Promotion> findByPromotionId(String promoId);
}
package com.nisum.ascend.promotions.service;
import com.nisum.ascend.promotions.exception.ResourceNotFoundException;
import com.nisum.ascend.promotions.model.Promotion;
import com.nisum.ascend.promotions.repository.PromotionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.util.List;
import reactor.core.publisher.Mono;
@Service
public class PromotionService {
@Autowired
PromotionRepository promotionRepository;
public Flux<Promotion> findAll(){
return promotionRepository.findAll();
}
public Mono<Promotion> findPromoById(String promoId) {
return promotionRepository.findByPromotionId(promoId);
}
public Flux<Promotion> findPromotionsByProductSku(String sku){
return promotionRepository.findByProductSku(sku);
}
public Flux<Promotion> bulkSearchSku(List<String> skus){
return Flux.fromIterable(skus).flatMap(sku -> findPromotionsByProductSku(sku));
}
public Mono<Promotion> createPromotion(Promotion promotion){
return promotionRepository.save(promotion);
}
public Mono<Promotion> updatePromotion(String promoId, Promotion promotion){
return promotionRepository.findByPromotionId(promoId)
.flatMap(promo ->{
promo.setDiscountPercentage(promotion.getDiscountPercentage());
promo.setProductSku(promotion.getProductSku());
promo.setMinimumQuantity(promotion.getMinimumQuantity());
return promotionRepository.save(promo);
});
}
public Mono<Promotion> deletePromotion(String promoId){
return promotionRepository.findByPromotionId(promoId)
.flatMap(promo -> promotionRepository.delete(promo)
.then(Mono.just(promo)))
.switchIfEmpty(Mono.error(new ResourceNotFoundException(HttpStatus.NOT_FOUND, "Promo Not Found")));
}
}
spring.data.mongodb.uri=mongodb+srv://admin:${db.password}@inventory-promotions.d4nfz.mongodb\
.net/${spring.data.mongodb.database}?retryWrites=true&w=majority
spring.data.mongodb.database=test
\ No newline at end of file
server.port=8082
spring.data.mongodb.uri=mongodb+srv://admin:${db.password}@inventory-promotions.d4nfz.mongodb\
.net/${spring.data.mongodb.database}?retryWrites=true&w=majority
spring.data.mongodb.database=products-promotions-DB
\ No newline at end of file
package com.nisum.ascend.promotions.controller;
import com.nisum.ascend.promotions.model.Promotion;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
@AutoConfigureWebTestClient
@ActiveProfiles("test")
@Slf4j
class PromotionsControllerTest {
@Autowired
private WebTestClient webTestClient;
@BeforeEach
void setUp() {
}
WebTestClient.ResponseSpec getPromotionWebClient(String promoId) {
return webTestClient.get().uri("/api/promos/" + promoId).exchange();
}
@Test
void getAllPromotions() {
webTestClient.get().uri("/api/promos").exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_VALUE)
.expectBodyList(Promotion.class)
.consumeWith(promo ->{
List<Promotion> promos = promo.getResponseBody();
assert promos != null;
promos.forEach(p ->{
assertNotNull(p.getPromotionId());
assertNotNull(p.getProductSku());
System.out.println(p.getPromotionId());
});
});
}
@Test
void getPromotionById() {
getPromotionWebClient("0003")
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_VALUE)
.expectBody()
.jsonPath("promotionId", "0003");
}
@Test
void createPromotion(){
Random rd = new Random();
String newPromoId = Integer.toString(rd.nextInt());
Promotion promotion = new Promotion(newPromoId,"AFP-1", 0.5f,5);
webTestClient.post().uri("/api/promos")
.contentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.body(Mono.just(promotion),Promotion.class)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.promotionId","50OFF");
}
@Test
void getPromotionsByProductSku(){
String skuWithOnePromo = "AFP-1";
String skuWithMultiplePromos = "AFP-2";
webTestClient.get()
.uri(uriBuilder -> uriBuilder
.path("/api/promos")
.queryParam("sku", skuWithOnePromo)
.build())
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_VALUE)
.expectBodyList(Promotion.class)
.consumeWith(promo ->{
List<Promotion> promos = promo.getResponseBody();
assert promos != null;
promos.forEach(p -> assertEquals(skuWithOnePromo, p.getProductSku()));
});
webTestClient.get()
.uri(uriBuilder -> uriBuilder
.path("/api/promos")
.queryParam("sku", skuWithMultiplePromos)
.build())
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_VALUE)
.expectBodyList(Promotion.class)
.consumeWith(promo ->{
List<Promotion> promos = promo.getResponseBody();
assert promos != null;
promos.forEach(p -> assertEquals(skuWithMultiplePromos, p.getProductSku()));
});
}
@Test
void editPromotion(){
Promotion promotion = new Promotion("0003","1000",(float) .5,1);
webTestClient.put()
.uri("/api/promos/0003")
.contentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.accept(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.body(Mono.just(promotion),Promotion.class)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.minimumQuantity").isEqualTo(1);
}
@Test
void deletePromotion(){
Promotion promo = new Promotion("deleteTest", "AFP-1", 0.33f, 3);
webTestClient.get()
.uri("/api/promos".concat("/{promoId}"), promo.getPromotionId())
.exchange()
.expectStatus().isNotFound();
webTestClient.post()
.uri("/api/promos")
.contentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.body(Mono.just(promo),Promotion.class)
.exchange()
.expectStatus().isOk()
.expectBody(Promotion.class);
webTestClient.delete()
.uri("/api/promos".concat("/{promoId}"),promo.getPromotionId())
.accept(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.exchange()
.expectStatus().isOk()
.expectBody(Void.class);
webTestClient.get()
.uri("/api/promos".concat("/{promoId}"), promo.getPromotionId())
.exchange()
.expectStatus().isNotFound();
}
@Test
public void bulkSearchTest(){
List<String> skus = Arrays.asList("AFP-1", "AFP-2");
webTestClient.post().uri("/api/promos/bulkSearch")
.contentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE))
.body(Mono.just(skus), List.class)
.exchange()
.expectStatus().isOk()
.expectBodyList(Promotion.class)
.consumeWith(promo ->{
List<Promotion> promos = promo.getResponseBody();
assert promos != null;
promos.forEach(p -> {
String sku = p.getProductSku();
String promotionId = p.getPromotionId();
assertTrue(skus.contains(sku));
System.out.println("Product SKU: " + sku);
System.out.println("Promotion ID: " + promotionId);
} );
});
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment