diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f106e40 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +LOGISTICS_SERVICE_PORT=8002 +DJANGO_PORT=8000 +KAFKA_BROKER_URL=kafka:9092 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dee0d57..503825d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,12 +45,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt - - - name: Wait for Kafka to be ready - run: | - echo "Waiting for Kafka..." - sleep 20 # adjust based on startup time + pip install --no-cache-dir -r requirements.txt - name: Run Django tests (including Kafka) run: | diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..16414e2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# Use official Python slim image +FROM python:3.12-slim + +LABEL maintainer="kevin" + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV DJANGO_PORT=8000 + +# Set working directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + libpq-dev \ + curl \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project files +COPY . . + +# Copy entrypoint script +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +# Expose port (match DJANGO_PORT env) +EXPOSE ${DJANGO_PORT} + +# Run entrypoint script +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index ead4566..db963f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,5 @@ version: '3.8' + services: zookeeper: image: confluentinc/cp-zookeeper:7.4.0 @@ -14,5 +15,19 @@ services: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + depends_on: + - zookeeper + + logistics-service: + build: . + container_name: logistics-service + ports: + - "${LOGISTICS_SERVICE_PORT:-8002}:8000" + environment: + - DJANGO_PORT=8000 + - KAFKA_BROKER_URL=kafka:9092 + env_file: .env + depends_on: + - kafka diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..e30b760 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "Running makemigrations..." +python manage.py makemigrations --noinput + +echo "Running migrate..." +python manage.py migrate --noinput + +echo "Starting Django server on port ${DJANGO_PORT}..." +python manage.py runserver 0.0.0.0:${DJANGO_PORT} diff --git a/logistics_core/settings.py b/logistics_core/settings.py index ac55724..f6c2c33 100644 --- a/logistics_core/settings.py +++ b/logistics_core/settings.py @@ -135,7 +135,10 @@ ENABLE_FLEET_EXTENDED_MODELS = False # kafka settings -KAFKA_BROKER_URL = "localhost:9092" +import os + +KAFKA_BROKER_URL = os.getenv("KAFKA_BROKER_URL", "localhost:9092") + CORS_ALLOWED_ORIGINS = [ "http://localhost:4200", diff --git a/order_simulator.py b/order_simulator.py index d362f6b..8e6c1a3 100644 --- a/order_simulator.py +++ b/order_simulator.py @@ -1,14 +1,27 @@ -from confluent_kafka import Producer +import os +import django import json +from confluent_kafka import Producer + +# Setup Django (assuming this file is at the project root level) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'logistics_core.settings') +django.setup() -producer = Producer({'bootstrap.servers': 'localhost:9092'}) +from django.conf import settings + +# Fallback if env not set +bootstrap_servers = getattr(settings, 'KAFKA_BROKER_URL', 'localhost:9092') + +producer = Producer({'bootstrap.servers': bootstrap_servers}) event = { - "order_id": "ORD9999", - "origin_warehouse_id": "WH001", - "destination_warehouse_id": "WH002" + "order_id": "ORD001", + "origin": {"lat": 6.9271, "lng": 79.8612}, + "destination": {"lat": 7.2906, "lng": 80.6337}, + "demand": 25 } producer.produce('orders.created', json.dumps(event).encode('utf-8')) producer.flush() -print("Published mock order event.") + +print("✅ Published mock order event to 'orders.created'") diff --git a/requirements.txt b/requirements.txt index 9d32fb0..91625e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ absl-py==2.2.2 asgiref==3.8.1 +confluent-kafka==2.10.0 Django==5.2 +django-cors-headers==4.7.0 django-filter==25.1 djangorestframework==3.16.0 drf-yasg==1.21.10 @@ -15,11 +17,10 @@ pluggy==1.5.0 protobuf==5.29.4 pytest==8.3.5 python-dateutil==2.9.0.post0 +python-dotenv==1.1.0 pytz==2025.2 PyYAML==6.0.2 six==1.17.0 sqlparse==0.5.3 tzdata==2025.2 uritemplate==4.1.1 -confluent-kafka~=2.10.0 -django-cors-headers \ No newline at end of file diff --git a/shipments/tests/test_integration_kafka.py b/shipments/tests/test_integration_kafka.py index 4cbd047..84583fc 100644 --- a/shipments/tests/test_integration_kafka.py +++ b/shipments/tests/test_integration_kafka.py @@ -4,6 +4,7 @@ from shipments.models import Shipment from confluent_kafka import Producer from shipments.consumers.order_events import run_consumer_once +from django.conf import settings logger = logging.getLogger(__name__) @@ -11,7 +12,7 @@ class KafkaE2ETest(TestCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.producer = Producer({'bootstrap.servers': 'localhost:9092'}) + cls.producer = Producer({'bootstrap.servers': settings.KAFKA_BROKER_URL}) def test_order_event_creates_shipment(self): order_id = "KAFKA_E2E_01"