A RESTful API for managing user accounts built with Java and Spring Boot following Clean Architecture principles.
- POST /api/users: Create new user accounts with validation
- GET /api/users: Retrieve all users from the system
- GET /api/users/{id}: Get specific user by ID
- PATCH /api/users/{id}: Update existing user information
- DELETE /api/users/{id}: Delete user accounts
- 🔐 Basic Authentication: All API endpoints secured with HTTP Basic Authentication (
admin:admin
) - Clean Architecture: Separation of concerns across presentation, application, domain, and infrastructure layers
- Comprehensive Validation: Field validation, password requirements, login uniqueness
- Environment Security: Secure credential management with
.env
files - Full Test Coverage: Unit and integration tests with JaCoCo reporting
- MySQL Integration: JPA/Hibernate with automatic schema creation
- CIS Integration: Compatible with existing CLI system
Category | Technology | Version | Purpose |
---|---|---|---|
Framework | Spring Boot | 3.5.5 | Main application framework |
Language | Java | 17+ | Programming language |
Database | MySQL | 8.0+ | Data persistence |
ORM | JPA/Hibernate | 6.3.1 | Database mapping |
Security | Spring Security | 6.3.4 | HTTP Basic Authentication |
Build Tool | Maven | 3.8+ | Dependency management |
Testing | JUnit 5 + Mockito | - | Unit testing framework |
Coverage | JaCoCo | 0.8.13 | Code coverage reporting |
Environment | dotenv-java | 3.2.0 | Environment variable loading |
- Java 17 or higher
- Maven 3.8+
- MySQL 8.0+
All API endpoints require HTTP Basic Authentication with the following credentials:
- Username:
admin
- Password:
admin
- Authorization Header:
Authorization: Basic YWRtaW46YWRtaW4=
# Encode admin:admin using base64: YWRtaW46YWRtaW4=
curl --location 'http://localhost:8080/api/users' \
--header 'Authorization: Basic YWRtaW46YWRtaW4='
- Select Authorization tab
- Choose Basic Auth from the dropdown
- Enter username:
admin
- Enter password:
admin
- Postman will automatically generate the Authorization header
The credentials admin:admin
are encoded as Base64: YWRtaW46YWRtaW4=
The API implements HTTP Basic Authentication using Spring Security with:
- In-memory user store with admin:admin credentials
- BCrypt password encoding for secure password hashing
- Stateless session management suitable for REST APIs
- 401 Unauthorized response for missing or invalid credentials
- Protected endpoints under
/api/users/**
/swagger-ui/**
- Swagger UI documentation/v3/api-docs/**
- OpenAPI documentation/h2-console/**
- H2 database console (development only)
- Clone the repository
- Install dependencies:
mvn clean install
The application uses environment variables for secure configuration management:
Variable | Description | Default | Required |
---|---|---|---|
DB_HOST |
Database host | localhost | No |
DB_PORT |
Database port | 3307 | No |
DB_NAME |
Database name | sd3 | No |
DB_USERNAME |
Database username | root | No |
DB_PASSWORD |
Database password | root | No |
-
Copy the example environment file:
cp .env.example .env
-
Edit
.env
with your database credentials:# Database Configuration for sd3db (CIS compatible) DB_HOST=localhost DB_PORT=3307 DB_NAME=sd3 DB_USERNAME=root DB_PASSWORD=root
-
The application will automatically load these variables on startup
The application automatically loads .env
variables during startup using the dotenv-java
library. Variables are loaded in the main()
method before Spring Boot initialization, ensuring proper database connectivity.
Startup Process:
- Load
.env
file from project root - Set system properties for Spring Boot
- Initialize Spring application context
- Connect to MySQL database
- ✅ DO: Use
.env
for local development - ✅ DO: Use environment variables in production
- ✅ DO: Share
.env.example
with the team - ❌ DON'T: Commit
.env
to version control - ❌ DON'T: Hardcode credentials in source code
If you encounter database connection errors:
- Verify
.env
file exists in project root directory - Check database credentials are correct in
.env
- Ensure MySQL service is running on your system
- Look for startup logs showing "🔧 Loading .env variables..."
Common Error Messages:
Access denied for user 'root'@'localhost' (using password: NO)
→ Missing or incorrect.env
fileUnknown database 'sd3'
→ Database not created in MySQL
This Users API is designed to be compatible with the existing CIS (Command Line Interface) system.
The API connects to the same sd3db
database used by the CIS CLI tool:
- Database Schema:
sd3
- Table:
users
- Port:
3307
(Docker MySQL instance) - Default Password:
root
CREATE TABLE `sd3`.`users` (
`id` VARCHAR(36) NOT NULL,
`name` VARCHAR(200) NOT NULL,
`login` VARCHAR(20) NOT NULL,
`password` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE
);
- Pull MySQL Docker image:
docker pull mysql:latest
- Run sd3db container:
docker run -d --name sd3db -e MYSQL_ROOT_PASSWORD=root -p 3307:3306 mysql
- Create database schema:
# Connect to MySQL
docker exec -it sd3db mysql -uroot -proot
# Create schema and table
CREATE DATABASE sd3;
USE sd3;
CREATE TABLE `users` (
`id` VARCHAR(36) NOT NULL,
`name` VARCHAR(200) NOT NULL,
`login` VARCHAR(20) NOT NULL,
`password` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE
);
- Update your .env file:
DB_HOST=localhost
DB_PORT=3307
DB_NAME=sd3
DB_USERNAME=root
DB_PASSWORD=root
The API maintains full compatibility with the CIS CLI tool:
- Shared Data: Both systems read/write to the same
sd3.users
table - Real-time Sync: Changes made via API are immediately visible in CLI
- Bidirectional: Changes made via CLI are immediately visible in API
- Data Integrity: Both systems respect the same database constraints
-
For CIS Integration (Recommended): Follow the CIS Integration section above
-
For Standalone Development:
CREATE DATABASE sd3;
-
Ensure your
.env
file is properly configured (see Environment Configuration section above) -
The application will automatically create tables on startup using JPA
mvn spring-boot:run
mvn clean package
java -jar target/users-api-1.0.0.jar
The application will start on http://localhost:8080
The application follows Clean Architecture principles with clear separation of concerns:
src/main/java/jalau/usersapi/
├── core/ # Domain & Application Layer
│ ├── application/ # Use cases and business rules
│ │ └── services/ # Application services
│ └── domain/ # Enterprise business rules
│ ├── entities/ # Domain entities
│ ├── exceptions/ # Domain exceptions
│ └── ports/ # Interface definitions
├── infrastructure/ # Infrastructure Layer
│ └── mysql/ # Database implementations
│ ├── entities/ # JPA entities
│ ├── mappers/ # Data mapping
│ └── repositories/ # Repository implementations
└── presentation/ # Presentation Layer
├── controllers/ # REST controllers
├── dtos/ # Data transfer objects
├── mappers/ # DTO mapping
└── validators/ # Request validation
- Dependency Inversion: Infrastructure depends on domain, not vice versa
- Ports & Adapters: Clear interfaces between layers
- Single Responsibility: Each class has one reason to change
- Domain Isolation: Business logic independent of frameworks
Authentication Required: Basic Auth (admin:admin
)
Create a new user account.
Request:
POST http://localhost:8080/api/users
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
{
"name": "John Doe",
"login": "johndoe",
"password": "SecurePass123!"
}
Responses:
{
"id": "3ff138e6-bdbb-45cc-922b-eda0d428b3f1",
"name": "John Doe",
"login": "johndoe"
}
{
"timestamp": "2025-09-14T19:35:00.123Z",
"status": 400,
"error": "Bad Request",
"message": "Validation failed",
"path": "/api/users"
}
{
"timestamp": "2025-09-14T19:35:00.123Z",
"status": 409,
"error": "Conflict",
"message": "Login already exists",
"path": "/api/users"
}
{
"timestamp": "2025-09-21T19:35:00.123Z",
"status": 401,
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/users"
}
Authentication Requirements:
- Username:
admin
- Password:
admin
- Header:
Authorization: Basic YWRtaW46YWRtaW4=
Authentication Required: Basic Auth (admin:admin
)
Retrieve all users from the system.
curl -X GET http://localhost:8080/api/users \
-H "Accept: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
- When the user exists:
[
{
"id": "3ff138e6-bdbb-45cc-922b-eda0d428b3f1",
"name": "John Doe",
"login": "johndoe"
}
]
- When there isn't users:
[]
Validation Rules:
name
: Required, 1-100 characterslogin
: Required, 3-50 characters, uniquepassword
: Required, 8-100 characters
Authentication Required: Basic Auth (admin:admin
)
- Description: Searches for a specific user by ID
- Parameter: User ID
- Responses:
- 200 OK: User found (UserResponseDto)
- 401 Unauthorized: Missing or invalid authentication
- 404 Not Found: User does not exist
- Response example:
{
"id": "aab5d5fd-70c1-11e5-a4fb-b026b977eb28",
"name": "John Smith",
"login": "john"
}
Authentication Required: Basic Auth (admin:admin
)
- Description: Updates an existing user
- Body: UserUpdateDto (optional fields)
- Responses:
- 200 OK: User successfully updated (UserResponseDto)
- 400 Bad Request: Invalid data (error array)
- 401 Unauthorized: Missing or invalid authentication
- 404 Not Found: User does not exist
- Request example:
{
"name": "New Name",
"login": "newlogin",
"password": "newpassword"
}
- Update User:
curl -X PATCH "http://localhost:8080/api/users/123" \
-H "Content-Type: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4=" \
-d '{"name": "Novo Nome", "login": "novologin"}'
- Verify the update
curl -X GET "http://localhost:8080/api/users" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
Authentication Required: Basic Auth (admin:admin
)
- Description: Deletes an existing user by ID
- Parameter: User ID (String)
- Responses:
- 200 OK: User successfully deleted (empty body)
- 401 Unauthorized: Missing or invalid authentication
- 404 Not Found: User does not exist (empty body)
# Delete a user (replace with actual user ID)
curl -X DELETE "http://localhost:8080/api/users/aab5d5fd-70c1-11e5-a4fb-b026b977eb28" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# Expected responses:
# Success (200 OK): Empty response body
# User not found (404 Not Found): Empty response body
# Test with non-existent ID
curl -X DELETE "http://localhost:8080/api/users/non-existent-id" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# Delete a user (replace with actual user ID)
$headers = @{
'Authorization' = 'Basic YWRtaW46YWRtaW4='
}
Invoke-RestMethod -Uri "http://localhost:8080/api/users/aab5d5fd-70c1-11e5-a4fb-b026b977eb28" -Method Delete -Headers $headers
# Test with non-existent ID
Invoke-RestMethod -Uri "http://localhost:8080/api/users/non-existent-id" -Method Delete -Headers $headers
# 1. Create a user first
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4=" \
-d '{
"name": "Test User",
"login": "testuser",
"password": "password123"
}'
# 2. Note the returned ID from step 1, then list users to verify
curl -X GET http://localhost:8080/api/users \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# 3. Delete the user using the ID from step 1
curl -X DELETE "http://localhost:8080/api/users/[USER_ID_FROM_STEP_1]" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# 4. Verify deletion by listing users again
curl -X GET http://localhost:8080/api/users \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# 5. Try to delete the same user again (should return 404)
curl -X DELETE "http://localhost:8080/api/users/[USER_ID_FROM_STEP_1]" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
- Create user via API:
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4=" \
-d '{"name": "API User", "login": "apiuser", "password": "test123"}'
- Verify via CIS CLI:
# Using CIS CLI tool
java -jar cis.jar -config=sd3.xml -read
- Delete user via API:
curl -X DELETE "http://localhost:8080/api/users/[USER_ID]" \
-H "Authorization: Basic YWRtaW46YWRtaW4="
- Verify deletion via CIS CLI:
# User should no longer appear in CLI results
java -jar cis.jar -config=sd3.xml -read
This ensures seamless coexistence between the REST API and the legacy CLI system during the transition period.
The project follows Clean Architecture principles, and the test structure mirrors the main application structure:
src/test/java/jalau/usersapi/
├── UsersApiApplicationTests.java # Spring Boot integration test
├── core/
│ └── application/
│ ├── UserCommandServiceTest.java # Service layer tests
│ └── UserCommandServiceLoginValidationTest.java # Login validation tests
│ └── UserQueryServiceTest.java
├── infrastructure/
│ ├── mysql/
│ │ ├── UserRepositoryExistsByLoginTest.java # Repository login check tests
│ │ └── mappers/
│ │ ├── DbUserToUserMapperTest.java # Database to domain mapping tests
│ │ └── UserToDbUserMapperTest.java # Domain to database mapping tests
│ │ └── UserRepositoryGetUsersTest.java
└── presentation/
├── controllers/
│ ├── HealthControllerTest.java # Health endpoint tests
│ ├── UserCommandControllerTest.java # User creation controller tests
│ └── UserCommandControllerConflictTest.java # Login conflict controller tests
│ └── UserQueryControllerTest.java
├── mappers/
│ ├── UserCreateDtoToUserMapperTest.java # Request DTO mapping tests
│ └── UserToUserResponseDtoMapperTest.java # Response DTO mapping tests
└── validators/
└── UserCreateDtoValidatorTest.java # Request validation tests
- Presentation Layer: Controllers, DTOs, validators, mappers
- Application Layer: Services, business logic (including DELETE operations)
- Infrastructure Layer: Repositories, database mappers
- Isolated testing: Uses Mockito for mocking dependencies
- Spring Boot Context: Application startup and configuration
- Database Integration: JPA repositories with real database connections
The BasicAuthenticationIntegrationTest
includes comprehensive authentication tests:
- Valid credentials (
admin:admin
): All endpoints return 200/201 with proper authentication - Missing Authorization header: All endpoints return 401 Unauthorized
- Invalid credentials: All endpoints return 401 Unauthorized
- 12 test methods covering all CRUD operations with different authentication scenarios
The UserCommandServiceTest
includes comprehensive delete operation tests:
deleteUser_UserExists_DeletesSuccessfully
: Successful deletion scenariodeleteUser_UserNotFound_ThrowsUserNotFoundException
: User not found scenariodeleteUser_NullId_ThrowsUserNotFoundException
: Null ID handling
The following test classes validate all user endpoints:
BasicAuthenticationIntegrationTest
: HTTP authentication testing (12 tests)UserCommandControllerTest
: HTTP request/response testing (POST)UserCommandControllerConflictTest
: Login duplication scenarios (409 Conflict)UserCreateDtoValidatorTest
: Request validation (11 validation tests)UserCommandServiceTest
: Business logic validation (including DELETE)UserCommandServiceLoginValidationTest
: Login existence checkingUserRepositoryExistsByLoginTest
: Database login verification
# Run all tests (including Basic Authentication integration tests)
mvn test
# Run only Basic Authentication integration tests
mvn test -Dtest=BasicAuthenticationIntegrationTest
# Run specific test class
mvn test -Dtest=HealthControllerTest
# Run specific test method
mvn test -Dtest=HealthControllerTest#healthEndpoint_ShouldReturn200OK
# Run all User tests
mvn test -Dtest=User*
# Run specific User test classes
mvn test -Dtest=UserCommandControllerTest
mvn test -Dtest=UserCreateDtoValidatorTest
mvn test -Dtest=UserCommandServiceTest
# Run tests with coverage report
mvn clean test jacoco:report
# Check coverage thresholds
mvn clean test jacoco:check
# Open coverage report in browser
# Report location: target/site/jacoco/index.html
start target/site/jacoco/index.html # Windows
open target/site/jacoco/index.html # Mac
xdg-open target/site/jacoco/index.html # Linux
Tests run: 57, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
Test Class | Tests | Purpose |
---|---|---|
BasicAuthenticationIntegrationTest |
12 | HTTP Basic Authentication with admin:admin credentials |
UserCreateDtoValidatorTest |
11 | Validates all request field requirements |
UserCommandControllerTest |
3 | HTTP endpoint behavior (201, 400 responses) |
UserCommandControllerConflictTest |
3 | Login duplication handling (409 response) |
UserCommandServiceTest |
9 | Business logic including DELETE operations |
UserCommandServiceLoginValidationTest |
3 | Business logic for login validation |
UserRepositoryExistsByLoginTest |
4 | Database login existence queries |
HealthControllerTest |
2 | Health endpoint functionality |
*MapperTest classes |
16 | Data transformation between layers |
Total | 57+ | Complete CRUD + Authentication coverage |
- Line Coverage: ≥80%
- Branch Coverage: ≥70%
- Instruction Coverage: ≥80%
Note: The health endpoint does NOT require authentication.
curl -X GET http://localhost:8080/api/health
Invoke-RestMethod -Uri "http://localhost:8080/api/health" -Method GET
- Method: GET
- URL:
http://localhost:8080/api/health
- Headers:
Content-Type: application/json
(optional)
{
"status": "UP",
"timestamp": "2025-09-12T18:30:45.123456789",
"application": "Users API",
"version": "1.0.0"
}
Authentication Required: Basic Auth (admin:admin
)
# Get all users
curl -X GET http://localhost:8080/api/users \
-H "Authorization: Basic YWRtaW46YWRtaW4="
# Expected response when users exist:
# [
# {
# "id": "aab5d5fd-70c1-11e5-a4fb-b026b977eb28",
# "name": "João Silva",
# "login": "joao"
# },
# {
# "id": "bcd6e6fe-81d2-22f6-b5fb-c137c088eb29",
# "name": "Maria Santos",
# "login": "maria"
# }
# ]
# Expected response when no users exist: []
# Get all users
$headers = @{
'Authorization' = 'Basic YWRtaW46YWRtaW4='
}
Invoke-RestMethod -Uri "http://localhost:8080/api/users" -Method Get -Headers $headers
Authentication Required: Basic Auth (admin:admin
)
# Create a new user (successful request)
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4=" \
-d '{
"name": "John Doe",
"login": "johndoe",
"password": "password123"
}'
# Invalid request (missing required fields)
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Basic YWRtaW46YWRtaW4=" \
-d '{
"name": "",
"login": "johndoe"
}'
# Test without authentication (should return 401)
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"login": "johndoe",
"password": "password123"
}'
# Create a new user (successful request)
$headers = @{
'Authorization' = 'Basic YWRtaW46YWRtaW4='
'Content-Type' = 'application/json'
}
$body = @{
name = "John Doe"
login = "johndoe"
password = "password123"
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8080/api/users" -Method POST -Body $body -Headers $headers
# Invalid request (missing required fields)
$invalidBody = @{
name = ""
login = "johndoe"
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8080/api/users" -Method POST -Body $invalidBody -Headers $headers
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
- Body (JSON):
{
"name": "João Silva",
"login": "joao.silva",
"password": "senha123"
}
- Expected Response (201):
{
"id": "aab5d5fd-70c1-11e5-a4fb-b026b977eb28",
"name": "João Silva",
"login": "joao.silva"
}
Note: Password is never returned in responses for security
Run this after Test 1 to test login duplication
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
- Body (JSON):
{
"name": "Maria Santos",
"login": "joao.silva",
"password": "outrasenha"
}
- Expected Response (409):
{
"error": "User with login 'joao.silva' already exists"
}
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
- Body (JSON):
{
"name": "",
"login": "",
"password": ""
}
- Expected Response (400):
{
"errors": [
"Name is required and cannot be empty",
"Login is required and cannot be empty",
"Password is required and cannot be empty"
]
}
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
- NO Authorization header
- Body (JSON):
{
"name": "John Doe",
"login": "johndoe",
"password": "password123"
}
- Expected Response (401):
{
"timestamp": "2025-09-21T19:35:00.123Z",
"status": 401,
"error": "Unauthorized",
"message": "Full authentication is required to access this resource"
}
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
- Body (JSON):
{
"name": "John Doe",
"login": "johndoe"
}
- Expected Response (400):
{
"errors": [
"Password is required and cannot be empty"
]
}
- Method: POST
- URL:
http://localhost:8080/api/users
- Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
- Body (JSON):
{
"name": null,
"login": null,
"password": null
}
- Expected Response (400):
{
"errors": [
"Name is required and cannot be empty",
"Login is required and cannot be empty",
"Password is required and cannot be empty"
]
}
Create a collection called "Users API" with the following requests:
1. Health Check
GET Health Check
2. Get Users
GET All Users - With Data
GET All Users - Empty Response
3. Create User
POST User - Success
POST User - Login Conflict
POST User - Empty Fields
POST User - Missing Fields
POST User - Null Values
4. Delete User
DELETE User - Success
DELETE User - Not Found
DELETE User - CIS Integration Test
Method: GET
URL: http://localhost:8080/api/health
Headers: (none required - public endpoint)
Body: (none)
Method: GET
URL: http://localhost:8080/api/users
Headers: Authorization: Basic YWRtaW46YWRtaW4=
Body: (none)
Expected Response: 200 OK
Response Format:
[
{
"id": "uuid-string",
"name": "User Name",
"login": "username"
}
]
Method: POST
URL: http://localhost:8080/api/users
Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
Body (JSON):
{
"name": "João Silva",
"login": "joao.silva",
"password": "password123"
}
Expected Response: 201 Created
Method: POST
URL: http://localhost:8080/api/users
Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
Body (JSON):
{
"name": "Another User",
"login": "joao.silva",
"password": "different123"
}
Expected Response: 409 Conflict
Method: POST
URL: http://localhost:8080/api/users
Headers: Content-Type: application/json
Body (JSON):
{
"name": "Test User",
"login": "testuser",
"password": "password123"
}
Expected Response: 401 Unauthorized
Method: POST
URL: http://localhost:8080/api/users
Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
Body (JSON):
{
"name": "",
"login": "",
"password": ""
}
Expected Response: 400 Bad Request
Method: POST
URL: http://localhost:8080/api/users
Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
Body (JSON):
{
"name": "Test User"
}
Expected Response: 400 Bad Request
Method: POST
URL: http://localhost:8080/api/users
Headers:
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
Body (JSON):
{
"name": null,
"login": null,
"password": null
}
Expected Response: 400 Bad Request
Method: DELETE
URL: http://localhost:8080/api/users/{user_id}
Headers: Authorization: Basic YWRtaW46YWRtaW4=
Body: (none)
Expected Response: 200 OK (empty body)
Method: DELETE
URL: http://localhost:8080/api/users/{user_id}
Headers: (none)
Body: (none)
Expected Response: 401 Unauthorized
Method: DELETE
URL: http://localhost:8080/api/users/non-existent-id
Headers: Authorization: Basic YWRtaW46YWRtaW4=
Body: (none)
Expected Response: 404 Not Found (empty body)
Each layer is tested independently:
- Presentation Layer: Controllers tested with MockMvc for HTTP request/response
- Application Layer: Services tested with Mockito for business logic isolation (including DELETE)
- Infrastructure Layer: Repositories tested with real database connections
- Arrange-Act-Assert (AAA): All tests follow this clear structure
- Mocking: External dependencies mocked using Mockito
- Test Data Builders: Consistent test data creation patterns
- Parameterized Tests: Multiple validation scenarios in single test methods
- JUnit 5: Core testing framework
- Mockito: Mocking framework for unit tests
- Spring Boot Test: Integration testing support
- MockMvc: HTTP layer testing
- JaCoCo: Code coverage analysis
# Run only functional tests (working tests)
mvn test -Dtest="*ControllerTest,*ValidatorTest,*MapperTest,*RepositoryExistsByLoginTest"
# Run user endpoint specific tests
mvn test -Dtest="*UserCommand*,*UserCreate*,*UserRepository*"
# Skip problematic tests and run functional ones
mvn test -Dtest="!*UserCommandServiceTest,!*LoginValidationTest,!*ApplicationTests"
# Run tests with coverage (working tests only)
mvn test jacoco:report -Dtest="*ControllerTest,*ValidatorTest,*MapperTest"
# Quick validation test (11 tests)
mvn test -Dtest="UserCreateDtoValidatorTest"
# Run DELETE operation tests
mvn test -Dtest="UserCommandServiceTest#deleteUser*"
- Some unit tests have classpath issues with MockitoExtension
- Integration tests may fail if MySQL is not running
- Workaround: Use the functional test commands above
- Application: Runs perfectly - all functionality works
users-api/
├── src/
│ ├── main/
│ │ ├── java/jalau/usersapi/
│ │ │ ├── UsersApiApplication.java
│ │ │ ├── config/
│ │ │ │ └── ApplicationConfig.java # Dependency injection configuration
│ │ │ ├── core/
│ │ │ │ ├── application/ # Service implementations
│ │ │ │ │ └── UserCommandService.java
│ │ │ │ └── domain/ # Entities, services, repositories
│ │ │ │ ├── entities/
│ │ │ │ │ └── User.java
│ │ │ │ ├── exceptions/
│ │ │ │ │ ├── LoginAlreadyExistsException.java
│ │ │ │ │ └── UserNotFoundException.java
│ │ │ │ ├── repositories/
│ │ │ │ │ └── IUserRepository.java
│ │ │ │ └── services/
│ │ │ │ ├── IUserCommandService.java
│ │ │ │ └── IUserQueryService.java
│ │ │ ├── infrastructure/
│ │ │ │ └── mysql/ # Database implementations
│ │ │ │ ├── entities/
│ │ │ │ │ └── DbUser.java # JPA entity
│ │ │ │ ├── mappers/ # Domain ↔ Database mappers
│ │ │ │ │ ├── DbUserToUserMapper.java
│ │ │ │ │ └── UserToDbUserMapper.java
│ │ │ │ └── UserRepository.java
│ │ │ └── presentation/
│ │ │ ├── controllers/ # REST controllers
│ │ │ │ ├── HealthController.java
│ │ │ │ ├── UserCommandController.java
│ │ │ │ └── UserQueryController.java
│ │ │ ├── dtos/ # Data Transfer Objects
│ │ │ │ ├── HealthResponseDto.java
│ │ │ │ ├── UserCreateDto.java
│ │ │ │ ├── UserResponseDto.java
│ │ │ │ └── UserUpdateDto.java
│ │ │ ├── mappers/ # DTO ↔ Domain mappers
│ │ │ │ ├── UserCreateDtoToUserMapper.java
│ │ │ │ ├── UserToUserResponseDtoMapper.java
│ │ │ │ └── UserUpdateDtoToUserMapper.java
│ │ │ └── validators/ # Request validation
│ │ │ ├── UserCreateDtoValidator.java
│ │ │ └── UserUpdateDtoValidator.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ └── java/jalau/usersapi/
│ ├── UsersApiApplicationTests.java
│ ├── core/
│ │ └── application/
│ │ ├── UserCommandServiceTest.java
│ │ ├── UserCommandServiceLoginValidationTest.java
│ │ └── UserQueryServiceTest.java
│ ├── infrastructure/
│ │ └── mysql/
│ │ ├── mappers/
│ │ │ ├── DbUserToUserMapperTest.java
│ │ │ └── UserToDbUserMapperTest.java
│ │ └── repositories/
│ │ ├── UserRepositoryExistsByLoginTest.java
│ │ ├── UserRepositoryGetUsersTest.java
│ │ └── UserRepositoryUpdateTest.java
│ └── presentation/
│ ├── controllers/
│ │ ├── HealthControllerTest.java
│ │ ├── UserCommandControllerTest.java
│ │ ├── UserCommandControllerConflictTest.java
│ │ └── UserQueryControllerTest.java
│ ├── mappers/
│ │ ├── UserCreateDtoToUserMapperTest.java
│ │ ├── UserToUserResponseDtoMapperTest.java
│ │ └── UserUpdateDtoToUserMapperTest.java
│ └── validators/
│ ├── UserCreateDtoValidatorTest.java
│ └── UserUpdateDtoValidatorTest.java
├── target/
│ └── site/
│ └── jacoco/ # Coverage reports
│ └── index.html
├── .env.example
├── .gitignore
├── pom.xml
└── README.md
# Clean and compile
mvn clean compile
# Clean, compile and test
mvn clean test
# Create JAR package
mvn clean package
# Run application
mvn spring-boot:run
# Generate coverage report
mvn jacoco:report
All endpoints require Basic Authentication with admin:admin
credentials except the health endpoint:
GET /api/health
- Health check endpoint (⚠️ No authentication required)GET /api/users
- Get all users (🔐 Requires Basic Auth)GET /api/users/{id}
- Get specific user by ID (🔐 Requires Basic Auth)POST /api/users
- Create a new user (🔐 Requires Basic Auth)PATCH /api/users/{id}
- Update existing user (🔐 Requires Basic Auth)DELETE /api/users/{id}
- Delete user by ID (🔐 Requires Basic Auth)
Authorization: Basic YWRtaW46YWRtaW4=
🔐 Authentication Required: Basic Auth (admin:admin
)
Retrieves all users from the system.
Request: No request body required.
Responses:
- 200 OK: Returns an array of
UserResponseDto
objects- If users exist: Returns array with 1...n elements
- If no users exist: Returns empty array
[]
- 401 Unauthorized: Missing or invalid Basic Authentication credentials
Response Example:
[
{
"id": "aab5d5fd-70c1-11e5-a4fb-b026b977eb28",
"name": "João Silva",
"login": "joao"
},
{
"id": "bcd6e6fe-81d2-22f6-b5fb-c137c088eb29",
"name": "Maria Santos",
"login": "maria"
}
]
🔐 Authentication Required: Basic Auth (admin:admin
)
Creates a new user in the system.
Request Body:
name
(string, required): User's full namelogin
(string, required): User's login usernamepassword
(string, required): User's password
Responses:
- 201 Created: User created successfully, returns
UserResponseDto
withid
,name
, andlogin
- 400 Bad Request: Validation errors for missing or invalid fields
- 401 Unauthorized: Missing or invalid Basic Authentication credentials
- 409 Conflict: Login already exists
🔐 Authentication Required: Basic Auth (admin:admin
)
Updates an existing user partially.
Request Body: All fields are optional
name
(string, optional): User's full namelogin
(string, optional): User's login usernamepassword
(string, optional): User's password
Responses:
- 200 OK: User updated successfully, returns
UserResponseDto
- 400 Bad Request: Validation errors for invalid fields
- 401 Unauthorized: Missing or invalid Basic Authentication credentials
- 404 Not Found: User does not exist
🔐 Authentication Required: Basic Auth (admin:admin
)
Deletes an existing user by their unique identifier.
Request: No request body required.
Path Parameter: id
(String) - The unique identifier of the user to delete
Responses:
- 200 OK: User successfully deleted, empty response body
- 401 Unauthorized: Missing or invalid Basic Authentication credentials
- 404 Not Found: User does not exist, empty response body
This project follows Clean Architecture principles with the following layers:
- Presentation Layer (
presentation/
): Controllers and DTOs - Application Layer (
core/application/
): Service implementations - Domain Layer (
core/domain/
): Entities, service interfaces, repository interfaces - Infrastructure Layer (
infrastructure/
): Database implementations and external integrations
- Unit Tests: Test individual components in isolation
- Integration Tests: Test component interactions (planned)
- Code Coverage: Minimum 50% line coverage enforced by JaCoCo
- MockMvc: Web layer testing without full application context
- JaCoCo Coverage: Configured with 50% minimum line coverage
- Maven: Standard project structure and build lifecycle
- Spring Boot Test: Comprehensive testing framework integration
- Clean Architecture: Separation of concerns and dependency inversion
- ✅ POST /api/users: Create new users with validation and conflict handling (🔐 Basic Auth Required)
- ✅ GET /api/users: Retrieve all users from the system (🔐 Basic Auth Required)
- ✅ GET /api/users/{id}: Get specific user by ID (🔐 Basic Auth Required)
- ✅ PATCH /api/users/{id}: Update existing users with partial data (🔐 Basic Auth Required)
- ✅ DELETE /api/users/{id}: Delete users with proper error handling (🔐 Basic Auth Required)
- ✅ HTTP Basic Authentication: All
/api/users/**
endpoints secured withadmin:admin
credentials - ✅ Spring Security Integration: Proper security configuration with BCrypt password encoding
- ✅ 401 Unauthorized Responses: Missing or invalid authentication returns appropriate error codes
- ✅ Stateless Session Management: REST API compatible authentication approach
- ✅ Public Documentation Access: Swagger UI accessible without authentication
- ✅ Secure Credential Management: Implemented
.env
file system for database credentials - ✅ Automatic Loading: Environment variables loaded before Spring Boot initialization
- ✅ Security Protection:
.gitignore
configured to prevent credential commits - ✅ Developer Template:
.env.example
file for team collaboration - ✅ CIS Compatibility: Database configuration aligned with legacy CLI system
- ✅ MySQL Connection: Properly configured with environment variables
- ✅ JPA Entity Management: Automatic table creation and relationship mapping
- ✅ Connection Pooling: HikariCP for optimal database performance
- ✅ sd3db Integration: Compatible with existing CIS CLI database
- ✅ Comprehensive Validation: Request DTOs with detailed validation rules
- ✅ Business Logic Validation: Login uniqueness checking at service layer
- ✅ HTTP Status Codes: Proper 200/201/400/404/409 response handling
- ✅ Exception Management: Clean architecture exception handling with custom exceptions
- ✅ Domain Exceptions: UserNotFoundException and LoginAlreadyExistsException
- ✅ 57+ Unit Tests: Comprehensive coverage across all architectural layers
- ✅ Basic Authentication Integration Tests: Complete test coverage for all authentication scenarios
- ✅ MockMvc Integration: Complete HTTP endpoint testing
- ✅ Mockito Mocking: Proper isolation testing with dependency mocking
- ✅ JaCoCo Reporting: Code coverage metrics and enforcement
- ✅ DELETE Operation Tests: Full test coverage for user deletion functionality
The application uses different database configurations for production and testing:
Production Environment:
- Uses MySQL database with credentials from
.env
file - Environment variables loaded via
dotenv-java
- Secure credential management
- Compatible with CIS CLI system
Test Environment:
- Uses in-memory H2 database for isolated testing
- Test-specific configuration in
src/test/resources/application-test.properties
- No external dependencies for test execution
- All 45+ tests pass independently of production database
# H2 Database Configuration for Tests
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA Configuration for Tests
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
This project fully implements the Basic Authentication with username and password in the Authorization header as specified in the issue requirements.
Requirement | Status | Implementation |
---|---|---|
All Users API operations secured | ✅ COMPLETE | Spring Security configuration secures /api/users/** |
Authorization header Basic YWRtaW46YWRtaW4= |
✅ COMPLETE | admin:admin Base64 encoded credentials required |
Spring Security implementation | ✅ COMPLETE | SecurityConfig.java with proper separation of concerns |
Comprehensive test coverage | ✅ COMPLETE | BasicAuthenticationIntegrationTest.java with 12+ tests |
Test Scenario | Expected | Actual | Status |
---|---|---|---|
GET /api/users with admin:admin |
200 OK | ✅ 200 OK | PASS |
POST /api/users with admin:admin |
201 Created | ✅ 201 Created | PASS |
PATCH /api/users/{id} with admin:admin |
200 OK | ✅ 200 OK | PASS |
DELETE /api/users/{id} with admin:admin |
200 OK | ✅ 200 OK | PASS |
GET /api/users without Authorization | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
POST /api/users without Authorization | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
PATCH /api/users/{id} without Authorization | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
DELETE /api/users/{id} without Authorization | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
GET /api/users with wrong credentials | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
POST /api/users with wrong credentials | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
PATCH /api/users/{id} with wrong credentials | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
DELETE /api/users/{id} with wrong credentials | 401 Unauthorized | ✅ 401 Unauthorized | PASS |
# Exact example from the issue specification
curl --location 'http://localhost:8080/api/users' \
--header 'Authorization: Basic YWRtaW46YWRtaW4='
- Security Configuration:
src/main/java/jalau/usersapi/config/SecurityConfig.java
- Authentication Tests:
src/test/java/jalau/usersapi/integration/security/BasicAuthenticationIntegrationTest.java
- Protected Controllers:
UserQueryController.java
andUserCommandController.java
- Credentials: Username:
admin
, Password:admin
(Base64:YWRtaW46YWRtaW4=
)
This project is part of an academic assignment for Software Development 3 course.