Skip to content

Commit 27c9655

Browse files
Release notifications endpoints (#353)
* Release notifications endpoints * Added create release notification and email on display * Update and delete release notifications * Json validation * Small fix returning the result * small fix * display notifications in reverse order * PR comments * submodules merge
1 parent ad42cfc commit 27c9655

File tree

5 files changed

+140
-2
lines changed

5 files changed

+140
-2
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Release notifications table
2+
3+
Revision ID: 24ca8432bd8b
4+
Revises: 9e606cf1f902
5+
Create Date: 2025-10-01 08:49:37.899217
6+
7+
"""
8+
9+
from alembic import op
10+
import sqlalchemy as sa
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = "24ca8432bd8b"
15+
down_revision = "9e606cf1f902"
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table(
23+
"release_notification",
24+
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
25+
sa.Column("created_at", sa.DateTime(), nullable=True),
26+
sa.Column("created_by", postgresql.UUID(as_uuid=True), nullable=True),
27+
sa.Column("link", sa.String(), nullable=False),
28+
sa.Column("config", sa.JSON(), nullable=True),
29+
sa.ForeignKeyConstraint(["created_by"], ["user.id"], ondelete="SET NULL"),
30+
sa.PrimaryKeyConstraint("id"),
31+
schema="global",
32+
)
33+
op.create_index(
34+
op.f("ix_global_release_notification_created_by"),
35+
"release_notification",
36+
["created_by"],
37+
unique=False,
38+
schema="global",
39+
)
40+
# ### end Alembic commands ###
41+
42+
43+
def downgrade():
44+
# ### commands auto generated by Alembic - please adjust! ###
45+
op.drop_index(
46+
op.f("ix_global_release_notification_created_by"),
47+
table_name="release_notification",
48+
schema="global",
49+
)
50+
op.drop_table("release_notification", schema="global")
51+
# ### end Alembic commands ###

controller/organization/manager.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,27 @@ def sync_organization_sharepoint_integrations(org_id: str) -> None:
128128
general.remove_and_refresh_session()
129129
for integration_id in all_integration_ids:
130130
transfer_api.post_process_integration(integration_id)
131+
132+
133+
def validate_json_release_notification(data: Dict[str, Any]) -> Dict[str, Any]:
134+
required_languages = ["en", "de", "nl", "it"]
135+
required_fields = ["headline", "description"]
136+
137+
for lang in required_languages:
138+
if lang not in data:
139+
return {"message": f"Missing language: {lang}", "is_valid": False}
140+
141+
for lang in required_languages:
142+
for field in required_fields:
143+
if field not in data[lang]:
144+
return {
145+
"message": f"Missing field '{field}' in language '{lang}'",
146+
"is_valid": False,
147+
}
148+
if not isinstance(data[lang][field], str):
149+
return {
150+
"message": f"Field '{field}' in '{lang}' is not a string",
151+
"is_valid": False,
152+
}
153+
154+
return {"message": "", "is_valid": True}

fast_api/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,3 +531,8 @@ class GetEmbeddingNameBody(BaseModel):
531531
embedding_type: str
532532
model: Optional[str] = None
533533
api_token_env_name: Optional[str] = None
534+
535+
536+
class CreateUpdateReleaseNotificationBody(BaseModel):
537+
link: StrictStr
538+
config: Dict[str, Any]

fast_api/routes/organization.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ChangeUserRoleBody,
99
CreateAdminMessageBody,
1010
CreateOrganizationBody,
11+
CreateUpdateReleaseNotificationBody,
1112
DeleteOrganizationBody,
1213
DeleteUserBody,
1314
MappedSortedPaginatedUsers,
@@ -16,6 +17,7 @@
1617
)
1718
from controller.auth import manager as auth_manager
1819
from controller.auth.kratos import (
20+
resolve_user_mail_by_id,
1921
resolve_user_name_by_id,
2022
)
2123
from controller.organization import manager
@@ -24,7 +26,7 @@
2426
from controller.user import manager as user_manager
2527

2628
from fast_api.routes.client_response import get_silent_success, pack_json_result
27-
from submodules.model.business_objects import organization, user
29+
from submodules.model.business_objects import organization, release_notification, user
2830
from submodules.model.util import sql_alchemy_to_dict
2931
from util import notification
3032

@@ -64,6 +66,7 @@
6466
"file_lifespan_days",
6567
"token_limit",
6668
}
69+
RELEASE_NOTIFICATIONS_WHITELIST = {"id", "link", "config"}
6770

6871

6972
# in use refinery-ui (07.01.25)
@@ -314,3 +317,58 @@ def get_user_to_organization(request: Request):
314317
auth_manager.check_admin_access(request.state.info)
315318
data = user.get_user_to_organization()
316319
return pack_json_result(data, wrap_for_frontend=False)
320+
321+
322+
# in use admin-dashboard (01.10.25)
323+
@router.get("/all-release-notifications-admin")
324+
def get_all_release_notifications(request: Request):
325+
auth_manager.check_admin_access(request.state.info)
326+
data = sql_alchemy_to_dict(release_notification.get_all())
327+
for item in data:
328+
item["createdByEmail"] = resolve_user_mail_by_id(item["created_by"])
329+
return pack_json_result(data)
330+
331+
332+
# in use admin-dashboard (08.10.25)
333+
@router.get("/release-notifications")
334+
def get_release_notifications(request: Request):
335+
data = sql_alchemy_to_dict(
336+
release_notification.get_all(),
337+
column_whitelist=RELEASE_NOTIFICATIONS_WHITELIST,
338+
)
339+
return pack_json_result(data)
340+
341+
342+
# in use admin-dashboard (01.10.25)
343+
@router.post("/create-release-notification")
344+
def create_release_notification(
345+
request: Request, body: CreateUpdateReleaseNotificationBody = Body(...)
346+
):
347+
auth_manager.check_admin_access(request.state.info)
348+
user_id = auth_manager.get_user_id_by_info(request.state.info)
349+
validate_result = manager.validate_json_release_notification(body.config)
350+
if validate_result["is_valid"]:
351+
release_notification.create(body.link, body.config, user_id, with_commit=True)
352+
return pack_json_result(validate_result, wrap_for_frontend=False)
353+
354+
355+
# in use admin-dashboard (02.10.25)
356+
@router.put("/update-release-notification/{notification_id}")
357+
def update_release_notification(
358+
request: Request,
359+
notification_id: str,
360+
body: CreateUpdateReleaseNotificationBody = Body(...),
361+
):
362+
auth_manager.check_admin_access(request.state.info)
363+
release_notification.update(
364+
notification_id, body.link, body.config, with_commit=True
365+
)
366+
return get_silent_success()
367+
368+
369+
# in use admin-dashboard (02.10.25)
370+
@router.delete("/delete-release-notification/{notification_id}")
371+
def delete_release_notification(request: Request, notification_id: str):
372+
auth_manager.check_admin_access(request.state.info)
373+
release_notification.delete(notification_id, with_commit=True)
374+
return get_silent_success()

0 commit comments

Comments
 (0)