spend-the-night is a Java Spring Boot REST API designed for managing and discovering accommodation offers. It allows users to create, update, search, and delete place offers with secure access via JWT and OAuth2.
- Deployment
- Request Authorization
- OpenAPI with Swagger UI
- Datasource
- Request Validation
- Testing
- Actuator
Define the following properties in the main application.properties file or use environment variables:
Property Key | Environment Variable |
---|---|
custom.auth.server.domain |
STN_AUTH_SERVER_DOMAIN |
custom.auth.server.realm |
STN_AUTH_SERVER_REALM |
custom.auth.server.scope.publish |
STN_AUTH_SERVER_SCOPE_PUBLISH |
custom.auth.server.scope.view |
STN_AUTH_SERVER_SCOPE_VIEW |
Run the application:
$ ./gradlew bootRun
or
$ ./gradlew clean bootJar
$ docker build -t spend-the-night-app:development .
$ docker run -p 8080:8080 --security-opt seccomp=unconfined -e STN_AUTH_SERVER_DOMAIN="http://{DOMAIN}:{PORT}" spend-the-night-app:development
Note: For tests to run successfully, the client secrets and tokens also have to be included in the test application.properties file.
The API uses JWT tokens for authorization and Keycloak as the OAuth2 resource server. Only place offer management and search endpoints are available, user management is not provided.
- User Data: Example data is taken from https://api.randomuser.me and can be found in UserData.java.
- User Identification: API requests use userName, while the User ID matches the Keycloak client ID.
- Scopes: Access control is defined through "publish" for managing offers and "view" for searching.
Corresponding Keycloak clients for managing permissions are pre-configured:
Show access token in Keycloak or:
curl -H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=xxx" \
-d "client_secret=xxx" \
-d "grant_type=client_credentials" \
-X POST {AUTH_SERVER_DOMAIN}/auth/realms/{AUTH_SERVER_REALM}/protocol/openid-connect/token
Authorize API request with header "Authorization: Bearer {access_token}"
Here is an example JSON request body:
{
"userName": "Tialda Bons",
"accommodation": {
"title": "Cozy Apartment in City Center",
"description": "A comfortable one-bedroom apartment with a beautiful view of the city.",
"address": {
"street": {
"name": "Schreinerstr.",
"number": "1"
},
"city": "Berlin",
"postalCode": "10247",
"country": "Deutschland"
},
"pictures": [
"https://example.com/1.jpg",
"https://example.com/2.jpg"
],
"pricePerNight": 50,
"currency": "EUR",
"availableDates": [
"2025-01-17",
"2025-01-18",
"2025-01-19"
],
"amenities": [
"WIFI",
"BBQ_GRILL"
],
"hostPreferences": {
"maxGuests": 2,
"checkInTime": "15:00",
"checkOutTime": "11:00",
"smokingAllowed": "NO",
"petsAllowed": "YES",
"languagesSpoken": [
"EN",
"DE"
]
}
}
}
with a POST request for creating a new accommodation offer:
Access the OpenAPI documentation and test endpoints via Swagger UI:
- Swagger UI: http://localhost:8080/api/swagger-ui.html
- API Docs: http://localhost:8080/api/v3/api-docs
To authorize API requests in Swagger, click "Authorize" in the top-right corner and enter the Bearer token without the word "Bearer".
Example POST request:
The application runs with a H2 in-memory database. The database web console can be accessed at http://localhost:8080/h2-console.
H2 web console configuration with default values:
Saved Settings: Generic H2 (Embedded)
Setting Name: Generic H2 (Embedded)
Driver Class: org.h2.Driver
JDBC URL: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
User Name: sa
Password: leave blank
Tables showing sample data:
Class diagram for the request body:
The following table includes validation requirements for the request body attributes:
class | attribute | type | format | nullable | default | unique | generated | restriction |
---|---|---|---|---|---|---|---|---|
PlaceOffer | userName | String | false | false | false | not blank | ||
accommodation | Accommodation | false | false | false | ||||
Accommodation | id | UUID | false | true | true | |||
title | String | false | true for title and User id combination | false | not blank | |||
description | String | true | null | false | false | |||
address | Address | false | false | false | must be valid | |||
pictures | Set<URL> | true | null | false | false | all items != null | ||
pricePerNight | Integer | false | false | false | >= 0 | |||
currency | enum | false | EUR | false | false | |||
priceInEuro | Double | false | false | true | ||||
availableDates | Set<LocalDate> | ["yyyy-MM-dd"] | false | false | false | |||
amenities | enum Set | true | null | false | false | at least 1 item, all items != null |
||
hostPreferences | HostPreferences | false | false | false | ||||
Address | street | Street | false | false | false | |||
city | String | false | false | false | not blank | |||
postalCode | String | false | false | false | not blank | |||
country | String | false | false | false | not blank | |||
latitude | Double | false | false | true | ||||
longitude | Double | false | false | true | ||||
timezone | ZoneId | false | false | true | ||||
Street | name | String | false | false | false | not blank | ||
number | String | false | false | false | not blank | |||
HostPreferences | maxGuests | int | false | false | false | > 0 | ||
checkInTime | LocalTime | "HH:mm" | false | false | false | |||
checkOutTime | LocalTime | "HH:mm" | false | false | false | |||
smokingAllowed | enum | false | NO | false | false | |||
petsAllowed | enum | false | NO | false | false | |||
languagesSpoken | enum Set | false | false | false | at least 1 item, all items != null |
Tests are written in Groovy using the Spock framework.
Results are available at: {ROOT_DIR}/spend-the-night/build/reports/tests/test/index.html
Monitoring the API with Spring Actuator endpoints like:
- Main Actuator: http://localhost:8080/api/actuator
- Health Check: http://localhost:8080/api/actuator/health
- Bean Information: http://localhost:8080/api/actuator/beans
- Prometheus Metrics: http://localhost:8080/api/actuator/prometheus
- App Metrics: http://localhost:8080/api/actuator/metrics