This project is designed to manage car-related data using a REST API. It reads car data from a .csv file, transforms it, and stores it in a PostgreSQL database. The project provides a comprehensive REST API for CRUD operations and includes OpenAPI and Swagger documentation. Docker is used for containerization, enabling easy setup and deployment.
- Features
- Getting Started
- Configuration
- Database Schema
- API Endpoints
- Security
- Swagger Documentation
- Contributors
- Read and parse
.csvfiles containing car data. - Store car data in a PostgreSQL database.
- Provide a REST API for CRUD operations on car data.
- Use OpenAPI and Swagger for API documentation.
- Utilize Docker for easy setup and deployment.
- Integrate Keycloak for OAuth2 and JWT-based authentication.
- Docker installed on your machine.
-
Clone the repository:
git clone https://github.com/avecoss/car-service.git cd car-service -
To configure your application to connect to a PostgreSQL database, add the following properties to your application.yml / docker-compose.yml file:
# Database configuration datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/your_database_name username: your_username password: your_password
# docker-compose configuration services: api: environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://db_pg:5432/your_database_name - SPRING_DATASOURCE_USERNAME=your_username - SPRING_DATASOURCE_PASSWORD=your_password db_pg: environment: - POSTGRES_DB=your_database_name - POSTGRES_USER=your_username - POSTGRES_PASSWORD=your_password
Make sure to replace
your_database_name,your_username, andyour_passwordwith your actual PostgreSQL database name, username, and password. -
Build and run the services using Docker Compose:
docker-compose up --build
This command will start the PostgreSQL database, the Car REST Service, and Keycloak for authentication.
Configuration details is provided in the docker-compose.yml file. Below is a brief overview of key configurations:
name: car-rest-service
services:
api:
image: car-service
build:
context: .
dockerfile: Dockerfile
restart: always
ports:
- "8080:8080"
depends_on:
- db_pg
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db_pg:5432/your_database_name
- SPRING_DATASOURCE_USERNAME=your_username
- SPRING_DATASOURCE_PASSWORD=your_password
container_name: "api-car-service"
db_pg:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: your_database_name
POSTGRES_USER: your_username
POSTGRES_PASSWORD: your_password
ports:
- "5433:5432"
container_name: "db-pg"
keycloak:
image: quay.io/keycloak/keycloak:25.0.1
restart: always
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- "8888:8080"
volumes:
- './config/keycloak:/opt/keycloak/data'
depends_on:
- db_pg
command: start-dev
container_name: "keycloak"The following tables are defined in the PostgreSQL database:
- Producer: Stores car manufacturers.
- Model: Stores car models.
- Category: Stores car categories.
- Car: Stores car details.
- Car_Category: Junction table for car categories.
Schema creation script:
CREATE SCHEMA IF NOT EXISTS car;
CREATE TABLE IF NOT EXISTS car.producer
(
producer_id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL
);
CREATE TABLE IF NOT EXISTS car.model
(
model_id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
producer_id BIGINT REFERENCES car.producer (producer_id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS car.category
(
category_id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE IF NOT EXISTS car.car
(
id BIGSERIAL PRIMARY KEY,
object_id VARCHAR(255),
year VARCHAR(4) NOT NULL CHECK (year ~ '^\d{4}$') CHECK (year BETWEEN '1900' AND '2100'),
model_id BIGINT REFERENCES car.model (model_id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS car.car_category
(
car_id BIGINT REFERENCES car.car (id) ON DELETE CASCADE,
category_id BIGINT REFERENCES car.category (category_id) ON DELETE CASCADE,
PRIMARY KEY (car_id, category_id)
)
The Car REST Service provides the following endpoints for managing cars, models, and manufacturers:
- GET /api/v1/cars/{id}: Retrieve a car by ID.
- POST /api/v1/cars: Create a new car.
- PATCH /api/v1/cars: Update an existing car.
- DELETE /api/v1/cars/{id}: Delete a car by ID.
- GET /api/v1/cars: List all cars with optional filtering and pagination.
- GET /api/v1/models/{id}: Retrieve a model by ID.
- POST /api/v1/models: Create a new model.
- PATCH /api/v1/models: Update an existing model.
- DELETE /api/v1/models/{id}: Delete a model by ID.
- GET /api/v1/models: List all models with optional filtering and pagination.
- GET /api/v1/manufacturers/{id}: Retrieve a manufacturer by ID.
- POST /api/v1/manufacturers: Create a new manufacturer.
- PATCH /api/v1/manufacturers: Update an existing manufacturer.
- DELETE /api/v1/manufacturers/{id}: Delete a manufacturer by ID.
- GET /api/v1/manufacturers: List all manufacturers with pagination.
The service uses Keycloak for OAuth2 and JWT-based authentication. Security configurations are defined in the SecurityConfig class.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtConverter jwtConverter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/**").permitAll()
.requestMatchers(HttpMethod.POST, "/**").authenticated()
.requestMatchers(HttpMethod.PATCH, "/**").authenticated()
.requestMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
.anyRequest().authenticated())
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter)))
.build();
}
}Swagger UI is available at /swagger-ui.html once the service is running. It provides an interactive interface to explore and test the API endpoints.
