Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c7318e
added project base
MohammmedAhmed8 May 2, 2025
b0734f8
added .github file
MohammmedAhmed8 May 2, 2025
280a921
fixed linter issue
MohammmedAhmed8 May 2, 2025
c059be8
fixed linter issue
MohammmedAhmed8 May 2, 2025
d27f2e1
added order model, repository, service and controller
MohammmedAhmed8 May 3, 2025
05f9c90
fixed linter issues
MohammmedAhmed8 May 3, 2025
c26372d
Add endpoints to get orders by user ID, cancel orders, and update ord…
oaly2 May 4, 2025
da784fc
Fix linter issue
oaly2 May 4, 2025
5d20343
Resolve Comments
oaly2 May 4, 2025
8cc4b16
Adjusted lint checks
oaly2 May 4, 2025
7191cb3
added swagger and logger in the controller
MohammmedAhmed8 May 6, 2025
3542af2
Merge branch 'dev' into Crud-Operations
MohammmedAhmed8 May 6, 2025
fd5c1e5
fixed linter issues
MohammmedAhmed8 May 6, 2025
a5423a9
Added Global Exception Handler and Endpoint to Check out an order
oaly2 May 8, 2025
71d8612
Create docker-compose.yml
oaly2 May 10, 2025
ae077b0
Add DockerFile
oaly2 May 11, 2025
5e4bb96
Removed Checkout endpoint and Adjusted OrderStatus enum
oaly2 May 12, 2025
94a14a8
added order address
MohammmedAhmed8 May 12, 2025
afcea40
fixed linter issues
MohammmedAhmed8 May 12, 2025
7a36737
Merge branch 'dev' into Omar/3-endpoints
oaly2 May 12, 2025
306e5a9
Merge branch 'Crud-Operations' into Omar/3-endpoints
oaly2 May 12, 2025
d8dd9ff
Merge pull request #3 from Podzilla/Omar/3-endpoints
oaly2 May 12, 2025
e881d83
fixed errors
MohammmedAhmed8 May 12, 2025
c911c79
Chnage long -> UUID in OrderService
oaly2 May 12, 2025
3bf3427
Merge branch 'Crud-Operations' of https://github.com/Podzilla/order i…
oaly2 May 12, 2025
667adea
long -> UUID in OrderController
oaly2 May 12, 2025
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
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM openjdk:25-ea-4-jdk-oraclelinux9

WORKDIR /app

COPY target/order-0.0.1-SNAPSHOT.jar /app/order-0.0.1-SNAPSHOT.jar

ENTRYPOINT [ "java", "-jar", "/app/order-0.0.1-SNAPSHOT.jar" ]
44 changes: 44 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
services:
backend:
image: openjdk:25-ea-4-jdk-oraclelinux9
container_name: order
ports:
- "8080:8080"
depends_on:
- order_db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://order_db:5432/orderDB
volumes:
- ./target:/app
- ./logs:/logs
command: [ "java", "-jar", "/app/order-0.0.1-SNAPSHOT.jar" ]

order_db:
image: postgres:14.17
container_name: order_db
environment:
POSTGRES_PASSWORD: 1234
POSTGRES_USER: postgres
POSTGRES_DB: orderDB
ports:
- "5432:5432"

loki:
image: grafana/loki:3.5.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml

grafana:
image: grafana/grafana:10.4.1
container_name: grafana
ports:
- "3000:3000"
depends_on:
- loki
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
Expand Down
141 changes: 141 additions & 0 deletions src/main/java/com/podzilla/order/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.podzilla.order.controller;

import com.podzilla.order.model.Order;
import com.podzilla.order.model.OrderStatus;
import com.podzilla.order.service.OrderService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.util.List;
import java.util.Optional;
import java.util.UUID;

