Skip to content

Commit 2aba554

Browse files
authored
Merge pull request #311 from entur/minor-refactoring-open-api-specs
Add comprehensive OpenAPI documentation for all API endpoints
2 parents 7e79387 + 6b22765 commit 2aba554

File tree

11 files changed

+1244
-45
lines changed

11 files changed

+1244
-45
lines changed

src/main/java/no/entur/mummu/config/SwaggerConfiguration.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import io.swagger.v3.core.jackson.ModelResolver;
88
import io.swagger.v3.core.util.Json;
99
import io.swagger.v3.oas.models.OpenAPI;
10+
import io.swagger.v3.oas.models.info.Contact;
11+
import io.swagger.v3.oas.models.info.Info;
1012
import io.swagger.v3.oas.models.servers.Server;
11-
import org.springdoc.core.customizers.SpringDocCustomizers;
13+
import io.swagger.v3.oas.models.tags.Tag;
1214
import org.springdoc.core.properties.SpringDocConfigProperties;
1315
import org.springdoc.core.providers.ObjectMapperProvider;
1416
import org.springframework.beans.factory.annotation.Value;
@@ -46,7 +48,38 @@ public SwaggerConfiguration() {
4648
public OpenAPI customOpenAPI() {
4749
Server server = new Server();
4850
server.setUrl(hostUrl);
49-
return new OpenAPI().servers(List.of(server));
51+
return new OpenAPI()
52+
.servers(List.of(server))
53+
.info(new Info()
54+
.title("Stop Place Register")
55+
.version("1.0.0")
56+
.description("The Stop Place Register provides access to public transportation infrastructure data across Norway, including stop places, quays, parkings, and related NeTEx entities. This API enables developers to query stop place information with details on location, accessibility features, transport modes, fare zones, and hierarchical relationships. Ideal for journey planning applications, transportation analysis, mobility services, and public transit integrations.")
57+
.contact(new Contact()
58+
.name("Entur API Support")
59+
.url("https://developer.entur.org")))
60+
.tags(List.of(
61+
new Tag()
62+
.name("Stop Places")
63+
.description("Query and retrieve stop place information including stations, terminals, bus stops, and ferry ports. Includes filtering by transport mode, location, and other attributes."),
64+
new Tag()
65+
.name("Quays")
66+
.description("Access detailed information about quays (platforms, boarding positions) and their relationship to stop places. Query by ID or retrieve version history."),
67+
new Tag()
68+
.name("Scheduled Stop Points")
69+
.description("Retrieve scheduled stop points used in route planning and timetables, and their mappings to physical stop places."),
70+
new Tag()
71+
.name("Fare Zones")
72+
.description("Access fare zone definitions and boundaries used for ticket pricing calculations. Query by authority or specific zones."),
73+
new Tag()
74+
.name("Parking")
75+
.description("Find parking facilities associated with stop places, including capacity, pricing, and accessibility information."),
76+
new Tag()
77+
.name("Geographic Areas")
78+
.description("Query topographic places (municipalities, counties, countries) and tariff zones for geographic and administrative boundaries."),
79+
new Tag()
80+
.name("Groupings")
81+
.description("Access logical groupings of stop places and fare zones for organizational and operational purposes.")
82+
));
5083
}
5184

5285
@Bean
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package no.entur.mummu.resources;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import java.util.Map;
6+
7+
@Schema(description = "Error response returned when a request fails")
8+
@JsonInclude(JsonInclude.Include.NON_NULL)
9+
public class ErrorResponse {
10+
11+
@Schema(description = "Machine-readable error code", example = "RESOURCE_NOT_FOUND")
12+
private String errorCode;
13+
14+
@Schema(description = "Human-readable error message", example = "Resource not found")
15+
private String message;
16+
17+
@Schema(description = "Additional error context")
18+
private Map<String, Object> details;
19+
20+
public ErrorResponse() {}
21+
22+
public ErrorResponse(String errorCode, String message) {
23+
this.errorCode = errorCode;
24+
this.message = message;
25+
}
26+
27+
public ErrorResponse(String errorCode, String message, Map<String, Object> details) {
28+
this.errorCode = errorCode;
29+
this.message = message;
30+
this.details = details;
31+
}
32+
33+
// Getters and setters
34+
public String getErrorCode() {
35+
return errorCode;
36+
}
37+
38+
public void setErrorCode(String errorCode) {
39+
this.errorCode = errorCode;
40+
}
41+
42+
public String getMessage() {
43+
return message;
44+
}
45+
46+
public void setMessage(String message) {
47+
this.message = message;
48+
}
49+
50+
public Map<String, Object> getDetails() {
51+
return details;
52+
}
53+
54+
public void setDetails(Map<String, Object> details) {
55+
this.details = details;
56+
}
57+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package no.entur.mummu.resources;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.validation.FieldError;
8+
import org.springframework.web.bind.MethodArgumentNotValidException;
9+
import org.springframework.web.bind.annotation.ExceptionHandler;
10+
import org.springframework.web.bind.annotation.RestControllerAdvice;
11+
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
12+
13+
import java.util.Map;
14+
15+
@RestControllerAdvice
16+
public class GlobalExceptionHandler {
17+
18+
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
19+
private static final String INVALID_PARAMETER = "INVALID_PARAMETER";
20+
21+
@ExceptionHandler(NotFoundException.class)
22+
public ResponseEntity<ErrorResponse> handleNotFoundException(NotFoundException ex) {
23+
ErrorResponse error = new ErrorResponse(
24+
"RESOURCE_NOT_FOUND",
25+
ex.getMessage()
26+
);
27+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
28+
}
29+
30+
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
31+
public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
32+
ErrorResponse error = new ErrorResponse(
33+
INVALID_PARAMETER,
34+
String.format("Invalid value '%s' for parameter '%s'", ex.getValue(), ex.getName()),
35+
Map.of("parameter", ex.getName(), "providedValue", String.valueOf(ex.getValue()))
36+
);
37+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
38+
}
39+
40+
@ExceptionHandler(MethodArgumentNotValidException.class)
41+
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
42+
FieldError fieldError = ex.getBindingResult().getFieldError();
43+
if (fieldError != null) {
44+
String parameterName = fieldError.getField();
45+
Object rejectedValue = fieldError.getRejectedValue();
46+
ErrorResponse error = new ErrorResponse(
47+
INVALID_PARAMETER,
48+
String.format("Invalid value '%s' for parameter '%s'", rejectedValue, parameterName),
49+
Map.of("parameter", parameterName, "providedValue", String.valueOf(rejectedValue))
50+
);
51+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
52+
}
53+
ErrorResponse error = new ErrorResponse(INVALID_PARAMETER, "Validation failed");
54+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
55+
}
56+
57+
@ExceptionHandler(IllegalArgumentException.class)
58+
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException ex) {
59+
ErrorResponse error = new ErrorResponse(INVALID_PARAMETER, ex.getMessage());
60+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
61+
}
62+
63+
@ExceptionHandler(Exception.class)
64+
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
65+
logger.error("Unexpected error", ex);
66+
ErrorResponse error = new ErrorResponse(
67+
"INTERNAL_ERROR",
68+
"An unexpected error occurred while processing your request"
69+
);
70+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
71+
}
72+
}

src/main/java/no/entur/mummu/resources/NotFoundException.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,14 @@
33
import org.springframework.http.HttpStatus;
44
import org.springframework.web.bind.annotation.ResponseStatus;
55

6-
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "entity not found")
7-
public class NotFoundException extends RuntimeException {}
6+
@ResponseStatus(code = HttpStatus.NOT_FOUND)
7+
public class NotFoundException extends RuntimeException {
8+
9+
public NotFoundException() {
10+
super("The requested resource was not found");
11+
}
12+
13+
public NotFoundException(String message) {
14+
super(message);
15+
}
16+
}

0 commit comments

Comments
 (0)