-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Table of Contents |
---|
The Tool Rental Service is simple tool rental application with the below features:
- Customers rent a tool for a specified number of days.
- When a customer checks out a tool, a Rental Agreement is produced.
- The store charges a daily rental fee, whose amount is different for each tool type.
- Some tools are free of charge on weekends or holidays.
- Clerks may give customers a discount that is applied to the total daily charges to reduce the final charge.
- The service exposes an REST API that supports the below resources/operations
- Operation to read all available tools
- Operation to add/delete tools available for rent [Future state]
- Operation to read pricing details per tool
- Operation to add/update pricing details per tool [Future state]
- Operation to checkout and create a rental agreement per tool
- Each operation should validate user entered input and throw
HTTP BAD Request
for validation errors - The service should follow an API First Design approach while adding new functionality. See API First Design approach for more details
- The service should auto-generate Spring REST code using the supplied Open API file. The OpenAPI file is available here
The Class Diagram includes some classes auto-generated from the OpenApi specification for this service
- The OpenAPI specification for the Tool Rental Service is available here.
- The service uses an API first approach to implement this service. The Java and Spring annotated REST classes were auto generated from The Online Swagger Editor
- The
AbstractController
clas implements the API interface - The below JPA repository classes are used to interface with the database
- ToolRepository to fetch available tools from the database
- ToolPriceRepository to fetch pricing details about a tool
- RentalRequestRepository to persist each rental request in the database
- RentalAgreementRepository to persist rental agreements in the database
---
title: ToolRentalService Class Diagram
---
classDiagram
namespace ControllerClasses{
class ToolRentalAPI{
<<interface>>
}
class Tool{
}
class ToolRentalPrice{
}
class RentalRequest{
}
class RentalAgreement{
}
class AbstractController{
}
}
Tool <-- ToolRentalAPI
ToolRentalPrice <-- ToolRentalAPI
RentalRequest <-- ToolRentalAPI
RentalAgreement <-- ToolRentalAPI
ToolRentalAPI <|-- AbstractController
namespace ServiceClasses {
class IRentalAgreementService {
<<interface>>
}
class IHoliday {
<<interface>>
}
class FourthJuly{
+isHoliday()
}
class LaborDay{
+isHoliday()
}
class Weekend{
+isHoliday()
}
class ObserverableHoliday{
+isHoliday()
}
class RentalAgreementService{
}
class RentalDurationService{
}
class DateRangeDetails {
}
}
IRentalAgreementService <|-- RentalAgreementService
RentalDurationService <-- RentalAgreementService
Weekend <-- RentalDurationService
ObserverableHoliday <-- RentalDurationService
DateRangeDetails <-- RentalDurationService
DateRangeDetails <-- RentalAgreementService
IRentalAgreementService <-- AbstractController
namespace PersistenceClasses {
class ToolRepository {
<<interface>>
}
class ToolPriceRepository {
<<interface>>
}
class RentalRequestRepository {
<<interface>>
}
class RentalAgreementRepository {
<<interface>>
}
class ToolEntity{
}
class ToolPriceEntity{
}
class RentalRequestEntity{
}
class RentalAgreementEntity{
}
}
ToolRepository <-- RentalAgreementService
ToolPriceRepository <-- RentalAgreementService
RentalRequestRepository <-- RentalAgreementService
RentalAgreementRepository <-- RentalAgreementService
RentalAgreementEntity <-- RentalAgreementRepository
RentalRequestEntity <-- RentalRequestRepository
ToolPriceEntity <-- ToolPriceRepository
ToolEntity <-- ToolRepository
IHoliday <|-- FourthJuly
IHoliday <|-- LaborDay
IHoliday <|-- Weekend
IHoliday <|-- ObserverableHoliday
LaborDay <-- ObserverableHoliday
FourthJuly <-- ObserverableHoliday
IHoliday: +isHoliday()
Please see Fetch all tools available for rent
Sequence diagram for fetching all tools available for rent in the system
Sequence diagram for fetching pricing details per tool
Sequence Diagram to checkout out a tool for rent
Expand to view code
{
"openapi": "3.0.1",
"info": {
"title": "ToolRentalApi",
"description": "An API to facilitate the rentai of tools",
"termsOfService": "",
"contact": {
"email": ""
},
"license": {
"name": "",
"url": "http://unlicense.org"
},
"version": "1.0"
},
"servers": [
{
"url": "http://localhost:3000",
"description": "Generated server url"
}
],
"paths": {
"/api/v1/tool/{code}/checkout": {
"post": {
"tags": [
"Tool"
],
"summary": "Create a rental agreement",
"operationId": "postApiV1ToolCodeCheckout",
"parameters": [
{
"name": "code",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RentalRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RentalAgreement"
}
}
}
}
}
}
},
"/api/v1/tool/{code}/inventory": {
"get": {
"tags": [
"Tool"
],
"summary": "Get Inventory details",
"operationId": "getApiV1ToolInventory",
"parameters": [
{
"name": "code",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Inventory"
}
}
}
}
}
}
},
"/api/v1/tool/{code}": {
"get": {
"tags": [
"Tool"
],
"summary": "Get pricing details",
"operationId": "getApiV1ToolCode",
"parameters": [
{
"name": "code",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ToolPricingDetails"
}
}
}
}
}
}
},
"/api/v1/tool/rentalAgreement": {
"get": {
"tags": [
"RentalAgreement"
],
"summary": "Get all rental agreements",
"operationId": "getAllRentalAgreements",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RentalAgreement"
}
}
}
}
}
}
}
},
"/api/v1/tool": {
"get": {
"tags": [
"Tool"
],
"summary": "Get tools available for rent",
"operationId": "getApiV1Tool",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tool"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"RentalRequest": {
"required": [
"checkout_date",
"rental_days_count",
"tool_code"
],
"type": "object",
"properties": {
"tool_code": {
"type": "string",
"readOnly": true
},
"rental_days_count": {
"type": "integer",
"format": "int32"
},
"discount_percent": {
"maximum": 100,
"minimum": 0,
"type": "integer",
"format": "int32"
},
"checkout_date": {
"type": "string"
}
}
},
"RentalAgreement": {
"required": [
"checkout_date",
"tool_code"
],
"type": "object",
"properties": {
"tool_code": {
"type": "string"
},
"tool_type": {
"type": "string"
},
"tool_brand": {
"type": "string"
},
"rental_days": {
"type": "string"
},
"checkout_date": {
"type": "string"
},
"due_date": {
"type": "string"
},
"daily_charge": {
"type": "number"
},
"charge_days": {
"type": "number"
},
"pre_discount_charge": {
"type": "number"
},
"discount_percent": {
"type": "string"
},
"discount_amount": {
"type": "number"
},
"final_charge": {
"type": "number"
}
}
},
"Inventory": {
"required": [
"current_count",
"max_available",
"tool_code"
],
"type": "object",
"properties": {
"tool_code": {
"type": "string"
},
"max_available": {
"type": "number"
},
"current_count": {
"type": "number"
}
}
},
"ToolPricingDetails": {
"required": [
"code",
"daily_charge",
"holiday_charge",
"weekend_charge"
],
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Unique identifier for a tool instance"
},
"daily_charge": {
"type": "number",
"description": "Daily Charge of the tool"
},
"weekend_charge": {
"type": "boolean",
"description": "Is the tool chargable on weekends?"
},
"holiday_charge": {
"type": "boolean",
"description": "Is the tool chargeable on observed holidays?"
}
},
"description": "Rental price details about each tool available for rent"
},
"Tool": {
"required": [
"brand",
"code",
"type"
],
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Unique identifier for a tool instance"
},
"type": {
"type": "string",
"description": "The type of tool."
},
"brand": {
"type": "string"
}
},
"description": "Tools available for rent"
}
}
}
}
Request to fetch all tools available for rent
The below curl command can be used to fetch all available tools
curl --location 'http://localhost:3000/api/v1/tool' \ --header 'Accept: application/json'
Fetch All Available tools Response
The service should respond back with the below json:
[
{
"code": "CHNS",
"type": "Chainsaw",
"brand": "Stihl"
},
{
"code": "LADW",
"type": "Ladder",
"brand": "Werner"
},
{
"code": "JAKD",
"type": "Jackhammer",
"brand": "DeWalt"
},
{
"code": "JAKR",
"type": "Jackhammer",
"brand": "Ridgid"
}
]
Request to fetch pricing details for a tool
The below curl command can be used to fetch all available tools
curl --location --globoff '{{baseUrl}}/api/v1/tool/:CHNS/rentalprice' \ --header 'Accept: application/json'
Response to fetch pricing details for a tool
{
"code": "CHNS",
"daily_charge": "2.99",
"holiday_charge": "true",
"weekend_charge": "false"
}
Request to checkout and create a rental agreement
The below curl command can be used to checkout and create a rental agreement
curl --location --globoff '{{baseUrl}}/api/v1/tool/CHNS/checkout' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --data '{ "checkout_date": "01/12/2024", "rental_days_count": "10", "tool_code": "CHNS", "discount_percent": "10" }'
{
"checkout_date": "01/12/2024",
"rental_days_count": "10",
"tool_code": "CHNS",
"discount_percent": "10"
}
Sample Response to checkout and create a rental agreement
The service should respond back with the below json
{
"tool_code": "CHNS",
"tool_type": "Chainsaw",
"tool_brand": "Stihl",
"rental_days": "10",
"checkout_date": "01/12/2024",
"due_date": "01/22/2024",
"daily_charge": 2.99,
"charge_days": 7,
"pre_discount_charge": 20.93,
"discount_percent": "10%",
"discount_amount": 2.09,
"final_charge": 18.84
}
The service responds with the below error codes for invalid inputs
Error Condition | Response Code and Desciption |
---|---|
Unknown Tool Code | Http 400 Bad Request Invalid tool code |
Invalid or missing checkout date | Http 400 Bad Request Invalid or missing checkout date |
Invalid discount percent | Http 400 Bad Request Invalid discount %. Please enter a value between 0-100 |
- All business errors should be wrapped in an
ValidationException
class - Communicate business exception as HTTP 400 Bad Request
Sample error handling Spring-based RestService . Reference Error Handling for Spring Rest Services
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
@ExceptionHandler(value
= { ValidationException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
}