Skip to main content

Overview

sgivu-vehicle manages the vehicle catalog (cars and motorcycles) and associated images. It supports CRUD operations, advanced search, state management (available/unavailable), and image upload/management via AWS S3 with presigned URLs.

Technologies

  • Java: 21
  • Spring Boot: 4.0.1
  • Spring Security: Resource Server (JWT validation)
  • Spring Data JPA: PostgreSQL persistence
  • Flyway: Database migrations
  • AWS SDK S3: software.amazon.awssdk.s3 for image storage
  • Spring Cloud Config Client: Centralized configuration
  • Spring Cloud Eureka Client: Service registration
  • SpringDoc OpenAPI: Swagger UI documentation
  • MapStruct: DTO mapping
  • Lombok: Code generation

Prerequisites

  • JDK 21
  • Maven 3.9+
  • PostgreSQL database
  • AWS S3 bucket with proper permissions
  • sgivu-config and sgivu-discovery services running

Running the Service

Development with Docker Compose

cd infra/compose/sgivu-docker-compose
docker compose -f docker-compose.dev.yml up -d

Local Execution

./mvnw clean install -DskipTests
./mvnw spring-boot:run

Docker Build

./build-image.bash
docker build -t sgivu-vehicle:local .

AWS S3 Integration

S3 Service

S3Service provides presigned URL generation for:
  • Upload: Direct client-to-S3 uploads
  • Download: Temporary access to images
Benefits:
  • Reduces server load (direct upload)
  • Secure temporary access
  • No proxy bandwidth consumption

Presigned URL Flow

1

Request Upload URL

Client requests presigned URL from backend:
POST /v1/vehicles/{id}/images/presigned-upload
{
  "fileName": "front-view.jpg",
  "contentType": "image/jpeg"
}
2

Upload to S3

Client uploads directly to S3 using presigned URL:
PUT https://bucket.s3.amazonaws.com/vehicle-123/front-view.jpg
Content-Type: image/jpeg

[binary image data]
3

Confirm Upload

Client confirms upload to backend:
POST /v1/vehicles/{id}/images/confirm-upload
{
  "key": "vehicle-123/front-view.jpg",
  "fileName": "front-view.jpg"
}
Backend validates key and registers image metadata in database.
4

Access Image

Client requests download URL when needed:
GET /v1/vehicles/{id}/images/{imageId}/download-url
Returns presigned download URL valid for limited time.

Allowed Image Types

Only these MIME types are accepted:
  • image/jpeg
  • image/png
  • image/webp
Upload requests with other content types will be rejected. Validate content type on client before requesting presigned URL.

Primary Image Management

  • First Image: Automatically marked as is_primary=true
  • Delete Primary: Next oldest image automatically promoted to primary
  • Set Primary: Use dedicated endpoint to change primary image

CORS Configuration

Bucket CORS

S3BucketCorsConfig ensures S3 bucket allows uploads from configured origins:
@Configuration
public class S3BucketCorsConfig {
    @PostConstruct
    public void configureBucketCors() {
        // Reads from aws.s3.allowed-origins
        // Applies CORS rules to bucket
    }
}
Configuration:
aws:
  s3:
    bucket-name: sgivu-vehicles
    allowed-origins:
      - http://localhost:3000
      - https://app.sgivu.com
Update allowed-origins to match your frontend domains in production.

Security

Authentication

JWT Token Validation:
  • Tokens issued by sgivu-auth
  • JwtAuthenticationConverter maps claims to authorities
  • Resource Server validates signature via JWKS

Authorization

Required Permissions:
  • vehicle:create - Create vehicles
  • vehicle:read - View vehicles
  • vehicle:update - Update vehicles
  • vehicle:delete - Delete vehicles
  • vehicle:manage-images - Upload/delete images

Internal Service Communication

InternalServiceAuthenticationFilter:
  • Allows service-to-service calls via X-Internal-Service-Key
  • Bypasses JWT validation for internal endpoints
  • Must be kept secret and rotated regularly

