MaterialController.java
package com.university.bookstore.controller;
import java.util.List;
import java.util.Optional;
import org.springframework.http.HttpStatus;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.university.bookstore.api.MaterialStore;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Material.MaterialType;
/**
* REST Controller for Material management operations.
*
* <p>This controller demonstrates how the existing MaterialStore interface
* can be easily exposed as REST endpoints without modifying the core
* business logic. It showcases the extensibility of the hexagonal
* architecture design.</p>
*
* <p>All operations delegate to the existing MaterialStore implementation,
* maintaining the same business logic and validation rules.</p>
*
* @author Navid Mohaghegh
* @version 1.0
* @since 2024-12-19
*/
@RestController
@RequestMapping("/api/materials")
public class MaterialController {
private final MaterialStore materialStore;
/**
* Constructor with dependency injection of MaterialStore.
*
* @param materialStore the material store implementation
*/
public MaterialController(MaterialStore materialStore) {
this.materialStore = materialStore;
}
/**
* GET /api/materials - Retrieve all materials
*
* @return list of all materials
*/
@GetMapping
public ResponseEntity<List<Material>> getAllMaterials() {
List<Material> materials = materialStore.getAllMaterials();
return ResponseEntity.ok(materials);
}
/**
* GET /api/materials/{id} - Retrieve a specific material by ID
*
* @param id the material ID
* @return the material if found, 404 if not found
*/
@GetMapping("/{id}")
public ResponseEntity<Material> getMaterialById(@PathVariable("id") String id) {
Optional<Material> material = materialStore.findById(id);
return material.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* POST /api/materials - Create a new material
*
* @param material the material to create
* @return the created material or 400 if invalid
*/
@PostMapping
public ResponseEntity<Material> createMaterial(@RequestBody Material material) {
try {
boolean added = materialStore.addMaterial(material);
if (added) {
return ResponseEntity.status(HttpStatus.CREATED).body(material);
} else {
return ResponseEntity.badRequest().build();
}
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
/**
* PUT /api/materials/{id} - Update an existing material
*
* @param id the material ID
* @param material the updated material
* @return the updated material or 404 if not found
*/
@PutMapping("/{id}")
public ResponseEntity<Material> updateMaterial(@PathVariable("id") String id, @RequestBody Material material) {
// Remove existing material and add the updated one
Optional<Material> existing = materialStore.removeMaterial(id);
if (existing.isPresent()) {
boolean added = materialStore.addMaterial(material);
if (added) {
return ResponseEntity.ok(material);
} else {
// Rollback: add the original material back
materialStore.addMaterial(existing.get());
return ResponseEntity.badRequest().build();
}
}
return ResponseEntity.notFound().build();
}
/**
* DELETE /api/materials/{id} - Delete a material
*
* @param id the material ID
* @return 204 if deleted, 404 if not found
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteMaterial(@PathVariable("id") String id) {
Optional<Material> removed = materialStore.removeMaterial(id);
return removed.isPresent() ?
ResponseEntity.noContent().build() :
ResponseEntity.notFound().build();
}
/**
* GET /api/materials/search/title?q={query} - Search materials by title
*
* @param q the search query
* @return list of matching materials
*/
@GetMapping("/search/title")
public ResponseEntity<List<Material>> searchByTitle(@RequestParam("q") String q) {
List<Material> materials = materialStore.searchByTitle(q);
return ResponseEntity.ok(materials);
}
/**
* GET /api/materials/search/creator?q={query} - Search materials by creator
*
* @param q the search query
* @return list of matching materials
*/
@GetMapping("/search/creator")
public ResponseEntity<List<Material>> searchByCreator(@RequestParam("q") String q) {
List<Material> materials = materialStore.searchByCreator(q);
return ResponseEntity.ok(materials);
}
/**
* GET /api/materials/type/{type} - Get materials by type
*
* @param type the material type
* @return list of materials of the specified type
*/
@GetMapping("/type/{type}")
public ResponseEntity<List<Material>> getMaterialsByType(@PathVariable("type") MaterialType type) {
List<Material> materials = materialStore.getMaterialsByType(type);
return ResponseEntity.ok(materials);
}
/**
* GET /api/materials/recent?years={years} - Get recent materials
*
* @param years number of years to look back
* @return list of recent materials
*/
@GetMapping("/recent")
public ResponseEntity<List<Material>> getRecentMaterials(@RequestParam(value = "years", defaultValue = "5") int years) {
List<Material> materials = materialStore.findRecentMaterials(years);
return ResponseEntity.ok(materials);
}
/**
* Get materials by price range.
*
* @param min minimum price
* @param max maximum price
* @return list of materials in price range
*/
@GetMapping("/price-range")
public ResponseEntity<List<Material>> getMaterialsByPriceRange(
@RequestParam("min") double min,
@RequestParam("max") double max) {
List<Material> materials = materialStore.getMaterialsByPriceRange(min, max);
return ResponseEntity.ok(materials);
}
/**
* GET /api/materials/stats - Get inventory statistics
*
* @return inventory statistics
*/
@GetMapping("/stats")
public ResponseEntity<MaterialStore.InventoryStats> getInventoryStats() {
MaterialStore.InventoryStats stats = materialStore.getInventoryStats();
return ResponseEntity.ok(stats);
}
/**
* GET /api/materials/count - Get total material count
*
* @return total number of materials
*/
@GetMapping("/count")
public ResponseEntity<Integer> getMaterialCount() {
int count = materialStore.size();
return ResponseEntity.ok(count);
}
}