Appearance
Ticket Endpoints
This document provides a comprehensive technical reference for the RESTful endpoints used to manage tickets within the IT Ticketing Service. These endpoints form the core CRUD (Create, Read, Update, Delete) API for the application.
All endpoints are prefixed with /api/tickets. For detailed information on data structures and error codes, please refer to the Request/Response Formats and Error Handling documentation.
Ticket Status and Priority Values
Status Values
The following status values are valid for tickets:
OPEN- Ticket has been created and is awaiting assignment or actionIN_PROGRESS- Ticket is actively being worked onWAITING_ON_CUSTOMER- Ticket is waiting for customer response or actionRESOLVED- Issue has been resolvedCLOSED- Ticket has been closed
Priority Values
The following priority values are valid for tickets:
LOW- Low priority issueMEDIUM- Medium priority issueHIGH- High priority issueCRITICAL- Critical priority issue requiring immediate attention
Create Ticket
This endpoint creates a new ticket record in the system. Upon successful creation, it also publishes a CREATED event to the message broker.
Endpoint: POST /api/tickets
Implementation Details
The TicketController handles the incoming POST request. It uses the @Valid annotation to trigger bean validation on the request body before passing it to the service layer.
java
// src/main/java/com/slalom/demo/ticketing/controller/TicketController.java
@PostMapping
public ResponseEntity<TicketResponse> createTicket(@Valid @RequestBody TicketRequest request) {
log.info("REST: Creating new ticket");
TicketResponse response = ticketService.createTicket(request);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
The TicketService.createTicket method contains the core business logic. It is wrapped in a @Transactional block to ensure atomicity. The service performs the following actions:
- Maps the
TicketRequestDTO to aTicketJPA entity. - Persists the new entity to the MySQL database using
TicketRepository. - Publishes a
TicketEventwitheventType: "CREATED"to the configured RabbitMQ exchange. - Maps the saved
Ticketentity to aTicketResponseDTO to be returned to the client.
java
// src/main/java/com/slalom/demo/ticketing/service/TicketService.java
@Transactional
public TicketResponse createTicket(TicketRequest request) {
log.info("Creating new ticket: {}", request.getTitle());
Ticket ticket = Ticket.builder()
.title(request.getTitle())
.description(request.getDescription())
.status(request.getStatus())
.priority(request.getPriority())
.requesterEmail(request.getRequesterEmail())
.assignedTo(request.getAssignedTo())
.build();
Ticket savedTicket = ticketRepository.save(ticket);
log.info("Ticket created with ID: {}", savedTicket.getId());
// Publish ticket created event
publishTicketEvent(savedTicket, "CREATED");
return toResponse(savedTicket);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Request Body
The request body must be a JSON object conforming to the TicketRequest structure.
| Field | Type | Description | Constraints |
|---|---|---|---|
title | String | A brief, descriptive title for the ticket. | Not Null, Not Blank |
description | String | A detailed description of the issue or request. | Not Null |
status | Enum | The initial status of the ticket. See Ticket Status and Priority Values for valid values. | Not Null |
priority | Enum | The priority level of the ticket. See Ticket Status and Priority Values for valid values. | Not Null |
requesterEmail | String | The email address of the person who created the ticket. | Not Null, Email format |
assignedTo | String | The email address of the person or group assigned to the ticket. | Can be null |
Example Request
http
POST http://localhost:8080/api/tickets
Content-Type: application/json
{
"title": "Laptop won't start",
"description": "My laptop won't turn on after the latest Windows update. The power light blinks once but nothing appears on screen.",
"status": "OPEN",
"priority": "HIGH",
"requesterEmail": "john.doe@slalom.com",
"assignedTo": null
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Success Response
- Code:
201 Created - Body: A
TicketResponseJSON object representing the newly created ticket, including the server-generatedidand timestamps.
Get All Tickets
This endpoint retrieves a list of tickets. It supports basic filtering by status or requester email.
Endpoint: GET /api/tickets
Implementation Details
The controller method accepts optional status and requesterEmail query parameters. The current implementation logic is sequential: it first checks for a status filter, then for a requesterEmail filter, and finally defaults to fetching all tickets if no filters are provided.
Note: The current filtering logic is mutually exclusive. You cannot filter by both
statusandrequesterEmailin the same request. The first matching parameter will be used.
java
// src/main/java/com/slalom/demo/ticketing/controller/TicketController.java
@GetMapping
public ResponseEntity<List<TicketResponse>> getAllTickets(
@RequestParam(required = false) TicketStatus status,
@RequestParam(required = false) String requesterEmail) {
log.info("REST: Fetching tickets with filters - status: {}, requesterEmail: {}", status, requesterEmail);
List<TicketResponse> tickets;
if (status != null) {
tickets = ticketService.getTicketsByStatus(status);
} else if (requesterEmail != null) {
tickets = ticketService.getTicketsByRequester(requesterEmail);
} else {
tickets = ticketService.getAllTickets();
}
return ResponseEntity.ok(tickets);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Performance Considerations
The current implementation (ticketRepository.findAll()) does not support pagination. For large datasets, this can lead to significant performance degradation and high memory consumption. A future enhancement should introduce pagination using Spring Data's Pageable interface.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | TicketStatus | Filters tickets by their status. |
requesterEmail | String | Filters tickets by the requester's email. |
Example Requests
Get all tickets:
http
GET http://localhost:8080/api/tickets1
Get tickets by status:
http
GET http://localhost:8080/api/tickets?status=OPEN1
Get tickets by requester:
http
GET http://localhost:8080/api/tickets?requesterEmail=john.doe@slalom.com1
Success Response
- Code:
200 OK - Body: A JSON array of
TicketResponseobjects. The array will be empty if no tickets match the criteria.
Get Ticket by ID
This endpoint retrieves a single ticket by its unique identifier.
Endpoint: GET /api/tickets/{id}
Implementation Details
The ticket id is passed as a path variable. The TicketService uses ticketRepository.findById(id) to fetch the record. If the ticket is not found, orElseThrow is called, which results in a RuntimeException.
Developer Note: The default Spring Boot behavior will map this uncaught
RuntimeExceptionto a500 Internal Server Error. A more robust implementation would use a custom, specific exception (e.g.,ResourceNotFoundException) and an@ExceptionHandlerto return a404 Not Foundstatus. See the Error Handling guide for best practices.
java
// src/main/java/com/slalom/demo/ticketing/service/TicketService.java
@Transactional(readOnly = true)
public TicketResponse getTicket(Long id) {
log.info("Fetching ticket with ID: {}", id);
Ticket ticket = ticketRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Ticket not found with id: " + id));
return toResponse(ticket);
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Example Request
http
# Replace {id} with an actual ticket ID, e.g., 1
GET http://localhost:8080/api/tickets/11
2
2
Success Response
- Code:
200 OK - Body: A
TicketResponseJSON object for the requested ticket.
Error Response
- Code:
500 Internal Server Error(if ticket with the given ID does not exist).
Update Ticket
This endpoint performs a full update on an existing ticket. Upon successful update, it also publishes an UPDATED event to the message broker.
Endpoint: PUT /api/tickets/{id}
Implementation Details
This endpoint implements the PUT method, which semantically implies a full replacement of the resource. The client is expected to provide all fields of the TicketRequest object, as any omitted fields may be overwritten with null values.
The TicketService.updateTicket method includes specific business logic:
- It fetches the existing
Ticketentity. If not found, it throws an exception. - It updates all fields on the entity from the
TicketRequestDTO. - Special Logic: It checks if the status is being changed to
RESOLVEDorCLOSED. If so, and if theresolvedAttimestamp is not already set, it sets it to the current time. - It persists the updated entity.
- It publishes a
TicketEventwitheventType: "UPDATED".
java
// src/main/java/com/slalom/demo/ticketing/service/TicketService.java
@Transactional
public TicketResponse updateTicket(Long id, TicketRequest request) {
log.info("Updating ticket with ID: {}", id);
Ticket ticket = ticketRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Ticket not found with id: " + id));
ticket.setTitle(request.getTitle());
ticket.setDescription(request.getDescription());
ticket.setStatus(request.getStatus());
ticket.setPriority(request.getPriority());
ticket.setRequesterEmail(request.getRequesterEmail());
ticket.setAssignedTo(request.getAssignedTo());
// Set resolved timestamp if status changed to RESOLVED or CLOSED
if ((request.getStatus() == TicketStatus.RESOLVED || request.getStatus() == TicketStatus.CLOSED)
&& ticket.getResolvedAt() == null) {
ticket.setResolvedAt(LocalDateTime.now());
}
Ticket updatedTicket = ticketRepository.save(ticket);
log.info("Ticket updated: {}", updatedTicket.getId());
// Publish ticket updated event
publishTicketEvent(updatedTicket, "UPDATED");
return toResponse(updatedTicket);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Request Body
The request body is the same TicketRequest JSON object used for creation. See the Create Ticket section for field details.
Example Request
http
# Replace {id} with an actual ticket ID, e.g., 1
PUT http://localhost:8080/api/tickets/1
Content-Type: application/json
{
"title": "Laptop won't start",
"description": "My laptop won't turn on after the latest Windows update. The power light blinks once but nothing appears on screen. Tried holding power button for 30 seconds.",
"status": "IN_PROGRESS",
"priority": "HIGH",
"requesterEmail": "john.doe@slalom.com",
"assignedTo": "tech.support@slalom.com"
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Success Response
- Code:
200 OK - Body: A
TicketResponseJSON object representing the state of the ticket after the update.
Delete Ticket
This endpoint permanently deletes a ticket from the system.
Endpoint: DELETE /api/tickets/{id}
Implementation Details
The service layer first verifies that a ticket with the given id exists using ticketRepository.existsById(id). This prevents an unnecessary DELETE call and allows for a more specific error if the ticket is not found. If it exists, ticketRepository.deleteById(id) is called.
java
// src/main/java/com/slalom/demo/ticketing/service/TicketService.java
@Transactional
public void deleteTicket(Long id) {
log.info("Deleting ticket with ID: {}", id);
if (!ticketRepository.existsById(id)) {
throw new RuntimeException("Ticket not found with id: " + id);
}
ticketRepository.deleteById(id);
log.info("Ticket deleted: {}", id);
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Cascade Effects
The application code does not define any explicit cascade deletion logic or event publishing for deletions. Maintainers should be aware of any ON DELETE CASCADE constraints configured at the database level, as deleting a ticket could have downstream effects on related tables not managed by this service.
Example Request
http
# Replace {id} with an actual ticket ID, e.g., 1
DELETE http://localhost:8080/api/tickets/11
2
2
Success Response
- Code:
204 No Content - Body: None.
Error Response
- Code:
500 Internal Server Error(if ticket with the given ID does not exist). See the note on error handling in the Get Ticket by ID section.