diff --git a/.github/workflows/ci.yml b/.github/workflows/pr-build.yml similarity index 57% rename from .github/workflows/ci.yml rename to .github/workflows/pr-build.yml index 0c4fddbc..c85332fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/pr-build.yml @@ -1,4 +1,4 @@ -name: Docker Image CI +name: PR Build on: push: @@ -23,8 +23,6 @@ on: jobs: build-context: runs-on: ubuntu-latest - outputs: - push_image: ${{ (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')) || (github.event_name == 'release' && github.event.action == 'published') }} steps: - name: Dump GitHub context env: @@ -37,14 +35,14 @@ jobs: PLATFORMS: "linux/amd64,linux/arm64" steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Env seen prerun run: env - name: Get branch name id: branch-name - uses: tj-actions/branch-names@v5.2 + uses: tj-actions/branch-names@v8 - name: Current branch name run: | @@ -79,20 +77,13 @@ jobs: # setup Docker build action - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Set up QEMU id: qemu - uses: docker/setup-qemu-action@v2 - - - name: Login to DockerHub - if: needs.build-context.outputs.push_image == 'true' - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + uses: docker/setup-qemu-action@v3 - name: Build crapi-identity image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/identity tags: crapi/crapi-identity:${{ env.TAG_LATEST }},crapi/crapi-identity:${{ env.TAG_NAME }} @@ -103,7 +94,7 @@ jobs: cache-to: type=gha,mode=max,scope=identity-service - name: Build crapi-workshop image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/workshop tags: crapi/crapi-workshop:${{ env.TAG_LATEST }},crapi/crapi-workshop:${{ env.TAG_NAME }} @@ -114,7 +105,7 @@ jobs: cache-to: type=gha,mode=max,scope=workshop-service - name: Build crapi-community image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/community tags: crapi/crapi-community:${{ env.TAG_LATEST }},crapi/crapi-community:${{ env.TAG_NAME }} @@ -125,7 +116,7 @@ jobs: cache-to: type=gha,mode=max,scope=community-service - name: Build crapi-web image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/web tags: crapi/crapi-web:${{ env.TAG_LATEST }},crapi/crapi-web:${{ env.TAG_NAME }} @@ -136,7 +127,7 @@ jobs: cache-to: type=gha,mode=max,scope=web-service - name: Build gateway-service image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/gateway-service tags: crapi/gateway-service:${{ env.TAG_LATEST }},crapi/gateway-service:${{ env.TAG_NAME }} @@ -146,16 +137,8 @@ jobs: cache-from: type=gha,scope=gateway-service cache-to: type=gha,mode=max,scope=gateway-service - - - name: Check Mailhog existence - id: check_mailhog_exists - uses: andstor/file-existence-action@v1 - with: - files: "./services/mailhog" - - name: Build mailhog image - if: steps.check_mailhog_exists.outputs.files_exists == 'true' - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: ./services/mailhog tags: crapi/mailhog:${{ env.TAG_LATEST }},crapi/mailhog:${{ env.TAG_NAME }} @@ -177,7 +160,7 @@ jobs: - name: Install Node uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 - name: Install newman run: npm install -g newman @@ -185,68 +168,82 @@ jobs: - name: Run Postman Collection run: (newman run "./postman_collections/crAPI.postman_collection.json" -e ./postman_collections/crAPI.postman_environment.json --verbose) || true - - name: Build crapi-identity all platforms and conditionally push to Docker Hub - uses: docker/build-push-action@v2 - with: - context: ./services/identity - tags: crapi/crapi-identity:${{ env.TAG_LATEST }},crapi/crapi-identity:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=identity-service - cache-to: type=gha,mode=max,scope=identity-service + - name: Dump docker logs on failure + if: failure() + uses: jwalton/gh-docker-logs@v2 - - name: Build crapi-workshop all platforms and conditionally push to Docker Hub - uses: docker/build-push-action@v2 - with: - context: ./services/workshop - tags: crapi/crapi-workshop:${{ env.TAG_LATEST }},crapi/crapi-workshop:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=workshop-service - cache-to: type=gha,mode=max,scope=workshop-service + - name: Run crAPI using built images + run: docker-compose -f deploy/docker/docker-compose.yml down --volumes --remove-orphans - - name: Build crapi-community all platforms and conditionally push to Docker Hub - uses: docker/build-push-action@v2 - with: - context: ./services/community - tags: crapi/crapi-community:${{ env.TAG_LATEST }},crapi/crapi-community:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=community-service - cache-to: type=gha,mode=max,scope=community-service - - name: Build crapi-web all platforms and conditionally push to Docker Hub - uses: docker/build-push-action@v2 + tests: + needs: build-context + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 with: - context: ./services/web - tags: crapi/crapi-web:${{ env.TAG_LATEST }},crapi/crapi-web:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=web-service - cache-to: type=gha,mode=max,scope=web-service + python-version: '3.10' - - name: Build gateway-service all platforms and conditionally push to Docker Hub - uses: docker/build-push-action@v2 + - name: Setup Java + uses: actions/setup-java@v4 with: - context: ./services/gateway-service - tags: crapi/gateway-service:${{ env.TAG_LATEST }},crapi/gateway-service:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=gateway-service - cache-to: type=gha,mode=max,scope=gateway-service + distribution: 'adopt' + java-version: '11' - - name: Build mailhog all platforms and conditionally push to Docker Hub - if: steps.check_mailhog_exists.outputs.files_exists == 'true' - uses: docker/build-push-action@v2 + - name: Setup Go + uses: actions/setup-go@v5 with: - context: ./services/mailhog - tags: crapi/mailhog:${{ env.TAG_LATEST }},crapi/mailhog:${{ env.TAG_NAME }} - push: ${{ needs.build-context.outputs.push_image }} - platforms: ${{ env.PLATFORMS }} - cache-from: type=gha,scope=mailhog-service - cache-to: type=gha,mode=max,scope=mailhog-service + go-version: '1.21' - - name: Dump docker logs on failure - if: failure() - uses: jwalton/gh-docker-logs@v2 + - name: Start the database + run: docker-compose -f services/docker-database.yml up -d + - name: Run identity tests + run: | + cd services/identity + ./gradlew test + + - name: Run community tests + run: | + cd services/community + go test -v ./... + mkdir test-results + go run gotest.tools/gotestsum@latest --format testname --junitfile test-results/unit-tests.xml + + - name: Run workshop tests + run: | + cd services/workshop + pip3 install virtualenv + virtualenv venv + source venv/bin/activate + pip3 install -r requirements.txt + mkdir -p test-results + source .env + IS_TESTING=True python3 manage.py test --no-input --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: | + **/test-results/**/*.xml + **/test-results/**/*.json + + - name: Run workshop coverage + run: | + cd services/workshop + source venv/bin/activate + source .env + IS_TESTING=True coverage run ./manage.py test --no-input crapi + coverage report + coverage xml -o coverage.xml + + - name: Publish Coverage for workshop + uses: orgoro/coverage@v3.1 + with: + coverageFile: services/workshop/coverage.xml + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..2f6a266b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,142 @@ +name: Publish Images + +on: + push: + branches: + - 'develop' + - 'main' + tags: + - 'v*.*.*' + paths: + - 'services/**' + - 'postman_collections/**' + - 'deploy/**' + - '.github/workflows/**' + workflow_dispatch: + +jobs: + build-context: + runs-on: ubuntu-latest + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + build: + needs: build-context + runs-on: ubuntu-latest + env: + PLATFORMS: "linux/amd64,linux/arm64" + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Env seen prerun + run: env + + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v8 + + - name: Current branch name + run: | + echo "Current Branch: ${{ steps.branch-name.outputs.current_branch }}" + echo "Target Branch: ${{ steps.branch-name.outputs.base_ref_branch }}" + echo "Tag if exist: ${{ steps.branch-name.outputs.tag }}" + + - name: Running on a tag. + if: steps.branch-name.outputs.is_tag == 'true' + run: | + echo "TAG_NAME=${{ steps.branch-name.outputs.tag }}" >> ${GITHUB_ENV} + echo "TAG_LATEST=latest" >> ${GITHUB_ENV} + + - name: Running on a branch and merge. + if: steps.branch-name.outputs.is_tag != 'true' && github.event_name == 'push' && steps.branch-name.outputs.current_branch != 'main' + run: | + echo "TAG_NAME=${{ steps.branch-name.outputs.current_branch }}" >> ${GITHUB_ENV} + echo "TAG_LATEST=${{ steps.branch-name.outputs.current_branch }}" >> ${GITHUB_ENV} + + - name: Running on main branch. + if: steps.branch-name.outputs.is_tag != 'true' && github.event_name == 'push' && steps.branch-name.outputs.current_branch == 'main' + run: | + echo "TAG_NAME=${{ steps.branch-name.outputs.current_branch }}" >> ${GITHUB_ENV} + echo "TAG_LATEST=latest" >> ${GITHUB_ENV} + + - name: Running on a branch and pull req. + if: steps.branch-name.outputs.is_tag != 'true' && github.event_name != 'push' + run: | + echo "TAG_NAME=${{ steps.branch-name.outputs.base_ref_branch }}" >> ${GITHUB_ENV} + echo "TAG_LATEST=${{ steps.branch-name.outputs.base_ref_branch }}" >> ${GITHUB_ENV} + + # setup Docker build action + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + - name: Set up QEMU + id: qemu + uses: docker/setup-qemu-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build crapi-identity all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/identity + tags: crapi/crapi-identity:${{ env.TAG_LATEST }},crapi/crapi-identity:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=identity-service + cache-to: type=gha,mode=max,scope=identity-service + + - name: Build crapi-workshop all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/workshop + tags: crapi/crapi-workshop:${{ env.TAG_LATEST }},crapi/crapi-workshop:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=workshop-service + cache-to: type=gha,mode=max,scope=workshop-service + + - name: Build crapi-community all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/community + tags: crapi/crapi-community:${{ env.TAG_LATEST }},crapi/crapi-community:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=community-service + cache-to: type=gha,mode=max,scope=community-service + + - name: Build crapi-web all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/web + tags: crapi/crapi-web:${{ env.TAG_LATEST }},crapi/crapi-web:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=web-service + cache-to: type=gha,mode=max,scope=web-service + + - name: Build gateway-service all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/gateway-service + tags: crapi/gateway-service:${{ env.TAG_LATEST }},crapi/gateway-service:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=gateway-service + cache-to: type=gha,mode=max,scope=gateway-service + + - name: Build mailhog all platforms and conditionally push to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./services/mailhog + tags: crapi/mailhog:${{ env.TAG_LATEST }},crapi/mailhog:${{ env.TAG_NAME }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha,scope=mailhog-service + cache-to: type=gha,mode=max,scope=mailhog-service + + - name: Dump docker logs on failure + if: failure() + uses: jwalton/gh-docker-logs@v2 + diff --git a/.gitignore b/.gitignore index 8552476c..dbc12474 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ deploy/vagrant/*.log deploy/vagrant/.vagrant .secrets .vscode/ +*.local diff --git a/deploy/docker/.env b/deploy/docker/.env index d263946e..602468f0 100644 --- a/deploy/docker/.env +++ b/deploy/docker/.env @@ -4,5 +4,5 @@ WORKSHOP_SERVER_PORT=8000 ENABLE_SHELL_INJECTION=false ENABLE_LOG4J=false LISTEN_IP="127.0.0.1" -TLS_ENABLED=false +TLS_ENABLED=true VERSION=latest diff --git a/services/community/.gitignore b/services/community/.gitignore new file mode 100644 index 00000000..51511d1f --- /dev/null +++ b/services/community/.gitignore @@ -0,0 +1 @@ +test-results/ diff --git a/services/community/Dockerfile b/services/community/Dockerfile index 89b85f8f..d7b6cf8e 100644 --- a/services/community/Dockerfile +++ b/services/community/Dockerfile @@ -12,7 +12,7 @@ # GoLang Build -FROM golang:alpine AS builder +FROM golang:1.21-alpine AS builder ENV GO111MODULE=on \ CGO_ENABLED=0 WORKDIR /build @@ -39,6 +39,6 @@ COPY certs /app/certs COPY health.sh /app/health.sh RUN ls -al /app -ARG SERVER_PORT +ARG SERVER_PORT EXPOSE ${SERVER_PORT} CMD /app/main diff --git a/services/docker-database.yml b/services/docker-database.yml new file mode 100644 index 00000000..8a2cf61e --- /dev/null +++ b/services/docker-database.yml @@ -0,0 +1,50 @@ +# Licensed under the Apache License, Version 2.0 (the “License”); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an “AS IS” BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +services: + + postgresdb: + container_name: postgresdb + image: 'postgres:14' + command: ["postgres", "-c", "max_connections=500"] + environment: + POSTGRES_USER: admin + POSTGRES_PASSWORD: crapisecretpassword + POSTGRES_DB: crapi + ports: + - "127.0.0.1:5432:5432" + healthcheck: + test: [ "CMD-SHELL", "pg_isready" ] + interval: 15s + timeout: 15s + retries: 15 + + mongodb: + container_name: mongodb + image: 'mongo:4.4' + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: crapisecretpassword + ports: + - "127.0.0.1:27017:27017" + healthcheck: + test: echo 'db.runCommand("ping").ok' | mongo mongodb:27017/test --quiet + interval: 15s + timeout: 15s + retries: 15 + start_period: 20s + + + + + + diff --git a/services/identity/.gitignore b/services/identity/.gitignore index 87b17ba0..23090a4e 100644 --- a/services/identity/.gitignore +++ b/services/identity/.gitignore @@ -21,3 +21,4 @@ gradle-app.setting # JDT-specific (Eclipse Java Development Tools) .classpath bin/ +test-results/ diff --git a/services/workshop/.gitignore b/services/workshop/.gitignore index ff7f2033..5fa7cefa 100644 --- a/services/workshop/.gitignore +++ b/services/workshop/.gitignore @@ -1,3 +1,6 @@ __pycache__/ *.pyc *.log +.coverage +*.xml +test-results/ diff --git a/services/workshop/Dockerfile b/services/workshop/Dockerfile index fd29593f..17481fc3 100644 --- a/services/workshop/Dockerfile +++ b/services/workshop/Dockerfile @@ -12,21 +12,20 @@ # limitations under the License. -FROM python:3.8-alpine3.15 as build +FROM python:3.10-alpine as build # Not using alpine based on suggestion # https://pythonspeed.com/articles/alpine-docker-python/ -# Python +# Python ENV PYTHONUNBUFFERED=1 RUN apk add --update --no-cache gettext curl py3-pip postgresql-dev RUN apk add --update --no-cache --virtual .build-deps \ python3-dev openssl-dev \ libffi-dev gcc py3-pip \ python3-dev \ - libressl-dev \ musl-dev \ libffi-dev - + ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 RUN mkdir /app @@ -34,7 +33,7 @@ WORKDIR /app COPY ./ /app RUN pip install wheel && pip wheel . --wheel-dir /app/wheels -FROM python:3.8-alpine3.15 +FROM python:3.10-alpine COPY --from=build /app /app WORKDIR /app RUN apk update && apk add --no-cache postgresql-libs curl diff --git a/services/workshop/core/__init__.py b/services/workshop/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/services/workshop/core/management/__init__.py b/services/workshop/core/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/services/workshop/core/management/commands/seed_database.py b/services/workshop/core/management/commands/seed_database.py new file mode 100644 index 00000000..597650c1 --- /dev/null +++ b/services/workshop/core/management/commands/seed_database.py @@ -0,0 +1,226 @@ +# +# Licensed under the Apache License, Version 2.0 (the “License”); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an “AS IS” BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +Configuration for crapi application +""" +import django +import sys +from django.apps import AppConfig +import bcrypt +from django.utils import timezone +from django.db import models +from django.db import connection, transaction +import logging +import traceback +from django.core.management.base import BaseCommand +from django.utils import timezone + +logger = logging.getLogger() + + +def create_products(): + from crapi.shop.models import Product + product_details_all = [ + { + 'name': 'Seat', + 'price': 10, + 'image_url': 'images/seat.svg' + }, + { + 'name': 'Wheel', + 'price': 10, + 'image_url': 'images/wheel.svg' + } + ] + for product_details in product_details_all: + if Product.objects.filter(name=product_details['name']).exists(): + logger.info("Product already exists. Skipping: "+ product_details['name']) + continue + product = Product.objects.create( + name=product_details['name'], + price=float(product_details['price']), + image_url=product_details['image_url'] + ) + product.save() + logger.info("Created Product: "+str(product.__dict__)) + +def create_mechanics(): + from crapi.user.models import User, UserDetails + from crapi.mechanic.models import Mechanic + mechanic_details_all = [ + { + 'name': 'Jhon', + 'email': 'jhon@example.com', + 'number': '', + 'password': 'Admin1@#', + 'mechanic_code': 'TRAC_JHN' + }, + { + 'name': 'James', + 'email': 'james@example.com', + 'number': '', + 'password': 'Admin1@#', + 'mechanic_code': 'TRAC_JME' + }, + ] + for mechanic_details in mechanic_details_all: + uset = User.objects.filter(email=mechanic_details['email']) + if not uset.exists(): + try: + cursor = connection.cursor() + cursor.execute("select nextval('user_login_id_seq')") + result = cursor.fetchone() + user_id = result[0] + except Exception as e: + logger.error("Failed to fetch user_login_id_seq"+str(e)) + user_id = 1 + + user = User.objects.create( + id=user_id, + email=mechanic_details['email'], + number=mechanic_details['number'], + password=bcrypt.hashpw( + mechanic_details['password'].encode('utf-8'), + bcrypt.gensalt() + ).decode(), + role=User.ROLE_CHOICES.MECH, + created_on=timezone.now() + ) + user.save() + logger.info("Created User: "+str(user.__dict__)) + else: + user = uset.first() + + if Mechanic.objects.filter(mechanic_code=mechanic_details['mechanic_code']): + logger.info("Mechanic already exists. Skipping: " + + mechanic_details['mechanic_code'] + + " " + mechanic_details['name'] + " " + + mechanic_details['email']) + continue + mechanic = Mechanic.objects.create( + mechanic_code=mechanic_details['mechanic_code'], + user=user + ) + mechanic.save() + try: + cursor = connection.cursor() + cursor.execute("select nextval('user_details_id_seq')") + result = cursor.fetchone() + user_details_id = result[0] + except Exception as e: + logger.error("Failed to fetch user_details_id_seq"+str(e)) + user_details_id = 1 + userdetails = UserDetails.objects.create( + id=user_details_id, + available_credit=0, + name=mechanic_details['name'], + status='ACTIVE', + user=user + ) + userdetails.save() + +def create_reports(): + import random + import sys + import textwrap + from crapi.user.models import User, UserDetails, Vehicle + from crapi.mechanic.models import Mechanic, ServiceRequest + from django.utils import timezone + count = ServiceRequest.objects.all().count() + if (count >= 5): + return + logger.info("Creating Reports") + mechanics = Mechanic.objects.all() + vehicles = Vehicle.objects.all() + for i in range(5): + try: + mechanic = random.choice(mechanics) + vehicle = random.choice(vehicles) + status = random.choice(ServiceRequest.STATUS_CHOICES)[0] + vehicle_model = vehicle.vehicle_model + vehicle_company = vehicle_model.vehiclecompany + user = vehicle.owner + user_detail = UserDetails.objects.filter(user=user).first() + service_request = ServiceRequest.objects.create( + vehicle=vehicle, + mechanic=mechanic, + problem_details=textwrap.dedent("""\ + My car {} - {} is having issues. + Can you give me a call on my mobile {}, + Or send me an email at {} + Thanks, + {}. + """.format( + vehicle_company.name, + vehicle_model.model, + user.number, + user.email, + user_detail.name) + ), + status=status, + created_on=timezone.now() + ) + service_request.save() + logger.info("Created Service Request for User %s: %s", user.email, service_request.__dict__) + except Exception as e: + print(sys.exc_info()[0]) + logger.error("Failed to create report: "+str(e)) + +def create_orders(): + import uuid + from crapi.user.models import User, UserDetails + from crapi.shop.models import Product + from crapi.shop.models import Order + if Order.objects.all().count() >= 1: + return + users = User.objects.filter(role=User.ROLE_CHOICES.PREDEFINED).order_by('id') + for user in users: + product = Product.objects.filter(name='Seat').first() + order = Order.objects.create( + user=user, + product=product, + quantity=2, + created_on=timezone.now(), + transaction_id=uuid.uuid4(), + ) + order.save() + logger.info("Created Order for User %s: %s", user.email, order.__dict__) + + +class Command(BaseCommand): + help = 'Seed the database with initial data.' + + def handle(self, *args, **kwargs): + """ + Pre-populate mechanic model and product model + :return: None + """ + logger.info("Pre Populating Model Data") + try: + create_products() + except Exception as e: + logger.error("Cannot Pre Populate Products: "+str(e)) + try: + create_mechanics() + except Exception as e: + logger.error("Cannot Pre Populate Mechanics: "+str(e)) + try: + create_reports() + except Exception as e: + logger.error("Cannot Pre Populate Reports: "+str(e)) + try: + create_orders() + except Exception as e: + logger.error("Cannot Pre Populate Orders: "+str(e)) diff --git a/services/workshop/crapi/apps.py b/services/workshop/crapi/apps.py index f0534e7b..59b3913f 100644 --- a/services/workshop/crapi/apps.py +++ b/services/workshop/crapi/apps.py @@ -147,8 +147,6 @@ def create_reports(): mechanic = random.choice(mechanics) vehicle = random.choice(vehicles) status = random.choice(ServiceRequest.STATUS_CHOICES)[0] - logger.info(vehicle.__dict__) - logger.info(status) vehicle_model = vehicle.vehicle_model vehicle_company = vehicle_model.vehiclecompany user = vehicle.owner @@ -173,7 +171,7 @@ def create_reports(): created_on=timezone.now() ) service_request.save() - logger.info(service_request.__dict__) + logger.info("Created Service Request for User %s: %s", user.email, service_request.__dict__) except Exception as e: print(sys.exc_info()[0]) logger.error("Failed to create report: "+str(e)) @@ -185,26 +183,18 @@ def create_orders(): from crapi.shop.models import Order if Order.objects.all().count() >= 1: return - user = User.objects.filter(email='test@example.com').first() - product = Product.objects.filter(name='Seat').first() - order1 = Order.objects.create( - user=user, - product=product, - quantity=2, - created_on=timezone.now(), - transaction_id=uuid.uuid4(), - ) - order1.save() - logger.info("Created Order:1: "+str(order1.__dict__)) - order2 = Order.objects.create( - user=user, - product=product, - quantity=2, - created_on=timezone.now(), - transaction_id=uuid.uuid4(), - ) - order2.save() - logger.info("Created Order:2: "+str(order2.__dict__)) + users = User.objects.filter(role=User.ROLE_CHOICES.PREDEFINED).order_by('id') + for user in users: + product = Product.objects.filter(name='Seat').first() + order = Order.objects.create( + user=user, + product=product, + quantity=2, + created_on=timezone.now(), + transaction_id=uuid.uuid4(), + ) + order.save() + logger.info("Created Order for User %s: %s", user.email, order.__dict__) diff --git a/services/workshop/crapi/mechanic/models.py b/services/workshop/crapi/mechanic/models.py index 7d6bf721..09b7bf26 100644 --- a/services/workshop/crapi/mechanic/models.py +++ b/services/workshop/crapi/mechanic/models.py @@ -53,8 +53,8 @@ class ServiceRequest(models.Model): updated_on = models.DateTimeField(null=True) STATUS_CHOICES = Choices( - ('PEN', "Pending", "Pending"), - ('FIN', "Finished", "Finished") + ('PEN', "pending", "Pending"), + ('FIN', "finished", "Finished") ) status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=STATUS_CHOICES.PEN) diff --git a/services/workshop/crapi/user/models.py b/services/workshop/crapi/user/models.py index a903b0aa..c248f4c2 100644 --- a/services/workshop/crapi/user/models.py +++ b/services/workshop/crapi/user/models.py @@ -36,6 +36,7 @@ class User(models.Model): password = models.CharField(max_length=255) ROLE_CHOICES = Choices( + ('PREDEFINED', 0, 'Predefined'), ('USER', 1, 'User'), ('MECH', 2, 'Mechanic'), ('ADMIN', 3, 'Admin'), diff --git a/services/workshop/crapi_site/settings.py b/services/workshop/crapi_site/settings.py index 85026849..d81d19fe 100644 --- a/services/workshop/crapi_site/settings.py +++ b/services/workshop/crapi_site/settings.py @@ -73,8 +73,10 @@ def get_env_value(env_variable): 'corsheaders', 'health_check', 'health_check.db', - 'crapi.apps.CRAPIConfig', - #'user.apps.UserConfig', + 'core', + 'crapi', + # 'crapi.apps.CRAPIConfig', + # 'user.apps.UserConfig', "django_extensions", ] @@ -95,6 +97,10 @@ def get_env_value(env_variable): ROOT_URLCONF = 'crapi_site.urls' +TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' + +TEST_OUTPUT_DIR = os.path.join(BASE_DIR, 'test-reports') + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', diff --git a/services/workshop/requirements.txt b/services/workshop/requirements.txt index 6662beb2..d8b2d67b 100644 --- a/services/workshop/requirements.txt +++ b/services/workshop/requirements.txt @@ -17,4 +17,7 @@ pymongo==3.13.0 pyOpenSSL==23.1.1 requests==2.30.0 Werkzeug==2.0.3 -Faker==22.1.0 \ No newline at end of file +Faker==22.1.0 +gunicorn==21.2.0 +coverage==7.4.1 +unittest-xml-reporting==3.2.0 \ No newline at end of file diff --git a/services/workshop/runner.sh b/services/workshop/runner.sh index 5725079b..27b85160 100755 --- a/services/workshop/runner.sh +++ b/services/workshop/runner.sh @@ -23,8 +23,8 @@ python3 manage.py migrate python3 manage.py check &&\ python3 manage.py health_check -## Uncomment the following line if you wish to run tests -IS_TESTING=True python3 manage.py test --no-input +echo "Seeding the database" +python3 manage.py seed_database echo "Starting Django server" if [ "$TLS_ENABLED" = "true" ] || [ "$TLS_ENABLED" = "1" ]; then @@ -38,9 +38,10 @@ if [ "$TLS_ENABLED" = "true" ] || [ "$TLS_ENABLED" = "1" ]; then fi echo "TLS_CERTIFICATE: $TLS_CERTIFICATE" echo "TLS_KEY: $TLS_KEY" - python3 manage.py runserver_plus --cert-file $TLS_CERTIFICATE --key-file $TLS_KEY --noreload 0.0.0.0:${SERVER_PORT} + # python3 manage.py runserver_plus --cert-file $TLS_CERTIFICATE --key-file $TLS_KEY --noreload 0.0.0.0:${SERVER_PORT} + gunicorn --workers=2 --threads=10 --timeout 60 --bind 0.0.0.0:${SERVER_PORT} --certfile $TLS_CERTIFICATE --keyfile $TLS_KEY --log-level=debug crapi_site.wsgi else echo "TLS is DISABLED" - python3 manage.py runserver 0.0.0.0:${SERVER_PORT} --noreload + # python3 manage.py runserver 0.0.0.0:${SERVER_PORT} --noreload + gunicorn --workers=2 --threads=10 --timeout 60 --bind 0.0.0.0:${SERVER_PORT} --log-level=debug crapi_site.wsgi fi -exec "$@" diff --git a/services/workshop/utils/messages.py b/services/workshop/utils/messages.py index 3b3d83fd..6393f961 100644 --- a/services/workshop/utils/messages.py +++ b/services/workshop/utils/messages.py @@ -37,6 +37,7 @@ COUPON_NOT_FOUND = "Coupon not found" RESTRICTED = "You are not allowed to access this resource!" INVALID_STATUS = "The value of 'status' has to be 'delivered','return pending' or 'returned'" +INVALID_SERVICE_REQUEST_STATUS = "The value of 'status' has to be 'Pending' or 'Finished'" REPORT_ID_MISSING = "Please enter the report_id value." INVALID_REPORT_ID = "Please enter a valid report_id value." REPORT_DOES_NOT_EXIST = "The Report does not exist for given report_id."