Skip to content

Commit f9298c9

Browse files
committed
Implemented Create Order API with tests
1 parent 4e2f508 commit f9298c9

21 files changed

+731
-9
lines changed

order-service/pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<java.version>21</java.version>
1818
<spring-cloud.version>2023.0.1</spring-cloud.version>
1919
<springdoc-openapi.version>2.5.0</springdoc-openapi.version>
20+
<instancio.version>4.5.1</instancio.version>
2021
<spotless.version>2.43.0</spotless.version>
2122
<dockerImageName>kavinduperera/bookstore-${project.artifactId}</dockerImageName>
2223
</properties>
@@ -111,6 +112,12 @@
111112
<artifactId>rest-assured</artifactId>
112113
<scope>test</scope>
113114
</dependency>
115+
<dependency>
116+
<groupId>org.instancio</groupId>
117+
<artifactId>instancio-junit</artifactId>
118+
<version>${instancio.version}</version>
119+
<scope>test</scope>
120+
</dependency>
114121
</dependencies>
115122
<dependencyManagement>
116123
<dependencies>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
/**
4+
* @author Kavindu Perera
5+
*/
6+
public class InvalidOrderException extends RuntimeException {
7+
8+
public InvalidOrderException(String message) {
9+
super(message);
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import com.codebykavindu.bookstore.orders.domain.models.Address;
4+
import com.codebykavindu.bookstore.orders.domain.models.Customer;
5+
import com.codebykavindu.bookstore.orders.domain.models.OrderStatus;
6+
import jakarta.persistence.AttributeOverride;
7+
import jakarta.persistence.AttributeOverrides;
8+
import jakarta.persistence.CascadeType;
9+
import jakarta.persistence.Column;
10+
import jakarta.persistence.Embedded;
11+
import jakarta.persistence.Entity;
12+
import jakarta.persistence.EnumType;
13+
import jakarta.persistence.Enumerated;
14+
import jakarta.persistence.GeneratedValue;
15+
import jakarta.persistence.GenerationType;
16+
import jakarta.persistence.Id;
17+
import jakarta.persistence.OneToMany;
18+
import jakarta.persistence.SequenceGenerator;
19+
import jakarta.persistence.Table;
20+
import java.time.LocalDateTime;
21+
import java.util.Set;
22+
23+
/**
24+
* @author Kavindu Perera
25+
*/
26+
@Entity
27+
@Table(name = "orders")
28+
class OrderEntity {
29+
30+
@Id
31+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "order_id_generator")
32+
@SequenceGenerator(name = "order_id_generator", sequenceName = "order_id_seq")
33+
private Long id;
34+
35+
@Column(nullable = false)
36+
private String orderNumber;
37+
38+
@Column(name = "username", nullable = false)
39+
private String userName;
40+
41+
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
42+
private Set<OrderItemEntity> items;
43+
44+
@Embedded
45+
@AttributeOverrides(
46+
value = {
47+
@AttributeOverride(name = "name", column = @Column(name = "customer_name")),
48+
@AttributeOverride(name = "email", column = @Column(name = "customer_email")),
49+
@AttributeOverride(name = "phone", column = @Column(name = "customer_phone"))
50+
})
51+
private Customer customer;
52+
53+
@Embedded
54+
@AttributeOverrides(
55+
value = {
56+
@AttributeOverride(name = "addressLine1", column = @Column(name = "delivery_address_line1")),
57+
@AttributeOverride(name = "addressLine2", column = @Column(name = "delivery_address_line2")),
58+
@AttributeOverride(name = "city", column = @Column(name = "delivery_address_city")),
59+
@AttributeOverride(name = "state", column = @Column(name = "delivery_address_state")),
60+
@AttributeOverride(name = "zipCode", column = @Column(name = "delivery_address_zip_code")),
61+
@AttributeOverride(name = "country", column = @Column(name = "delivery_address_country")),
62+
})
63+
private Address deliveryAddress;
64+
65+
@Enumerated(EnumType.STRING)
66+
private OrderStatus status;
67+
68+
private String comments;
69+
70+
@Column(name = "created_at", nullable = false, updatable = false)
71+
private LocalDateTime createdAt = LocalDateTime.now();
72+
73+
@Column(name = "updated_at")
74+
private LocalDateTime updatedAt;
75+
76+
public Long getId() {
77+
return id;
78+
}
79+
80+
public void setId(Long id) {
81+
this.id = id;
82+
}
83+
84+
public String getOrderNumber() {
85+
return orderNumber;
86+
}
87+
88+
public void setOrderNumber(String orderNumber) {
89+
this.orderNumber = orderNumber;
90+
}
91+
92+
public String getUserName() {
93+
return userName;
94+
}
95+
96+
public void setUserName(String userName) {
97+
this.userName = userName;
98+
}
99+
100+
public Set<OrderItemEntity> getItems() {
101+
return items;
102+
}
103+
104+
public void setItems(Set<OrderItemEntity> items) {
105+
this.items = items;
106+
}
107+
108+
public Customer getCustomer() {
109+
return customer;
110+
}
111+
112+
public void setCustomer(Customer customer) {
113+
this.customer = customer;
114+
}
115+
116+
public Address getDeliveryAddress() {
117+
return deliveryAddress;
118+
}
119+
120+
public void setDeliveryAddress(Address deliveryAddress) {
121+
this.deliveryAddress = deliveryAddress;
122+
}
123+
124+
public OrderStatus getStatus() {
125+
return status;
126+
}
127+
128+
public void setStatus(OrderStatus status) {
129+
this.status = status;
130+
}
131+
132+
public String getComments() {
133+
return comments;
134+
}
135+
136+
public void setComments(String comments) {
137+
this.comments = comments;
138+
}
139+
140+
public LocalDateTime getCreatedAt() {
141+
return createdAt;
142+
}
143+
144+
public void setCreatedAt(LocalDateTime createdAt) {
145+
this.createdAt = createdAt;
146+
}
147+
148+
public LocalDateTime getUpdatedAt() {
149+
return updatedAt;
150+
}
151+
152+
public void setUpdatedAt(LocalDateTime updatedAt) {
153+
this.updatedAt = updatedAt;
154+
}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.GeneratedValue;
6+
import jakarta.persistence.GenerationType;
7+
import jakarta.persistence.Id;
8+
import jakarta.persistence.JoinColumn;
9+
import jakarta.persistence.ManyToOne;
10+
import jakarta.persistence.SequenceGenerator;
11+
import jakarta.persistence.Table;
12+
import java.math.BigDecimal;
13+
14+
/**
15+
* @author Kavindu Perera
16+
*/
17+
@Entity
18+
@Table(name = "order_items")
19+
class OrderItemEntity {
20+
21+
@Id
22+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "order_item_id_generator")
23+
@SequenceGenerator(name = "order_item_id_generator", sequenceName = "order_item_id_seq")
24+
private Long id;
25+
26+
@Column(nullable = false)
27+
private String code;
28+
29+
private String name;
30+
31+
@Column(nullable = false)
32+
private BigDecimal price;
33+
34+
@Column(nullable = false)
35+
private Integer quantity;
36+
37+
@ManyToOne(optional = false)
38+
@JoinColumn(name = "order_id")
39+
private OrderEntity order;
40+
41+
public Long getId() {
42+
return id;
43+
}
44+
45+
public void setId(Long id) {
46+
this.id = id;
47+
}
48+
49+
public String getCode() {
50+
return code;
51+
}
52+
53+
public void setCode(String code) {
54+
this.code = code;
55+
}
56+
57+
public String getName() {
58+
return name;
59+
}
60+
61+
public void setName(String name) {
62+
this.name = name;
63+
}
64+
65+
public BigDecimal getPrice() {
66+
return price;
67+
}
68+
69+
public void setPrice(BigDecimal price) {
70+
this.price = price;
71+
}
72+
73+
public Integer getQuantity() {
74+
return quantity;
75+
}
76+
77+
public void setQuantity(Integer quantity) {
78+
this.quantity = quantity;
79+
}
80+
81+
public OrderEntity getOrder() {
82+
return order;
83+
}
84+
85+
public void setOrder(OrderEntity order) {
86+
this.order = order;
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import com.codebykavindu.bookstore.orders.domain.models.CreateOrderRequest;
4+
import com.codebykavindu.bookstore.orders.domain.models.OrderItem;
5+
import com.codebykavindu.bookstore.orders.domain.models.OrderStatus;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
import java.util.UUID;
9+
10+
/**
11+
* @author Kavindu Perera
12+
*/
13+
class OrderMapper {
14+
private OrderMapper() {}
15+
16+
static OrderEntity convertToEntity(CreateOrderRequest request) {
17+
OrderEntity newOrder = new OrderEntity();
18+
newOrder.setOrderNumber(UUID.randomUUID().toString());
19+
newOrder.setStatus(OrderStatus.NEW);
20+
newOrder.setCustomer(request.customer());
21+
newOrder.setDeliveryAddress(request.deliveryAddress());
22+
Set<OrderItemEntity> orderItems = new HashSet<>();
23+
for (OrderItem item : request.items()) {
24+
OrderItemEntity orderItem = new OrderItemEntity();
25+
orderItem.setCode(item.code());
26+
orderItem.setName(item.name());
27+
orderItem.setPrice(item.price());
28+
orderItem.setQuantity(item.quantity());
29+
orderItem.setOrder(newOrder);
30+
orderItems.add(orderItem);
31+
}
32+
newOrder.setItems(orderItems);
33+
return newOrder;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import org.springframework.data.jpa.repository.JpaRepository;
4+
5+
/**
6+
* @author Kavindu Perera
7+
*/
8+
interface OrderRepository extends JpaRepository<OrderEntity, Long> {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import com.codebykavindu.bookstore.orders.domain.models.CreateOrderRequest;
4+
import com.codebykavindu.bookstore.orders.domain.models.CreateOrderResponse;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.stereotype.Service;
8+
import org.springframework.transaction.annotation.Transactional;
9+
10+
/**
11+
* @author Kavindu Perera
12+
*/
13+
@Service
14+
@Transactional
15+
public class OrderService {
16+
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
17+
18+
private final OrderRepository orderRepository;
19+
20+
OrderService(OrderRepository orderRepository) {
21+
this.orderRepository = orderRepository;
22+
}
23+
24+
public CreateOrderResponse createOrder(String userName, CreateOrderRequest request) {
25+
OrderEntity newOrder = OrderMapper.convertToEntity(request);
26+
newOrder.setUserName(userName);
27+
OrderEntity savedOrder = this.orderRepository.save(newOrder);
28+
log.info("Created Order with orderNumber={}", savedOrder.getOrderNumber());
29+
return new CreateOrderResponse(savedOrder.getOrderNumber());
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.codebykavindu.bookstore.orders.domain;
2+
3+
import org.springframework.stereotype.Service;
4+
5+
/**
6+
* @author Kavindu Perera
7+
*/
8+
@Service
9+
public class SecurityService {
10+
11+
public String getLoginUsername() {
12+
return "user";
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.codebykavindu.bookstore.orders.domain.models;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
/**
6+
* @author Kavindu Perera
7+
*/
8+
public record Address(
9+
@NotBlank(message = "AddressLine1 is required") String addressLine1,
10+
String addressLine2,
11+
@NotBlank(message = "City is required") String city,
12+
@NotBlank(message = "State is required") String state,
13+
@NotBlank(message = "ZipCode is required") String zipCode,
14+
@NotBlank(message = "Country is required") String country) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.codebykavindu.bookstore.orders.domain.models;
2+
3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.NotEmpty;
5+
import java.util.Set;
6+
7+
/**
8+
* @author Kavindu Perera
9+
*/
10+
public record CreateOrderRequest(
11+
@Valid @NotEmpty(message = "Items cannot be empty") Set<OrderItem> items,
12+
@Valid Customer customer,
13+
@Valid Address deliveryAddress) {}

0 commit comments

Comments
 (0)