diff --git a/OrderProcessingAPI/build.gradle b/OrderProcessingAPI/build.gradle index a310425..bd7d9ca 100644 --- a/OrderProcessingAPI/build.gradle +++ b/OrderProcessingAPI/build.gradle @@ -2,6 +2,7 @@ plugins { id 'org.jetbrains.kotlin.jvm' version "1.9.22" id "org.jetbrains.kotlin.plugin.allopen" version "1.9.22" id 'io.quarkus' + id "org.jetbrains.kotlin.plugin.noarg" version "1.9.22" } repositories { diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/Order.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/Order.kt new file mode 100644 index 0000000..9a7f5a8 --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/Order.kt @@ -0,0 +1,70 @@ +package com.blazc.model.order + +import com.blazc.OrderGrpc +import com.blazc.model.payment.PaymentType +import com.blazc.utils.DateFormatter +import org.bson.types.ObjectId +import java.time.LocalDateTime + +data class Order ( + var id: ObjectId? = null, + var sellerId: ObjectId? = null, + var deliveryPersonId: ObjectId? = null, + var address: String, + var customerName: String, + var items: List, + var status: OrderStatus, + var orderDate: LocalDateTime, + var paymentType: PaymentType, + var totalPrice: Int +) { + + constructor() : this( + id = ObjectId(), + sellerId = ObjectId(), + deliveryPersonId = ObjectId(), + address = "", + customerName = "", + items = emptyList(), + status = OrderStatus.PENDING, + orderDate = LocalDateTime.now(), + paymentType = PaymentType.CASH, + totalPrice = 0 + ) + + companion object { + fun fromGrpc(order: OrderGrpc.Order): Order { + return Order( + id = ObjectId(order.id), + sellerId = ObjectId(order.sellerId), + deliveryPersonId = ObjectId(order.deliveryPersonId), + address = order.address, + customerName = order.customerName, + items = order.itemsList.map { + OrderItem.fromGrpc(it) + }, + status = OrderStatus.fromGrpc(order.status), + orderDate = DateFormatter.toLocalDateTime(order.orderDate), + paymentType = PaymentType.fromGrpc(order.paymentType), + totalPrice = order.totalPrice + ) + } + + fun toGrpc(order: Order): OrderGrpc.Order { + return OrderGrpc.Order.newBuilder() + .setId(order.id.toString()) + .setSellerId(order.sellerId.toString()) + .setDeliveryPersonId(order.deliveryPersonId.toString()) + .setAddress(order.address) + .setCustomerName(order.customerName) + .addAllItems(order.items.map { + OrderItem.toGrpc(it) + }) + .setStatus(OrderStatus.toGrpc(order.status)) + .setOrderDate(DateFormatter.toString(order.orderDate)) + .setPaymentType(PaymentType.toGrpc(order.paymentType)) + .setTotalPrice(order.totalPrice) + .build() + } + } +} diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderItem.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderItem.kt new file mode 100644 index 0000000..1ce5b26 --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderItem.kt @@ -0,0 +1,40 @@ +package com.blazc.model.order + +import com.blazc.OrderGrpc +import com.blazc.model.product.Product +import org.bson.types.ObjectId + +data class OrderItem( + var id: ObjectId? = null, + var product: Product, + var quantity: Int, + var price: Int +) { + + constructor() : this( + id = ObjectId(), + product = Product(), + quantity = 0, + price = 0 + ) + + companion object { + fun fromGrpc(orderItem: OrderGrpc.OrderItem): OrderItem { + return OrderItem( + id = ObjectId(orderItem.id), + product = Product.fromGrpc(orderItem.product), + quantity = orderItem.quantity, + price = orderItem.price + ) + } + + fun toGrpc(orderItem: OrderItem): OrderGrpc.OrderItem { + return OrderGrpc.OrderItem.newBuilder() + .setId(orderItem.id.toString()) + .setProduct(Product.toGrpc(orderItem.product)) + .setQuantity(orderItem.quantity) + .setPrice(orderItem.price) + .build() + } + } +} diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderStatus.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderStatus.kt new file mode 100644 index 0000000..78f009e --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/order/OrderStatus.kt @@ -0,0 +1,37 @@ +package com.blazc.model.order + +import com.blazc.OrderGrpc + +enum class OrderStatus { + PENDING, + PREPARING, + READY_FOR_PICKUP, + DELIVERING, + DELIVERED, + CANCELLED; + + companion object { + fun fromGrpc(status: OrderGrpc.OrderStatus): OrderStatus { + return when (status) { + OrderGrpc.OrderStatus.PENDING -> PENDING + OrderGrpc.OrderStatus.PREPARING -> PREPARING + OrderGrpc.OrderStatus.READY_FOR_PICKUP -> READY_FOR_PICKUP + OrderGrpc.OrderStatus.DELIVERING -> DELIVERING + OrderGrpc.OrderStatus.DELIVERED -> DELIVERED + OrderGrpc.OrderStatus.CANCELLED -> CANCELLED + OrderGrpc.OrderStatus.UNRECOGNIZED -> PENDING + } + } + + fun toGrpc(status: OrderStatus): OrderGrpc.OrderStatus { + return when (status) { + PENDING -> OrderGrpc.OrderStatus.PENDING + PREPARING -> OrderGrpc.OrderStatus.PREPARING + READY_FOR_PICKUP -> OrderGrpc.OrderStatus.READY_FOR_PICKUP + DELIVERING -> OrderGrpc.OrderStatus.DELIVERING + DELIVERED -> OrderGrpc.OrderStatus.DELIVERED + CANCELLED -> OrderGrpc.OrderStatus.CANCELLED + } + } + } +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/model/payment/PaymentType.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/payment/PaymentType.kt new file mode 100644 index 0000000..14bdcfe --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/payment/PaymentType.kt @@ -0,0 +1,25 @@ +package com.blazc.model.payment + +import com.blazc.OrderGrpc + +enum class PaymentType { + CREDIT_CARD, + CASH; + + companion object { + fun fromGrpc(type: OrderGrpc.PaymentType): PaymentType { + return when (type) { + OrderGrpc.PaymentType.CREDIT_CARD -> PaymentType.CREDIT_CARD + OrderGrpc.PaymentType.CASH -> PaymentType.CASH + OrderGrpc.PaymentType.UNRECOGNIZED -> PaymentType.CASH + } + } + + fun toGrpc(type: PaymentType): OrderGrpc.PaymentType { + return when (type) { + PaymentType.CREDIT_CARD -> OrderGrpc.PaymentType.CREDIT_CARD + PaymentType.CASH -> OrderGrpc.PaymentType.CASH + } + } + } +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/model/product/Product.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/product/Product.kt new file mode 100644 index 0000000..8857a6f --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/model/product/Product.kt @@ -0,0 +1,36 @@ +package com.blazc.model.product + +import com.blazc.OrderGrpc +import org.bson.types.ObjectId + + +data class Product( + var id: ObjectId? = null, + var name: String, + var price: Int // 100 = 1 +) { + + constructor() : this( + id = ObjectId(), + name = "", + price = 0 + ) + + companion object { + fun fromGrpc(productGrpc: OrderGrpc.Product): Product { + return Product( + id = ObjectId(productGrpc.id), + name = productGrpc.name, + price = productGrpc.price + ) + } + + fun toGrpc(product: Product): OrderGrpc.Product { + return OrderGrpc.Product.newBuilder() + .setId(product.id.toString()) + .setName(product.name) + .setPrice(product.price) + .build() + } + } +} diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/repository/OrderRepository.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/repository/OrderRepository.kt new file mode 100644 index 0000000..f44e1c5 --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/repository/OrderRepository.kt @@ -0,0 +1,19 @@ +package com.blazc.repository + +import com.blazc.model.order.Order +import io.quarkus.mongodb.panache.PanacheMongoRepository +import jakarta.enterprise.context.ApplicationScoped +import org.bson.types.ObjectId + +@ApplicationScoped +class OrderRepository : PanacheMongoRepository { + + fun findAllBySellerId(sellerId: ObjectId): List { + return find("sellerId", sellerId).list() + } + + fun findAllByDeliveryPersonId(deliveryPersonId: ObjectId): List { + return find("deliveryPersonId", deliveryPersonId).list() + } + +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/service/OrderService.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/service/OrderService.kt new file mode 100644 index 0000000..dac1e5a --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/service/OrderService.kt @@ -0,0 +1,165 @@ +package com.blazc.service + +import com.blazc.OrderGrpc +import com.blazc.OrderServiceGrpc +import com.blazc.model.order.Order +import com.blazc.repository.OrderRepository +import io.grpc.stub.StreamObserver +import io.quarkus.grpc.GrpcService +import jakarta.inject.Inject +import org.bson.types.ObjectId +import io.grpc.Status + +@GrpcService +class OrderService : OrderServiceGrpc.OrderServiceImplBase() { + + @Inject + lateinit var orderRepository: OrderRepository + + override fun health(request: OrderGrpc.Empty, responseObserver: StreamObserver) { + val confirmation = OrderGrpc.Confirmation.newBuilder() + .setError("") + .setMessage("success") + .build() + + responseObserver.onNext(confirmation) + responseObserver.onCompleted() + } + + override fun createOrder(request: OrderGrpc.Order, responseObserver: StreamObserver) { + val order: Order? + + try { + order = Order.fromGrpc(request) + } catch (e: Exception) { + e.printStackTrace() + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid order format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + orderRepository.persistOrUpdate(order) + + val response = OrderGrpc.Confirmation.newBuilder() + .setError("") + .setMessage("created") + .build() + + responseObserver.onNext(response) + responseObserver.onCompleted() + } + + override fun getOrder(request: OrderGrpc.GetOrderRequest, responseObserver: StreamObserver) { + val orderId: ObjectId? + + try { + orderId = ObjectId(request.id) + } catch (e: Exception) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid objectId format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + println(orderId) + + val order = orderRepository.findById(orderId) + if (order == null) { + print("not found") + responseObserver.onError(Status.NOT_FOUND.asRuntimeException()) + responseObserver.onCompleted() + return + } + + println("found") + + val response = Order.toGrpc(order) + responseObserver.onNext(response) + responseObserver.onCompleted() + } + + override fun getOrdersBySeller(request: OrderGrpc.GetOrdersRequest, responseObserver: StreamObserver) { + val sellerId: ObjectId? + + try { + sellerId = ObjectId(request.id) + } catch (e: Exception) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid objectId format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + val orders = orderRepository.findAllBySellerId(sellerId) + + orders.forEach { + responseObserver.onNext(Order.toGrpc(it)) + } + + responseObserver.onCompleted() + } + + override fun getOrdersByDeliveryPerson(request: OrderGrpc.GetOrdersRequest, responseObserver: StreamObserver) { + val deliveryPersonId: ObjectId? + + try { + deliveryPersonId = ObjectId(request.id) + } catch (e: Exception) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid objectId format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + val orders = orderRepository.findAllByDeliveryPersonId(deliveryPersonId) + + print(orders.size) + + orders.forEach { + responseObserver.onNext(Order.toGrpc(it)) + } + + responseObserver.onCompleted() + } + + override fun updateOrder(request: OrderGrpc.Order, responseObserver: StreamObserver) { + val order: Order? + + try { + order = Order.fromGrpc(request) + } catch (e: Exception) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid order format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + orderRepository.update(order) + + val response = OrderGrpc.Confirmation.newBuilder() + .setError("") + .setMessage("updated") + .build() + + responseObserver.onNext(response) + responseObserver.onCompleted() + } + + override fun deleteOrder(request: OrderGrpc.DeleteOrderRequest, responseObserver: StreamObserver) { + val orderId: ObjectId? + + try { + orderId = ObjectId(request.id) + } catch (e: Exception) { + responseObserver.onError(Status.INVALID_ARGUMENT.withDescription("invalid objectId format").asRuntimeException()) + responseObserver.onCompleted() + return + } + + val deleted = orderRepository.deleteById(orderId) + + val response = OrderGrpc.Confirmation.newBuilder() + .setError(if (deleted) "" else "order was not found") + .setMessage(if (deleted) "deleted" else "error while deleting") + .build() + + responseObserver.onNext(response) + responseObserver.onCompleted() + } +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/kotlin/com/blazc/utils/DateFormatter.kt b/OrderProcessingAPI/src/main/kotlin/com/blazc/utils/DateFormatter.kt new file mode 100644 index 0000000..e1964ce --- /dev/null +++ b/OrderProcessingAPI/src/main/kotlin/com/blazc/utils/DateFormatter.kt @@ -0,0 +1,19 @@ +package com.blazc.utils + +import java.time.LocalDateTime + +class DateFormatter { + companion object { + + fun toLocalDateTime(date: String): LocalDateTime { + val localDateTime = LocalDateTime.parse(date, java.time.format.DateTimeFormatter.ISO_DATE_TIME) + return localDateTime + } + + fun toString(date: LocalDateTime): String { + val string = date.format(java.time.format.DateTimeFormatter.ISO_DATE_TIME) + return string + } + + } +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/proto/order_service.proto b/OrderProcessingAPI/src/main/proto/order_service.proto new file mode 100644 index 0000000..505bbd6 --- /dev/null +++ b/OrderProcessingAPI/src/main/proto/order_service.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +package com.blazc; + +option java_outer_classname = "OrderGrpc"; + +service OrderService { + rpc CreateOrder (Order) returns (Confirmation) {} + rpc GetOrder (GetOrderRequest) returns (Order) {} + rpc UpdateOrder (Order) returns (Confirmation) {} + rpc GetOrdersBySeller (GetOrdersRequest) returns (stream Order) {} + rpc GetOrdersByDeliveryPerson (GetOrdersRequest) returns (stream Order) {} + rpc DeleteOrder (DeleteOrderRequest) returns (Confirmation) {} + rpc Health (Empty) returns (Confirmation) {} +} + +message Empty {} + +message Confirmation { + string message = 2; + string error = 3; +} + +message GetOrdersRequest { + string id = 1; +} + +message GetOrderRequest { + string id = 1; +} + +message DeleteOrderRequest { + string id = 1; +} + +message Order { + string id = 1; + string sellerId = 2; + string deliveryPersonId = 3; + string address = 4; + string customerName = 5; + repeated OrderItem items = 6; + OrderStatus status = 7; + string orderDate = 8; + PaymentType paymentType = 9; + int32 totalPrice = 10; +} + +message OrderItem { + string id = 1; + Product product = 2; + int32 quantity = 3; + int32 price = 4; +} + +message Product { + string id = 1; + string name = 2; + int32 price = 3; +} + +enum PaymentType { + CREDIT_CARD = 0; + CASH = 2; +} + +enum OrderStatus { + PENDING = 0; + PREPARING = 1; + READY_FOR_PICKUP = 2; + DELIVERING = 3; + DELIVERED = 4; + CANCELLED = 5; +} \ No newline at end of file diff --git a/OrderProcessingAPI/src/main/resources/application.properties b/OrderProcessingAPI/src/main/resources/application.properties index e69de29..2759034 100644 --- a/OrderProcessingAPI/src/main/resources/application.properties +++ b/OrderProcessingAPI/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.mongodb.connection-string = mongodb://localhost:27017 +quarkus.mongodb.database = orders \ No newline at end of file