Skip to content

Commit

Permalink
Merge branch 'release-0.2.0' into LCFS-1255-AddOrganizationNameFSE
Browse files Browse the repository at this point in the history
  • Loading branch information
areyeslo authored Dec 11, 2024
2 parents 598faa7 + 38279d5 commit 96180b9
Show file tree
Hide file tree
Showing 42 changed files with 1,290 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Remove notifications email from user_profile
Revision ID: cd8698fe40e6
Revises: 26ab15f8ab18
Create Date: 2024-12-09 22:33:29.554360
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "cd8698fe40e6"
down_revision = "26ab15f8ab18"
branch_labels = None
depends_on = None


def upgrade() -> None:
# Remove notifications_email column from user_profile table
op.drop_column("user_profile", "notifications_email")


def downgrade() -> None:
# Add notifications_email column to user_profile table
op.add_column(
"user_profile",
sa.Column(
"notifications_email",
sa.String(length=255),
nullable=True,
comment="Email address used for notifications",
),
)
3 changes: 0 additions & 3 deletions backend/lcfs/db/models/user/UserProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ class UserProfile(BaseModel, Auditable):
String(150), unique=True, nullable=False, comment="keycloak Username"
)
email = Column(String(255), nullable=True, comment="Primary email address")
notifications_email = Column(
String(255), nullable=True, comment="Email address used for notifications"
)
title = Column(String(100), nullable=True, comment="Professional Title")
phone = Column(String(50), nullable=True, comment="Primary phone number")
mobile_phone = Column(String(50), nullable=True, comment="Mobile phone number")
Expand Down
4 changes: 2 additions & 2 deletions backend/lcfs/tests/fuel_code/test_fuel_code_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ async def test_approve_fuel_code_not_found():
repo_mock.get_fuel_code.return_value = None

# Act & Assert
with pytest.raises(ServiceException):
with pytest.raises(ValueError, match="Fuel code not found"):
await service.approve_fuel_code(fuel_code_id)
repo_mock.get_fuel_code.assert_called_once_with(fuel_code_id)

Expand All @@ -229,7 +229,7 @@ async def test_approve_fuel_code_invalid_status():
repo_mock.get_fuel_code.return_value = mock_fuel_code

# Act & Assert
with pytest.raises(ServiceException):
with pytest.raises(ValueError, match="Fuel code is not in Draft"):
await service.approve_fuel_code(fuel_code_id)

repo_mock.get_fuel_code.assert_called_once_with(fuel_code_id)
2 changes: 1 addition & 1 deletion backend/lcfs/tests/other_uses/test_other_uses_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ async def test_update_other_use_not_found(other_uses_service):

mock_repo.get_other_use_version_by_user = AsyncMock(return_value=None)

with pytest.raises(ServiceException):
with pytest.raises(ValueError, match="Other use not found"):
await service.update_other_use(other_use_data, UserTypeEnum.SUPPLIER)


Expand Down
14 changes: 6 additions & 8 deletions backend/lcfs/tests/user/test_user_repo.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from unittest.mock import AsyncMock, Mock
from unittest.mock import Mock

import pytest

from lcfs.db.models import UserProfile, UserLoginHistory
from lcfs.web.api.user.repo import UserRepository
from lcfs.tests.user.user_payloads import user_orm_model
from lcfs.web.exception.exceptions import DataNotFoundException


@pytest.fixture
Expand Down Expand Up @@ -50,14 +49,13 @@ async def test_create_login_history(dbsession, user_repo):