@RestController
@RequestMapping("/orders")
@Tag(name = "Order API", description = "Order management operations")
public class OrderController {

private final OrderService orderService;
private static final Logger LOGGER =
LoggerFactory.getLogger(OrderController.class);

@Autowired
public OrderController(final OrderService orderService) {
this.orderService = orderService;
}

@PostMapping
@Operation(summary = "Create a new order",
description = "Creates a new order and returns the created order")
@ApiResponse(responseCode = "200", description = "Order created "
+ "successfully")
public ResponseEntity<Order> createOrder(@RequestBody final Order order) {
LOGGER.info("Creating new order: {}", order);
Order createdOrder = orderService.createOrder(order);
return ResponseEntity.ok(createdOrder);
}

@GetMapping
@Operation(summary = "Get all orders",
description = "Returns a list of all orders")
@ApiResponse(responseCode = "200", description = "List of orders")
public ResponseEntity<List<Order>> getAllOrders() {
LOGGER.info("Fetching all orders");
List<Order> orders = orderService.getAllOrders();
return ResponseEntity.ok(orders);
}

@GetMapping("/{id}")
@Operation(summary = "Get order by ID",
description = "Returns an order by its ID")
@ApiResponse(responseCode = "200", description = "Order found")
public ResponseEntity<Order> getOrderById(@PathVariable final UUID id) {
LOGGER.info("Fetching order with ID: {}", id);
Order order = orderService.getOrderById(id)
.orElseThrow(() -> new RuntimeException("Order not found"));
return ResponseEntity.ok(order);
}

@PutMapping("/{id}")
@Operation(summary = "Update an order",
description = "Updates an existing order and returns the updated "
+ "order")
@ApiResponse(responseCode = "200", description = "Order updated "
+ "successfully")
public ResponseEntity<Order> updateOrder(@PathVariable final UUID id,
@RequestBody final Order order) {
LOGGER.info("Updating order with ID: {}", id);
Order updatedOrder = orderService.updateOrder(id, order);
return ResponseEntity.ok(updatedOrder);
}

@DeleteMapping("/{id}")
@Operation(summary = "Delete an order",
description = "Deletes an order by its ID")
@ApiResponse(responseCode = "204", description = "Order deleted "
+ "successfully")
public ResponseEntity<Void> deleteOrder(@PathVariable final UUID id) {
LOGGER.info("Deleting order with ID: {}", id);
orderService.deleteOrder(id);
return ResponseEntity.noContent().build();
}

@GetMapping("/user/{userId}")
@Operation(
summary = "Get order by user ID",
description = "Fetches an order based on the provided user ID"
)
@ApiResponse(
responseCode = "200", description = "Order found"
)
public ResponseEntity<Optional<Order>> getOrderByUserId(
@PathVariable final UUID userId) {
Optional<Order> order = orderService.getOrderByUserId(userId);
LOGGER.info("Order found for user ID: {}", userId);
return ResponseEntity.ok(order);
}

@PutMapping("/cancel/{id}")
@Operation(
summary = "Cancel order",
description = "Cancels an order based on the provided order ID"
)
@ApiResponse(
responseCode = "200", description = "Order cancelled"
)
public ResponseEntity<Order> cancelOrder(@PathVariable final UUID id) {
Order order = orderService.cancelOrder(id);
LOGGER.info("Order with ID: {} cancelled", id);
return ResponseEntity.ok(order);
}

@PutMapping("/status/{id}")
@Operation(
summary = "Update order status",
description = "Updates the status of an order based on "
+ "the provided order ID"
)
@ApiResponse(
responseCode = "200", description = "Order status updated"
)
public ResponseEntity<Order> updateOrderStatus(@PathVariable final UUID id,
@RequestBody final OrderStatus status) {
Order order = orderService.updateOrderStatus(id, status);
LOGGER.info("Order status updated for ID: {}", id);
return ResponseEntity.ok(order);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/podzilla/order/exception/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.podzilla.order.exception;

import lombok.Getter;
import org.springframework.http.HttpStatus;

import java.time.LocalDateTime;

@Getter
public class ErrorResponse {
private final String message;
private final HttpStatus status;
private final LocalDateTime timestamp;

public ErrorResponse(final String message, final HttpStatus status) {
this.message = message;
this.status = status;
this.timestamp = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.podzilla.order.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {


@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDeniedException(
final AccessDeniedException exception) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.FORBIDDEN));
}

@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthenticationException(
final AuthenticationException exception) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.UNAUTHORIZED));
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFoundException(
final NotFoundException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.NOT_FOUND));
}

@ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handleValidationException(
final ValidationException exception) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.BAD_REQUEST));
}

@ExceptionHandler(InvalidActionException.class)
public ResponseEntity<ErrorResponse> handleInvalidActionException(
final InvalidActionException exception) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.BAD_REQUEST));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(
final Exception exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(exception.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.podzilla.order.exception;

public class InvalidActionException extends RuntimeException {
public InvalidActionException(final String message) {
super("Invalid action: " + message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.podzilla.order.exception;

public class NotFoundException extends RuntimeException {
public NotFoundException(final String message) {
super("Not Found: " + message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.podzilla.order.exception;

public class ValidationException extends RuntimeException {
public ValidationException(final String message) {
super("Validation error: " + message);
}
}
37 changes: 37 additions & 0 deletions src/main/java/com/podzilla/order/model/Address.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.podzilla.order.model;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.UUID;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "addresses")
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;

private String street;
private String city;
private String state;
private String country;
private String postalCode;

@ManyToOne
@JoinColumn(name = "order_id", nullable = false)
private Order order;
}
Loading