Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/send org approval message to creator #2008

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/backend/app/auth/auth_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ async def login_required(
request,
settings.cookie_name, # FMTM cookie
)
print("manage")
return await _authenticate_user(extracted_token)


Expand Down
50 changes: 50 additions & 0 deletions src/backend/app/auth/providers/osm.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import os
from time import time

import requests
from fastapi import Request, Response
from fastapi.exceptions import HTTPException
from loguru import logger as log
from osm_login_python.core import Auth

Expand Down Expand Up @@ -107,3 +109,51 @@ async def handle_osm_callback(request: Request, osm_auth: Auth):
)

return response_plus_cookies


def get_osm_token(request: Request, osm_auth: Auth) -> str:
"""Extract and deserialize OSM token from cookies."""
cookie_name = f"{settings.cookie_name}_osm"
log.debug(f"Extracting OSM token from cookie {cookie_name}")
serialised_osm_token = request.cookies.get(cookie_name)
if not serialised_osm_token:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="You must be logged in to your OpenStreetMap account.",
)
return osm_auth.deserialize_data(serialised_osm_token)


def send_osm_message(
osm_token: str,
title: str,
body: str,
osm_username: str = None,
osm_id: int = None,
) -> None:
"""Send a message via OSM API."""
if not osm_username and not osm_id:
raise ValueError("Either recipient or recipient_id must be provided.")

email_url = f"{settings.OSM_URL}api/0.6/user/messages"
headers = {"Authorization": f"Bearer {osm_token}"}
post_body = {
"title": title,
"body": body,
}

if osm_id:
post_body["recipient_id"] = osm_id
else:
post_body["recipient"] = osm_username

log.debug(
f"Sending message to user ({osm_id or osm_username}) via OSM API: {email_url}"
)
response = requests.post(email_url, headers=headers, data=post_body)

if response.status_code == 200:
log.info("Message sent successfully")
else:
msg = "Sending message via OSM failed"
log.error(f"{msg}: {response.text}")
1 change: 1 addition & 0 deletions src/backend/app/helpers/helper_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ async def send_test_osm_message(

> Notes section
""")
# NOTE post body should contain either recipient or recipient_id
post_body = {
"recipient_id": 16289154,
# "recipient_id": current_user.id,
Expand Down
37 changes: 36 additions & 1 deletion src/backend/app/organisations/organisation_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@
#
"""Logic for organisation management."""

from fastapi import UploadFile
from textwrap import dedent

from fastapi import (
Request,
UploadFile,
)
from loguru import logger as log
from osm_login_python.core import Auth
from psycopg import Connection
from psycopg.rows import class_row

from app.auth.auth_schemas import AuthUser
from app.auth.providers.osm import get_osm_token, send_osm_message
from app.config import settings
from app.db.enums import MappingLevel, UserRole
from app.db.models import DbOrganisation, DbOrganisationManagers, DbUser
Expand Down Expand Up @@ -118,3 +126,30 @@ async def get_my_organisations(
async with db.cursor(row_factory=class_row(OrganisationOut)) as cur:
await cur.execute(sql, {"user_id": current_user.id})
return await cur.fetchall()


async def send_approval_message(
request: Request,
creator_id: int,
organisation_name: str,
osm_auth: Auth,
):
"""Send message to the organisation creator after approval."""
log.info(f"Sending approval message to organisation creator ({creator_id}).")
osm_token = get_osm_token(request, osm_auth)
message_content = dedent(f"""
## Congratulations!

Your organisation **{organisation_name}** has been approved.

You can now manage your organisation freely.

Thank you for being a part of our platform!
""")
send_osm_message(
osm_token=osm_token,
osm_id=creator_id,
title="Your organisation has been approved!",
body=message_content,
)
log.info(f"Approval message sent to organisation creator ({creator_id}).")
19 changes: 18 additions & 1 deletion src/backend/app/organisations/organisation_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

from fastapi import (
APIRouter,
BackgroundTasks,
Depends,
File,
HTTPException,
Request,
Response,
UploadFile,
)
Expand All @@ -32,6 +34,7 @@

from app.auth.auth_deps import login_required
from app.auth.auth_schemas import AuthUser, OrgUserDict
from app.auth.providers.osm import init_osm_auth
from app.auth.roles import org_admin, super_admin
from app.db.database import db_conn
from app.db.enums import HTTPStatus
Expand Down Expand Up @@ -128,13 +131,18 @@ async def delete_unapproved_org(

@router.post("/approve", response_model=OrganisationOut)
async def approve_organisation(
request: Request,
org_id: int,
background_tasks: BackgroundTasks,
db: Annotated[Connection, Depends(db_conn)],
current_user: Annotated[AuthUser, Depends(super_admin)],
osm_auth=Depends(init_osm_auth),
):
"""Approve the organisation request made by the user.

The logged in user must be super admin to perform this action .
The logged in user must be super admin to perform this action.

A background task notifies the organisation creator.
"""
log.info(f"Approving organisation ({org_id}).")
approved_org = await DbOrganisation.update(
Expand All @@ -148,6 +156,15 @@ async def approve_organisation(
db, approved_org.id, approved_org.created_by
)

log.info(f"Approved organisation ({org_id}).")
background_tasks.add_task(
organisation_crud.send_approval_message,
request=request,
creator_id=approved_org.created_by,
organisation_name=approved_org.name,
osm_auth=osm_auth,
)

return approved_org


Expand Down
Loading