@pytest.mark.anyio
async def test_update_notifications_email_success(dbsession, user_repo):
async def test_update_email_success(dbsession, user_repo):
# Arrange: Create a user in the database
user = UserProfile(
keycloak_user_id="user_id_1",
keycloak_email="[email protected]",
keycloak_username="username1",
email="[email protected]",
notifications_email=None,
title="Developer",
phone="1234567890",
mobile_phone="0987654321",
Expand All @@ -70,10 +68,10 @@ async def test_update_notifications_email_success(dbsession, user_repo):
await dbsession.commit()
await dbsession.refresh(user)

# Act: Update the notifications email
updated_user = await user_repo.update_notifications_email(
# Act: Update the email
updated_user = await user_repo.update_email(
user_profile_id=1, email="[email protected]"
)

# Assert: Check if the notifications email was updated
assert updated_user.notifications_email == "[email protected]"
# Assert: Check if the email was updated
assert updated_user.email == "[email protected]"
7 changes: 3 additions & 4 deletions backend/lcfs/tests/user/test_user_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,19 +468,18 @@ async def test_track_logged_in_success(client: AsyncClient, fastapi_app, set_moc


@pytest.mark.anyio
async def test_update_notifications_email_success(
async def test_update_email_success(
client: AsyncClient,
fastapi_app,
set_mock_user,
add_models,
):
set_mock_user(fastapi_app, [RoleEnum.GOVERNMENT])

# Prepare request data
request_data = {"notifications_email": "[email protected]"}
request_data = {"email": "[email protected]"}

# Act: Send POST request to the endpoint
url = fastapi_app.url_path_for("update_notifications_email")
url = fastapi_app.url_path_for("update_email")
response = await client.post(url, json=request_data)

# Assert: Check response status and content
Expand Down
1 change: 0 additions & 1 deletion backend/lcfs/tests/user/user_payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
keycloak_email="[email protected]",
keycloak_username="username",
email="[email protected]",
notifications_email=None,
title="Developer",
phone="1234567890",
mobile_phone="0987654321",
Expand Down
22 changes: 8 additions & 14 deletions backend/lcfs/web/api/fuel_code/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,19 +396,8 @@ async def create_fuel_code(self, fuel_code: FuelCode) -> FuelCode:
"""
self.db.add(fuel_code)
await self.db.flush()
await self.db.refresh(
fuel_code,
[
"fuel_code_status",
"fuel_code_prefix",
"fuel_type",
"feedstock_fuel_transport_modes",
"finished_fuel_transport_modes",
],
)
# Manually load nested relationships
await self.db.refresh(fuel_code.fuel_type, ["provision_1", "provision_2"])
return fuel_code
result = await self.get_fuel_code(fuel_code.fuel_code_id)
return result

@repo_handler
async def get_fuel_code(self, fuel_code_id: int) -> FuelCode:
Expand Down Expand Up @@ -593,9 +582,14 @@ async def validate_fuel_code(self, suffix: str, prefix_id: int) -> str:
result = (await self.db.execute(query)).scalar_one_or_none()
if result:
fuel_code_main_version = suffix.split(".")[0]
return await self.get_next_available_sub_version_fuel_code_by_prefix(
suffix = await self.get_next_available_sub_version_fuel_code_by_prefix(
fuel_code_main_version, prefix_id
)
if int(suffix.split(".")[1]) > 9:
return await self.get_next_available_fuel_code_by_prefix(
result.fuel_code_prefix.prefix
)
return suffix
else:
return suffix

Expand Down
4 changes: 4 additions & 0 deletions backend/lcfs/web/api/fuel_code/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ async def convert_to_model(
transport_mode_id=matching_transport_mode.transport_mode_id,
)
)
else:
raise ValueError(f"Invalid transport mode: {transport_mode}")

for transport_mode in fuel_code_schema.finished_fuel_transport_mode or []:
matching_transport_mode = next(
Expand All @@ -221,6 +223,8 @@ async def convert_to_model(
transport_mode_id=matching_transport_mode.transport_mode_id,
)
)
else:
raise ValueError(f"Invalid transport mode: {transport_mode}")

return fuel_code

Expand Down
3 changes: 2 additions & 1 deletion backend/lcfs/web/api/monitoring/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@


@router.get("/health")
def health_check() -> None:
def health_check() -> str:
"""
Checks the health of a project.
It returns 200 if the project is healthy.
"""
return "healthy"
5 changes: 2 additions & 3 deletions backend/lcfs/web/api/user/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ async def create_login_history(self, user: UserProfile):
self.db.add(login_history)

@repo_handler
async def update_notifications_email(
async def update_email(
self, user_profile_id: int, email: str
) -> UserProfile:
# Fetch the user profile
Expand All @@ -679,8 +679,7 @@ async def update_notifications_email(
result = await self.db.execute(query)
user_profile = result.scalar_one_or_none()

# Update the notifications_email field
user_profile.notifications_email = email
user_profile.email = email

# Flush and refresh without committing
await self.db.flush()
Expand Down
5 changes: 2 additions & 3 deletions backend/lcfs/web/api/user/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class UserBaseSchema(BaseSchema):
keycloak_username: str
keycloak_email: EmailStr
email: Optional[EmailStr] = None
notifications_email: Optional[EmailStr] = None
title: Optional[str] = None
phone: Optional[str] = None
first_name: Optional[str] = None
Expand Down Expand Up @@ -97,5 +96,5 @@ class UserLoginHistoryResponseSchema(BaseSchema):
pagination: PaginationResponseSchema


class UpdateNotificationsEmailSchema(BaseSchema):
notifications_email: EmailStr
class UpdateEmailSchema(BaseSchema):
email: EmailStr
9 changes: 3 additions & 6 deletions backend/lcfs/web/api/user/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,12 @@ async def track_user_login(self, user: UserProfile):
await self.repo.create_login_history(user)

@service_handler
async def update_notifications_email(self, user_id: int, email: str):
async def update_email(self, user_id: int, email: str):
try:
# Update the notifications_email field of the user
return await self.repo.update_notifications_email(user_id, email)
# Return the updated user
return UserBaseSchema.model_validate(user)
return await self.repo.update_email(user_id, email)
except DataNotFoundException as e:
logger.error(f"User not found: {e}")
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error updating notifications email: {e}")
logger.error(f"Error updating email: {e}")
raise HTTPException(status_code=500, detail="Internal Server Error")
27 changes: 9 additions & 18 deletions backend/lcfs/web/api/user/views.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
from typing import List

import structlog
from fastapi import (
APIRouter,
Body,
status,
Request,
Response,
Depends,
Query,
HTTPException,
)
from fastapi import APIRouter, Body, status, Request, Response, Depends, Query
from fastapi.responses import StreamingResponse

from lcfs.db import dependencies
Expand All @@ -23,7 +14,7 @@
UserLoginHistoryResponseSchema,
UsersSchema,
UserActivitiesResponseSchema,
UpdateNotificationsEmailSchema,
UpdateEmailSchema,
)
from lcfs.web.api.user.services import UserServices
from lcfs.web.core.decorators import view_handler
Expand Down Expand Up @@ -255,18 +246,18 @@ async def get_all_user_login_history(


@router.post(
"/update-notifications-email",
response_model=UpdateNotificationsEmailSchema,
"/update-email",
response_model=UpdateEmailSchema,
status_code=status.HTTP_200_OK,
)
@view_handler(["*"])
async def update_notifications_email(
async def update_email(
request: Request,
email_data: UpdateNotificationsEmailSchema = Body(...),
email_data: UpdateEmailSchema = Body(...),
service: UserServices = Depends(),
):
user_id = request.user.user_profile_id
email = email_data.notifications_email
email = email_data.email

user = await service.update_notifications_email(user_id, email)
return user
user = await service.update_email(user_id, email)
return UpdateEmailSchema(email=user.email)
19 changes: 9 additions & 10 deletions backend/lcfs/web/application.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
from importlib import metadata
import structlog
import logging
import uuid

import os
import debugpy
import structlog
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import UJSONResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import UJSONResponse
from prometheus_fastapi_instrumentator import Instrumentator
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.authentication import (
AuthenticationBackend,
AuthCredentials,
UnauthenticatedUser,
)
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
import uuid
import contextvars

from lcfs.settings import settings
from lcfs.logging_config import setup_logging, correlation_id_var
from lcfs.web.api.router import api_router
from lcfs.services.keycloak.authentication import UserAuthentication
from lcfs.settings import settings
from lcfs.web.api.router import api_router
from lcfs.web.exception.exception_handler import validation_exception_handler
from lcfs.web.lifetime import register_shutdown_event, register_startup_event

Expand Down Expand Up @@ -67,6 +63,9 @@ async def authenticate(self, request):
if request.scope["method"] == "OPTIONS":
return AuthCredentials([]), UnauthenticatedUser()

if request.url.path == "/api/health": # Skip for health check
return AuthCredentials([]), UnauthenticatedUser()

# Lazily retrieve Redis, session, and settings from app state
redis_client = self.app.state.redis_client
session_factory = self.app.state.db_session_factory
Expand Down
2 changes: 1 addition & 1 deletion backend/lcfs/web/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)

# raise the error to the view layer
except (DatabaseException, HTTPException, DataNotFoundException):
except (DatabaseException, HTTPException, DataNotFoundException, ValueError):
raise
# all other errors that occur in the service layer will log an error
except Exception as e:
Expand Down
Binary file modified etl/database/nifi-registry-primary.mv.db
Binary file not shown.
Binary file modified etl/nifi/conf/flow.json.gz
Binary file not shown.
Binary file modified etl/nifi/conf/flow.xml.gz
Binary file not shown.
Loading

0 comments on commit 96180b9

Please sign in to comment.