Appearance
Tickets API
This document provides detailed technical documentation for the Tickets API, which is responsible for managing support tickets within the system. It is intended for developers who will be maintaining and extending this service.
The API is a standard RESTful service built with Spring Boot. For a general overview of the project's architecture and technology stack, please refer to the main technical stack documentation. For a quick guide on running the application, see the Quick Start Guide.
Create Ticket
This endpoint creates a new support ticket.
POST /api/tickets
The creation process is transactional and triggers an asynchronous event upon successful persistence.
Implementation Details
The TicketController handles the incoming request, delegating the core logic to the TicketService.
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 performs the following actions:
- Builds a
Ticketentity from the incomingTicketRequestDTO. - Persists the new
Ticketentity to the MySQL database using Spring Data JPA'sticketRepository.save(). - Publishes a
TicketEventwitheventType: "CREATED"to a RabbitMQ message queue. This allows other services to react to ticket creation asynchronously. - Maps the persisted
Ticketentity to aTicketResponseDTO and returns it.
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())
// ... other fields
.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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Request Body
The request body must be a JSON object conforming to the TicketRequest structure. The @Valid annotation triggers server-side validation based on constraints defined in the DTO.
For detailed information on the fields, validation rules, and data types, refer to the Data Models documentation.
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.",
"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
Response
- Success:
201 Createdwith theTicketResponseobject in the body, including the server-generatedidand timestamps. - Bad Request:
400 Bad Requestif the request body fails validation. See the Error Handling Guide for the error response format.
Get All Tickets
This endpoint retrieves a list of tickets. It supports optional filtering by status or requester email.
GET /api/tickets
Implementation Details
The controller method accepts two optional RequestParam parameters: status and requesterEmail.
Important: The current implementation in TicketController can only handle one filter parameter at a time. If both status and requesterEmail are provided, the status filter will take precedence and requesterEmail will be ignored. This is a known limitation that could be improved by using a more advanced query mechanism like JPA Specifications or Querydsl.
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
The TicketService contains dedicated, read-only transactional methods that call the corresponding TicketRepository finders.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | String | (Optional) Filters tickets by their status. Must match one of the TicketStatus enum values (e.g., OPEN, IN_PROGRESS). |
requesterEmail | String | (Optional) Filters tickets by the requester's email address. |
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
Response
- Success:
200 OKwith a JSON array ofTicketResponseobjects. 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.
GET /api/tickets/{id}
Implementation Details
The id is extracted from the URL path using @PathVariable. The TicketService uses ticketRepository.findById(id) to fetch the entity.
If no ticket is found for the given id, the service throws a RuntimeException. This exception is expected to be caught by a global exception handler, which translates it into a 404 Not Found HTTP response.
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
Response
- Success:
200 OKwith the correspondingTicketResponseobject in the body. - Not Found:
404 Not Foundif no ticket with the specifiedidexists. See the Error Handling Guide.
Example Request
http
GET http://localhost:8080/api/tickets/11
Update Ticket
This endpoint updates an existing ticket. It performs a full replacement of all mutable fields provided in the request body.
PUT /api/tickets/{id}
Implementation Details
The update process is transactional and involves several key steps within the TicketService:
- Fetch the existing
Ticketentity byid. If not found, aRuntimeExceptionis thrown. - Update the entity's properties with values from the
TicketRequestDTO. - Business Logic: A specific rule is applied to the
resolvedAttimestamp. It is set to the current time only if the status is being changed toRESOLVEDorCLOSEDand theresolvedAtfield is currentlynull. This prevents the resolved timestamp from being overwritten on subsequent updates. - The updated entity is saved back to the database.
- An asynchronous
TicketEventwitheventType: "UPDATED"is published to RabbitMQ. - The updated entity is mapped to a
TicketResponseand returned.
java
// src/main/java/com/slalom/demo/ticketing/service/TicketService.java
@Transactional
public TicketResponse updateTicket(Long id, TicketRequest request) {
// ... find ticket or throw exception ...
ticket.setTitle(request.getTitle());
// ... update other fields ...
// 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);
// 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Request Body
The request body must be a valid TicketRequest JSON object. For details, see the Data Models documentation.
Example Request (Updating status):
http
PUT http://localhost:8080/api/tickets/1
Content-Type: application/json
{
"title": "Laptop won't start",
"description": "My laptop won't turn on... 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
2
3
4
5
6
7
8
9
10
11
Response
- Success:
200 OKwith the updatedTicketResponseobject in the body. - Not Found:
404 Not Foundif theiddoes not exist. - Bad Request:
400 Bad Requestif the request body fails validation.
Delete Ticket
This endpoint permanently deletes a ticket from the system.
DELETE /api/tickets/{id}
Implementation Details
The TicketService first verifies the ticket's existence using ticketRepository.existsById(id). This is done to provide a consistent "not found" error, as deleteById does not throw an exception if the entity doesn't exist. If the ticket exists, ticketRepository.deleteById(id) is called within the transaction.
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
The controller method returns a 204 No Content status, which is standard practice for successful DELETE operations that do not return a body.
java
// src/main/java/com/slalom/demo/ticketing/controller/TicketController.java
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteTicket(@PathVariable Long id) {
log.info("REST: Deleting ticket with ID: {}", id);
ticketService.deleteTicket(id);
return ResponseEntity.noContent().build();
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Response
- Success:
204 No Contentwith an empty body. - Not Found:
404 Not Foundif no ticket with the specifiedidexists. See the Error Handling Guide.
Example Request
http
DELETE http://localhost:8080/api/tickets/11