AWS Security Recommendations

Production Security:
  • Move AWS credentials to secret manager (AWS Secrets Manager, HashiCorp Vault)
  • Use IAM roles instead of access keys when running on EC2/ECS
  • Apply least privilege bucket policies
  • Limit presigned URL expiration time (currently 15 minutes)
  • Enable S3 bucket versioning and logging
  • Use VPC endpoints for S3 access

Database Schema

Flyway Migrations

Location: src/main/resources/db/migration Tables Created:
  • vehicles: Main vehicle catalog
    • Common fields: VIN, brand, model, year, price, status
    • Type discriminator for cars vs motorcycles
  • vehicle_images: Image metadata
    • S3 key, file name, content type
    • is_primary flag
    • Foreign key to vehicles
Indexes:
  • vehicles.vin (unique)
  • vehicles.status
  • vehicles.vehicle_type
  • vehicle_images.vehicle_id
  • vehicle_images.is_primary

API Endpoints

Vehicle Management

GET /v1/vehicles
protected
List vehicles with advanced filteringRequired Permission: vehicle:readQuery Parameters:
  • status: Filter by status (AVAILABLE, SOLD, RESERVED)
  • vehicleType: Filter by type (CAR, MOTORCYCLE)
  • minPrice, maxPrice: Price range
  • brand, model, year: Search criteria
  • page, size, sort: Pagination
GET /v1/vehicles/{id}
protected
Get vehicle by ID with imagesRequired Permission: vehicle:read
POST /v1/vehicles
protected
Create new vehicleRequired Permission: vehicle:createValidations:
  • Unique VIN
  • Valid vehicle type
  • Required fields for type
PUT /v1/vehicles/{id}
protected
Update vehicle informationRequired Permission: vehicle:update
PATCH /v1/vehicles/{id}/status
protected
Update vehicle statusRequired Permission: vehicle:updateBody: {"status": "AVAILABLE"}
DELETE /v1/vehicles/{id}
protected
Delete vehicle (soft delete)Required Permission: vehicle:delete

Image Management

POST /v1/vehicles/{id}/images/presigned-upload
protected
Request presigned URL for image uploadRequired Permission: vehicle:manage-imagesBody:
{
  "fileName": "front-view.jpg",
  "contentType": "image/jpeg"
}
Returns:
{
  "uploadUrl": "https://bucket.s3.amazonaws.com/...",
  "key": "vehicle-123/front-view.jpg",
  "expiresIn": 900
}
POST /v1/vehicles/{id}/images/confirm-upload
protected
Confirm image upload and register metadataRequired Permission: vehicle:manage-imagesBody:
{
  "key": "vehicle-123/front-view.jpg",
  "fileName": "front-view.jpg"
}
GET /v1/vehicles/{id}/images/{imageId}/download-url
protected
Get presigned download URL for imageRequired Permission: vehicle:readReturns:
{
  "downloadUrl": "https://bucket.s3.amazonaws.com/...",
  "expiresIn": 900
}
PUT /v1/vehicles/{id}/images/{imageId}/set-primary
protected
Set image as primaryRequired Permission: vehicle:manage-images
DELETE /v1/vehicles/{id}/images/{imageId}
protected
Delete vehicle imageRequired Permission: vehicle:manage-imagesBehavior: If deleting primary image, promotes next image to primary

Request/Response Examples

Create Car

POST /v1/vehicles
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "vehicleType": "CAR",
  "vin": "1HGBH41JXMN109186",
  "brand": "Toyota",
  "model": "Camry",
  "year": 2023,
  "color": "Silver",
  "mileage": 15000,
  "price": 28500.00,
  "status": "AVAILABLE",
  "doors": 4,
  "transmission": "AUTOMATIC",
  "fuelType": "GASOLINE"
}

Response

