A Spring Boot REST API for managing product packages with multi-currency support.
- Java 17
- Spring Boot 3.1.0
- Spring Data MongoDB
- MongoDB 6.0 (via Docker)
- Maven for build management
- Java 17+
- Docker & Docker Compose
- Maven (or use the included wrapper)
This starts the API, MongoDB, and Mongo Express (web UI):
docker compose up -dServices will be available at:
- API: http://localhost:8080
- Mongo Express (DB viewer): http://localhost:8081
To rebuild after code changes:
docker compose down && docker compose build && docker compose up -dStart MongoDB separately:
docker compose up -d mongoThen run the Spring Boot app:
mvn spring-boot:runOr using the wrapper:
./mvnw spring-boot:run- Docker internal port: 27017
- Host-mapped port: 27018 (access from host machine)
- Database name:
codingexercise - Connection string (app):
mongodb://mongo:27017/codingexercise - Connection string (local):
mongodb://localhost:27018/codingexercise
Seed data is automatically created when the Spring Boot application starts via CommandLineRunner services:
| Service | Description |
|---|---|
UserSeedService |
Creates demo users (demo-user, tuser) |
ProductSeedService |
Creates sample products with USD prices |
PackageSeedService |
Creates sample packages for tuser |
Default Users:
| Username | Password |
|---|---|
demo-user |
password123 |
tuser |
tpass |
Seed services run on every application startup but only insert data if it doesn't already exist (idempotent).
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/login |
Authenticate user |
Request:
{
"username": "demo-user",
"password": "password123"
}Response:
{
"authenticated": true,
"token": "ZGVtby11c2VyOjE3MDk...",
"username": "demo-user",
"message": "Login successful",
"locale": "en-US"
}| Method | Endpoint | Description |
|---|---|---|
| PUT | /users/{username}/locale |
Update user locale preference |
Request:
{
"locale": "en-GB"
}| Method | Endpoint | Description |
|---|---|---|
| GET | /products |
List all products (with locale conversion) |
| GET | /products?locale=en-GB |
List products in GBP |
| POST | /products?name=X&price=Y¤cy=USD |
Create a product |
Supported Locales & Currencies:
| Locale | Currency |
|---|---|
en-US |
USD |
en-GB |
GBP |
fr-FR |
EUR |
ja-JP |
JPY |
Product Response:
{
"id": "abc123",
"name": "Starter Pack",
"price": 9.99,
"currency": "USD",
"locale": "en-US"
}Note: Prices are stored internally as USD cents. When retrieved, they're converted to the requested locale's currency using live exchange rates from the Frankfurter API.
| Method | Endpoint | Description |
|---|---|---|
| GET | /packages |
List packages (optionally filter by owner) |
| GET | /packages?ownerUsername=demo-user |
List packages for a specific user |
| GET | /packages/all |
List all packages from all users |
| GET | /packages/{id} |
Get a specific package |
| POST | /packages |
Create a new package |
| PUT | /packages/{id} |
Update a package |
| DELETE | /packages/{id} |
Delete a package |
Create/Update Package Request:
{
"name": "My Bundle",
"description": "A collection of items",
"productQuantities": {
"product-id-1": 2,
"product-id-2": 1
},
"ownerUsername": "demo-user"
}src/main/java/com/example/codingexercise/
├── CodingExerciseApplication.java # Application entry point
├── config/ # Configuration classes
│ └── GatewayConfig.java # RestTemplate bean
├── controller/ # REST controllers
│ ├── AuthController.java # Login endpoint
│ ├── PackageController.java # Package CRUD
│ ├── ProductController.java # Product operations
│ ├── UserController.java # User settings
│ └── dto/ # Request/Response DTOs
├── exception/ # Custom exceptions
├── gateway/ # External API clients
│ └── ProductServiceGateway.java
├── model/ # Domain entities
│ ├── Product.java
│ ├── ProductPackage.java
│ └── User.java
├── repository/ # Data access layer
│ ├── PackageRepository.java
│ ├── ProductRepository.java
│ └── UserRepository.java
└── service/ # Business logic
└── CurrencyRateService.java # Exchange rate conversion
Run all tests:
mvn testRun a specific test class:
mvn test -Dtest=PackageControllerTestsTest Coverage:
AuthControllerTests- Login success/failure scenariosPackageControllerTests- Package CRUD operationsProductControllerTests- Product listing with localesUserControllerTests- Locale updateCurrencyRateServiceTests- Currency conversion logic
The API uses the Frankfurter API for live exchange rates:
- Products are stored with
usdPricein cents (e.g.,$19.99=1999) - On retrieval, prices are converted to the requested locale's currency
- JPY prices have 0 decimal places; others have 2
| Variable | Default | Description |
|---|---|---|
SPRING_DATA_MONGODB_URI |
mongodb://localhost:27017/codingexercise |
MongoDB connection |
| Service | Port | Description |
|---|---|---|
app |
8080 | Spring Boot API |
mongo |
27018 (host) / 27017 (internal) | MongoDB database |
mongo-express |
8081 | MongoDB web UI |
MongoDB connection issues:
# Check if MongoDB is running
docker compose ps
# View MongoDB logs
docker compose logs mongoReset everything:
docker compose down -v
docker compose up -dPort conflicts:
- API (8080): Change in
docker-compose.ymlunderapp.ports - MongoDB (27018): Change in
docker-compose.ymlundermongo.ports - Mongo Express (8081): Change in
docker-compose.ymlundermongo-express.ports