From 2761b0901a59a0129c8f808f5a5ed74313e47f38 Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Tue, 13 Aug 2024 20:25:31 +0100 Subject: [PATCH 1/7] feat : Added get all Invitations endpoint --- api/v1/routes/organisation.py | 15 ++++++- api/v1/services/invite.py | 7 ++-- .../v1/invitation/test_get_all_invitations.py | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/v1/invitation/test_get_all_invitations.py diff --git a/api/v1/routes/organisation.py b/api/v1/routes/organisation.py index ec66b3939..842a241ac 100644 --- a/api/v1/routes/organisation.py +++ b/api/v1/routes/organisation.py @@ -3,7 +3,7 @@ from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session - +from api.v1.services import invite from api.utils.success_response import success_response from api.v1.models.user import User from api.v1.schemas.organisation import ( @@ -127,3 +127,16 @@ async def delete_organisation( status_code=status.HTTP_200_OK, message="Organisation with ID {org_id} deleted successfully", ) + +@organisation.get("/invites", status_code=200) +def get_organization_invites( + db : Session = Depends(get_db), + current_user : User = Depends(user_service.get_current_super_admin), +) : + invitations = invite.InviteService.fetch_all(db) + + return success_response( + status_code=status.HTTP_200_OK, + message='Invites fetched succesfully', + data=[jsonable_encoder(invitation) for invitation in invitations] + ) diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index b78546f45..f50407619 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -212,9 +212,10 @@ def delete_all(session: Session): def fetch(self): pass - def fetch_all(self): - pass - + @staticmethod + def fetch_all(session : Session): + all_invites = session.query(Invitation).all() + return all_invites def update(self): pass diff --git a/tests/v1/invitation/test_get_all_invitations.py b/tests/v1/invitation/test_get_all_invitations.py new file mode 100644 index 000000000..4dfbb0592 --- /dev/null +++ b/tests/v1/invitation/test_get_all_invitations.py @@ -0,0 +1,42 @@ +from unittest.mock import patch, MagicMock +import pytest +from fastapi.testclient import TestClient +from datetime import datetime, timedelta, timezone +from uuid_extensions import uuid7 +from api.v1.models.invitation import Invitation +from api.v1.services.user import user_service, UserService +from main import app +from api.db.database import get_db +from api.v1.models.user import User + +client = TestClient(app) +@pytest.fixture +def mock_db_session(): + with patch("api.db.database.get_db", autospec=True) as mock_get_db: + mock_db = MagicMock() + app.dependency_overrides[get_db] = lambda: mock_db + yield mock_db + app.dependency_overrides = {} + +def test_get_all_invitations(mock_db_session): + + invitations = [Invitation(user_id=str(uuid7()), is_valid = True , organisation_id = str(uuid7()), id=str(uuid7()))] + + mock_db_session.query().all.return_value = invitations + + app.dependency_overrides[user_service.get_current_super_admin] = lambda : User( + id=str(uuid7()), + email="testuser@gmail.com", + password="hashed_password", + first_name='Test', + last_name='User', + is_active=True, + created_at=datetime.now(timezone.utc), + updated_at=datetime.now(timezone.utc), + is_superadmin = True + ) + + res = client.get('/api/v1/organisations/invites') + + assert res.status_code == 200 + From 4c31090912b5655cf458a356e16dcb106886d561 Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Tue, 13 Aug 2024 20:32:54 +0100 Subject: [PATCH 2/7] feat : Added get all invitations endpoint --- tests/v1/invitation/test_get_all_invitations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/v1/invitation/test_get_all_invitations.py b/tests/v1/invitation/test_get_all_invitations.py index 4dfbb0592..bdbd7111a 100644 --- a/tests/v1/invitation/test_get_all_invitations.py +++ b/tests/v1/invitation/test_get_all_invitations.py @@ -36,7 +36,7 @@ def test_get_all_invitations(mock_db_session): is_superadmin = True ) - res = client.get('/api/v1/organisations/invites') + response = client.get('/api/v1/organisations/invites') - assert res.status_code == 200 + assert response.status_code == 200 From c92e55c3ad537e8845db501cabda360acb6915af Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Wed, 14 Aug 2024 01:22:55 +0100 Subject: [PATCH 3/7] Feat added invitations endpoint --- api/v1/routes/organisation.py | 3 ++- api/v1/services/invite.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/v1/routes/organisation.py b/api/v1/routes/organisation.py index 842a241ac..c0568908a 100644 --- a/api/v1/routes/organisation.py +++ b/api/v1/routes/organisation.py @@ -134,9 +134,10 @@ def get_organization_invites( current_user : User = Depends(user_service.get_current_super_admin), ) : invitations = invite.InviteService.fetch_all(db) + return success_response( status_code=status.HTTP_200_OK, message='Invites fetched succesfully', - data=[jsonable_encoder(invitation) for invitation in invitations] + data=[jsonable_encoder(invitation, exclude=['user_id','organisation_id']) for invitation in invitations] ) diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index f50407619..bb445b9bb 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -1,7 +1,7 @@ import logging from datetime import datetime, timedelta from pytz import utc -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session,joinedload from sqlalchemy import insert from fastapi import HTTPException, Request, Depends, status from collections import OrderedDict @@ -9,6 +9,7 @@ from api.v1.models.invitation import Invitation from api.v1.models.organisation import Organisation from api.v1.models.user import User +from api.v1.services.user import user_service from sqlalchemy.exc import IntegrityError from api.v1.models.permissions.role import Role from api.v1.schemas.permissions.roles import RoleCreate @@ -214,7 +215,10 @@ def fetch(self): @staticmethod def fetch_all(session : Session): - all_invites = session.query(Invitation).all() + all_invites = session.query(Invitation).options(joinedload(Invitation.organisation)).all() + for invite in all_invites : + user = user_service.get_user_by_id(db=session , id=invite.user_id) + invite.email = user.email return all_invites def update(self): pass From 5ce054aab701538176c76d41f084d4d5895e170d Mon Sep 17 00:00:00 2001 From: Sarah Aligbe <37581442+Sarahligbe@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:34:36 +0100 Subject: [PATCH 4/7] new ci workflow --- .../workflows/{cd.dev.yml.bac => cd.dev.yaml} | 19 +++---- .github/workflows/cd.dev.yml | 40 -------------- .../{cd.prod.yml.bac => cd.prod.yaml} | 17 +++--- .github/workflows/cd.prod.yml | 39 -------------- .../{cd.staging.yml.bac => cd.staging.yaml} | 17 +++--- .github/workflows/cd.staging.yml | 39 -------------- .github/workflows/ci.yml | 9 +--- .../{pr-deploy.yml => pr-deploy.yml.bkp} | 0 .github/workflows/test.yml.bac | 53 ------------------- 9 files changed, 21 insertions(+), 212 deletions(-) rename .github/workflows/{cd.dev.yml.bac => cd.dev.yaml} (60%) delete mode 100644 .github/workflows/cd.dev.yml rename .github/workflows/{cd.prod.yml.bac => cd.prod.yaml} (61%) delete mode 100644 .github/workflows/cd.prod.yml rename .github/workflows/{cd.staging.yml.bac => cd.staging.yaml} (69%) delete mode 100644 .github/workflows/cd.staging.yml rename .github/workflows/{pr-deploy.yml => pr-deploy.yml.bkp} (100%) delete mode 100644 .github/workflows/test.yml.bac diff --git a/.github/workflows/cd.dev.yml.bac b/.github/workflows/cd.dev.yaml similarity index 60% rename from .github/workflows/cd.dev.yml.bac rename to .github/workflows/cd.dev.yaml index fcb167d96..2ebf6cc75 100644 --- a/.github/workflows/cd.dev.yml.bac +++ b/.github/workflows/cd.dev.yaml @@ -1,16 +1,16 @@ name: Dev Branch Deployment on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [dev] + paths-ignore: + - "README.md" + - ".github/workflows/**" + workflow_dispatch: jobs: - on-success: + deploy: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v3 @@ -22,14 +22,9 @@ jobs: username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/dev_source_code/ + cd boilerplate-python/dev/ git pull origin dev source .venv/bin/activate pip install -r requirements.txt alembic upgrade head - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.dev.yml b/.github/workflows/cd.dev.yml deleted file mode 100644 index dc018e8f5..000000000 --- a/.github/workflows/cd.dev.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Dev cd pipeline -on: - push: - branches: [dev] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Build image - run: docker build -t anchor-python-bp-dev:latest -f Dockerfile . - - - name: Save image - run: docker save anchor-python-bp-dev:latest | gzip > dev.tar.gz - - - name: Copy image to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - source: "dev.tar.gz" - target: "/home/${{ secrets.USERNAME }}/boilerplate-python/dev_source_code" - - - name: Deploy image on server - uses: appleboy/ssh-action@v0.1.8 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - script: | - cd /home/${{ secrets.USERNAME }}/boilerplate-python/dev_source_code - git pull origin dev - docker load --input dev.tar.gz - docker compose -f docker-compose.yml up -d - rm -f dev.tar.gz - diff --git a/.github/workflows/cd.prod.yml.bac b/.github/workflows/cd.prod.yaml similarity index 61% rename from .github/workflows/cd.prod.yml.bac rename to .github/workflows/cd.prod.yaml index 23217d2e5..f65f90954 100644 --- a/.github/workflows/cd.prod.yml.bac +++ b/.github/workflows/cd.prod.yaml @@ -1,16 +1,16 @@ name: Prod Branch Deployment on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [main] + paths-ignore: + - "README.md" + - ".github/workflows/**" + workflow_dispatch: jobs: on-success: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v3 @@ -22,14 +22,9 @@ jobs: username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/prod_source_code/ + cd boilerplate-python/prod/ git pull origin main source .venv/bin/activate pip install -r requirements.txt alembic upgrade head - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.prod.yml b/.github/workflows/cd.prod.yml deleted file mode 100644 index 46dff1e8f..000000000 --- a/.github/workflows/cd.prod.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Prod cd pipeline -on: - push: - branches: [main] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Build image - run: docker build -t anchor-python-bp-prod:latest -f Dockerfile . - - - name: Save image - run: docker save anchor-python-bp-prod:latest | gzip > prod.tar.gz - - - name: Copy image to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - source: "prod.tar.gz" - target: "/home/${{ secrets.USERNAME }}/boilerplate-python/prod_source_code" - - - name: Deploy image on server - uses: appleboy/ssh-action@v0.1.8 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - script: | - cd /home/${{ secrets.USERNAME }}/boilerplate-python/prod_source_code - git pull origin main - docker load --input prod.tar.gz - docker compose -f docker-compose.prod.yml up -d - rm -f prod.tar.gz diff --git a/.github/workflows/cd.staging.yml.bac b/.github/workflows/cd.staging.yaml similarity index 69% rename from .github/workflows/cd.staging.yml.bac rename to .github/workflows/cd.staging.yaml index 7de13a1b6..5138accb7 100644 --- a/.github/workflows/cd.staging.yml.bac +++ b/.github/workflows/cd.staging.yaml @@ -1,11 +1,12 @@ name: Staging Branch Deployment on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [staging] + paths-ignore: + - "README.md" + - ".github/workflows/**" + workflow_dispatch: jobs: on-success: @@ -22,14 +23,8 @@ jobs: username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/staging_source_code/ + cd boilerplate-python/staging/ git pull origin staging source .venv/bin/activate pip install -r requirements.txt alembic upgrade head - - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.staging.yml b/.github/workflows/cd.staging.yml deleted file mode 100644 index cdc182adc..000000000 --- a/.github/workflows/cd.staging.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Staging cd pipeline -on: - push: - branches: [staging] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Build image - run: docker build -t anchor-python-bp-staging:latest -f Dockerfile . - - - name: Save image - run: docker save anchor-python-bp-staging:latest | gzip > staging.tar.gz - - - name: Copy image to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - source: "staging.tar.gz" - target: "/home/${{ secrets.USERNAME }}/boilerplate-python/staging_source_code" - - - name: Deploy image on server - uses: appleboy/ssh-action@v0.1.8 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - script: | - cd /home/${{ secrets.USERNAME }}/boilerplate-python/staging_source_code - git pull origin staging - docker load --input staging.tar.gz - docker compose -f docker-compose.staging.yml up -d - rm -f staging.tar.gz diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1500c277a..3b86b3f40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,8 @@ name: CI on: - push: - branches: [main, staging, dev, devops-main] - paths-ignore: - - "README.md" - - ".github/workflows/**" pull_request: - branches: [main, staging, dev, devops-main] + types: [opened, synchronize, reopened] paths-ignore: - "README.md" - ".github/workflows/**" @@ -28,7 +23,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 diff --git a/.github/workflows/pr-deploy.yml b/.github/workflows/pr-deploy.yml.bkp similarity index 100% rename from .github/workflows/pr-deploy.yml rename to .github/workflows/pr-deploy.yml.bkp diff --git a/.github/workflows/test.yml.bac b/.github/workflows/test.yml.bac deleted file mode 100644 index 621a3a634..000000000 --- a/.github/workflows/test.yml.bac +++ /dev/null @@ -1,53 +0,0 @@ -name: Test -on: - pull_request: - types: [opened, synchronize, reopened, closed] - paths-ignore: - - "README.md" - - ".github/workflows/**" - -jobs: - test: - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:latest - env: - POSTGRES_USER: "username" - POSTGRES_PASSWORD: "password" - POSTGRES_DB: "test" - ports: - - 5432:5432 - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install dependencies - run: | - pip install -r requirements.txt - - - name: Copy env file - run: cp .env.sample .env - - - name: Run migrations - run: | - alembic upgrade head - - - name: Run tests - run: | - PYTHONPATH=. pytest From fcf1acbf4a196f28fdd1e58343fcf2e1e66ee6ec Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Thu, 15 Aug 2024 03:50:18 +0100 Subject: [PATCH 5/7] fix worked on product feature endpoint --- api/v1/routes/product.py | 23 +---------------------- api/v1/services/product.py | 4 ++++ 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/api/v1/routes/product.py b/api/v1/routes/product.py index cde3239a8..fe8c2a941 100644 --- a/api/v1/routes/product.py +++ b/api/v1/routes/product.py @@ -265,31 +265,10 @@ def get_organisation_products( db, user=current_user, org_id=org_id, limit=limit, page=page ) - total_products = len(products) - - total_pages = int(total_products / limit) + (total_products % limit > 0) - - product_data = [ - { - "name": product.name, - "description": product.description, - "price": str(product.price), - } - for product in products - ] - - data = { - "current_page": page, - "total_pages": total_pages, - "limit": limit, - "total_items": total_products, - "products": product_data, - } - return success_response( status_code=200, message="Successfully fetched organisations products", - data=data, + data=[jsonable_encoder(product) for product in products], ) diff --git a/api/v1/services/product.py b/api/v1/services/product.py index a52ea6a4f..60c93a63b 100644 --- a/api/v1/services/product.py +++ b/api/v1/services/product.py @@ -252,6 +252,10 @@ def fetch_all(db: Session, **query_params: Optional[Any]): ) return query.all() + + + + product_service = ProductService() From 568546a776799644ad73df554c092632c8b330bb Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Thu, 15 Aug 2024 03:59:17 +0100 Subject: [PATCH 6/7] fix products endpoint --- api/v1/routes/organisation.py | 16 +------ api/v1/services/invite.py | 9 +--- .../v1/invitation/test_get_all_invitations.py | 42 ------------------- 3 files changed, 3 insertions(+), 64 deletions(-) delete mode 100644 tests/v1/invitation/test_get_all_invitations.py diff --git a/api/v1/routes/organisation.py b/api/v1/routes/organisation.py index c0568908a..d291454fe 100644 --- a/api/v1/routes/organisation.py +++ b/api/v1/routes/organisation.py @@ -126,18 +126,4 @@ async def delete_organisation( return success_response( status_code=status.HTTP_200_OK, message="Organisation with ID {org_id} deleted successfully", - ) - -@organisation.get("/invites", status_code=200) -def get_organization_invites( - db : Session = Depends(get_db), - current_user : User = Depends(user_service.get_current_super_admin), -) : - invitations = invite.InviteService.fetch_all(db) - - - return success_response( - status_code=status.HTTP_200_OK, - message='Invites fetched succesfully', - data=[jsonable_encoder(invitation, exclude=['user_id','organisation_id']) for invitation in invitations] - ) + ) \ No newline at end of file diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index bb445b9bb..5ee27b507 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -213,13 +213,8 @@ def delete_all(session: Session): def fetch(self): pass - @staticmethod - def fetch_all(session : Session): - all_invites = session.query(Invitation).options(joinedload(Invitation.organisation)).all() - for invite in all_invites : - user = user_service.get_user_by_id(db=session , id=invite.user_id) - invite.email = user.email - return all_invites + def fetch_all(self): + pass def update(self): pass diff --git a/tests/v1/invitation/test_get_all_invitations.py b/tests/v1/invitation/test_get_all_invitations.py deleted file mode 100644 index bdbd7111a..000000000 --- a/tests/v1/invitation/test_get_all_invitations.py +++ /dev/null @@ -1,42 +0,0 @@ -from unittest.mock import patch, MagicMock -import pytest -from fastapi.testclient import TestClient -from datetime import datetime, timedelta, timezone -from uuid_extensions import uuid7 -from api.v1.models.invitation import Invitation -from api.v1.services.user import user_service, UserService -from main import app -from api.db.database import get_db -from api.v1.models.user import User - -client = TestClient(app) -@pytest.fixture -def mock_db_session(): - with patch("api.db.database.get_db", autospec=True) as mock_get_db: - mock_db = MagicMock() - app.dependency_overrides[get_db] = lambda: mock_db - yield mock_db - app.dependency_overrides = {} - -def test_get_all_invitations(mock_db_session): - - invitations = [Invitation(user_id=str(uuid7()), is_valid = True , organisation_id = str(uuid7()), id=str(uuid7()))] - - mock_db_session.query().all.return_value = invitations - - app.dependency_overrides[user_service.get_current_super_admin] = lambda : User( - id=str(uuid7()), - email="testuser@gmail.com", - password="hashed_password", - first_name='Test', - last_name='User', - is_active=True, - created_at=datetime.now(timezone.utc), - updated_at=datetime.now(timezone.utc), - is_superadmin = True - ) - - response = client.get('/api/v1/organisations/invites') - - assert response.status_code == 200 - From e6d302e71061c5c1f93b97a39d27a2385f42bdd5 Mon Sep 17 00:00:00 2001 From: utibe solomon Date: Thu, 15 Aug 2024 04:12:58 +0100 Subject: [PATCH 7/7] fix products endpoint --- api/v1/routes/organisation.py | 1 - api/v1/services/invite.py | 2 +- api/v1/services/product.py | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/api/v1/routes/organisation.py b/api/v1/routes/organisation.py index d291454fe..a41d159d8 100644 --- a/api/v1/routes/organisation.py +++ b/api/v1/routes/organisation.py @@ -3,7 +3,6 @@ from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session -from api.v1.services import invite from api.utils.success_response import success_response from api.v1.models.user import User from api.v1.schemas.organisation import ( diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index 5ee27b507..45a818de9 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -1,7 +1,7 @@ import logging from datetime import datetime, timedelta from pytz import utc -from sqlalchemy.orm import Session,joinedload +from sqlalchemy.orm import Session from sqlalchemy import insert from fastapi import HTTPException, Request, Depends, status from collections import OrderedDict diff --git a/api/v1/services/product.py b/api/v1/services/product.py index 60c93a63b..61cce2f3a 100644 --- a/api/v1/services/product.py +++ b/api/v1/services/product.py @@ -255,7 +255,4 @@ def fetch_all(db: Session, **query_params: Optional[Any]): - - - product_service = ProductService()