{
  "id": 123,
  "vehicleType": "CAR",
  "vin": "1HGBH41JXMN109186",
  "brand": "Toyota",
  "model": "Camry",
  "year": 2023,
  "color": "Silver",
  "mileage": 15000,
  "price": 28500.00,
  "status": "AVAILABLE",
  "doors": 4,
  "transmission": "AUTOMATIC",
  "fuelType": "GASOLINE",
  "images": [],
  "createdAt": "2024-03-06T10:30:00Z",
  "updatedAt": "2024-03-06T10:30:00Z"
}

Observability

Actuator Endpoints

  • /actuator/health: Service and S3 connectivity health
  • /actuator/info: Application metadata
  • /actuator/metrics: Custom metrics for image operations

OpenAPI Documentation

Swagger UI: /swagger-ui/index.html Configuration: OpenApiConfig with environment-specific servers

Distributed Tracing

Zipkin Integration:
  • Configured via sgivu-config
  • Traces S3 operations
  • Correlates image upload flows

Testing

./mvnw test
Test Coverage:
  • Unit tests for services
  • Repository integration tests
  • S3 service mocking
  • Security authorization tests
  • Image workflow integration tests

Troubleshooting

Symptoms: API requests return unauthorized or forbiddenSolutions:
  • Verify Bearer token contains required authorities
  • Check token issuer matches auth server
  • Ensure user roles include vehicle permissions
  • Validate token hasn’t expired
Symptoms: Presigned URL upload returns 403 or failsSolutions:
  • Verify AWS credentials and permissions
  • Check S3 bucket policy allows PutObject
  • Confirm bucket name is correct
  • Validate content type is allowed
  • Review CORS configuration on bucket
  • Check presigned URL hasn’t expired
Symptoms: Upload to S3 succeeds but image not appearingSolutions:
  • Ensure confirm-upload endpoint is called
  • Verify S3 key matches between upload and confirm
  • Check database for image record
  • Review backend logs for validation errors
  • Confirm vehicle ID exists
Symptoms: Browser blocks S3 upload with CORS errorSolutions:
  • Verify aws.s3.allowed-origins includes frontend domain
  • Check S3 bucket CORS configuration applied
  • Ensure S3BucketCorsConfig ran on startup
  • Add explicit CORS rules in AWS console if needed
  • Verify Content-Type header matches allowed types
Symptoms: Wrong image shows as primary or no primary setSolutions:
  • Check database for is_primary flag consistency
  • Verify only one image per vehicle has is_primary=true
  • Review delete logic for primary image promotion
  • Use set-primary endpoint to explicitly set primary
Symptoms: Vehicle creation fails with constraint violationSolutions:
  • Check VIN uniqueness before creation
  • Handle duplicate key exception gracefully
  • Allow VIN update only in specific scenarios
  • Consider soft delete for removed vehicles

Environment Variables

VariableDescriptionExample
DB_HOSTPostgreSQL hostsgivu-postgres-vehicle
DB_PORTPostgreSQL port5432
DB_NAMEDatabase namesgivu_vehicle
DB_USERNAMEDatabase usernamesgivu_user
DB_PASSWORDDatabase passwordsecure_password
AWS_ACCESS_KEY_IDAWS access keyAKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEYAWS secret keywJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_REGIONS3 regionus-east-1
aws.s3.bucket-nameS3 bucket namesgivu-vehicles
aws.s3.allowed-originsCORS allowed originshttp://localhost:3000
SERVICE_INTERNAL_SECRET_KEYInternal service keysecret-key-value

Best Practices

Presigned URLs

Use presigned URLs for direct client-to-S3 uploads to reduce server load

Image Validation

Validate image types and sizes before generating presigned URLs

IAM Roles

Use IAM roles instead of access keys in production environments

URL Expiration

Keep presigned URL expiration short (15 minutes) for security

Purchase-Sale

References vehicles in purchase/sale contracts

Client Service

Clients purchase vehicles

Gateway

Routes vehicle management requests