1- from typing import List , Optional
1+ from typing import Optional
22
3- from app .deps .authorization_deps import ListenerAuthorization
3+ from app .deps .authorization_deps import FeedAuthorization , ListenerAuthorization
44from app .keycloak_auth import get_current_user , get_current_username
55from app .models .feeds import FeedDB , FeedIn , FeedOut
66from app .models .files import FileOut
77from app .models .listeners import EventListenerDB , FeedListener
8+ from app .models .pages import Paged , _construct_page_metadata , _get_page_query
89from app .models .users import UserOut
910from app .rabbitmq .listeners import submit_file_job
1011from app .routers .authentication import get_admin , get_admin_mode
1112from app .search .connect import check_search_result
1213from beanie import PydanticObjectId
14+ from beanie .operators import Or , RegEx
1315from fastapi import APIRouter , Depends , HTTPException
1416from pika .adapters .blocking_connection import BlockingChannel
1517
1618router = APIRouter ()
1719
1820
1921# TODO: Move this to MongoDB middle layer
20- async def disassociate_listener_db (feed_id : str , listener_id : str ):
22+ async def disassociate_listener_db (
23+ feed_id : str , listener_id : str , allows : bool = Depends (FeedAuthorization ())
24+ ):
2125 """Remove a specific Event Listener from a feed. Does not delete either resource, just removes relationship.
2226
2327 This actually performs the database operations, and can be used by any endpoints that need this functionality.
@@ -71,34 +75,90 @@ async def save_feed(
7175 return feed .dict ()
7276
7377
74- @router .get ("" , response_model = List [FeedOut ])
78+ @router .put ("/{feed_id}" , response_model = FeedOut )
79+ async def edit_feed (
80+ feed_id : str ,
81+ feed_in : FeedIn ,
82+ user = Depends (get_current_username ),
83+ allow : bool = Depends (FeedAuthorization ()),
84+ ):
85+ """Update the information about an existing Feed..
86+
87+ Arguments:
88+ feed_id -- UUID of the feed to be udpated
89+ feed_in -- JSON object including updated information
90+ """
91+ feed = await FeedDB .get (PydanticObjectId (feed_id ))
92+ if feed :
93+ # TODO: Refactor this with permissions checks etc.
94+ feed_update = feed_in .dict ()
95+ if (
96+ not feed_update ["name" ]
97+ or not feed_update ["search" ]
98+ or len (feed_update ["listeners" ]) == 0
99+ ):
100+ raise HTTPException (
101+ status_code = 400 ,
102+ detail = "Feed name/search/listeners can't be null or empty" ,
103+ )
104+ return
105+ feed .description = feed_update ["description" ]
106+ feed .name = feed_update ["name" ]
107+ feed .search = feed_update ["search" ]
108+ feed .listeners = feed_update ["listeners" ]
109+ try :
110+ await feed .save ()
111+ return feed .dict ()
112+ except Exception as e :
113+ raise HTTPException (status_code = 500 , detail = e .args [0 ])
114+ raise HTTPException (status_code = 404 , detail = f"listener { feed_id } not found" )
115+
116+
117+ @router .get ("" , response_model = Paged )
75118async def get_feeds (
76- name : Optional [str ] = None ,
119+ searchTerm : Optional [str ] = None ,
77120 user = Depends (get_current_user ),
78121 skip : int = 0 ,
79122 limit : int = 10 ,
123+ admin = Depends (get_admin ),
124+ admin_mode = Depends (get_admin_mode ),
80125):
81126 """Fetch all existing Feeds."""
82- if name is not None :
83- feeds = (
84- await FeedDB .find (FeedDB .name == name )
85- .sort (- FeedDB .created )
86- .skip (skip )
87- .limit (limit )
88- .to_list ()
89- )
90- else :
91- feeds = (
92- await FeedDB .find ().sort (- FeedDB .created ).skip (skip ).limit (limit ).to_list ()
127+ criteria_list = []
128+ if not admin or not admin_mode :
129+ criteria_list .append (FeedDB .creator == user .email )
130+ if searchTerm is not None :
131+ criteria_list .append (
132+ Or (
133+ RegEx (field = FeedDB .name , pattern = searchTerm , options = "i" ),
134+ RegEx (field = FeedDB .description , pattern = searchTerm , options = "i" ),
135+ )
93136 )
94137
95- return [feed .dict () for feed in feeds ]
138+ feeds_and_count = (
139+ await FeedDB .find (
140+ * criteria_list ,
141+ )
142+ .aggregate (
143+ [_get_page_query (skip , limit , sort_field = "created" , ascending = False )],
144+ )
145+ .to_list ()
146+ )
147+ page_metadata = _construct_page_metadata (feeds_and_count , skip , limit )
148+ page = Paged (
149+ metadata = page_metadata ,
150+ data = [
151+ FeedOut (id = item .pop ("_id" ), ** item ) for item in feeds_and_count [0 ]["data" ]
152+ ],
153+ )
154+ return page .dict ()
96155
97156
98157@router .get ("/{feed_id}" , response_model = FeedOut )
99158async def get_feed (
100159 feed_id : str ,
101160 user = Depends (get_current_user ),
161+ allow : bool = Depends (FeedAuthorization ()),
102162):
103163 """Fetch an existing saved search Feed."""
104164 if (feed := await FeedDB .get (PydanticObjectId (feed_id ))) is not None :
@@ -107,15 +167,16 @@ async def get_feed(
107167 raise HTTPException (status_code = 404 , detail = f"Feed { feed_id } not found" )
108168
109169
110- @router .delete ("/{feed_id}" )
170+ @router .delete ("/{feed_id}" , response_model = FeedOut )
111171async def delete_feed (
112172 feed_id : str ,
113173 user = Depends (get_current_user ),
174+ allow : bool = Depends (FeedAuthorization ()),
114175):
115176 """Delete an existing saved search Feed."""
116177 if (feed := await FeedDB .get (PydanticObjectId (feed_id ))) is not None :
117178 await feed .delete ()
118- return { "deleted" : feed_id }
179+ return feed . dict ()
119180 raise HTTPException (status_code = 404 , detail = f"Feed { feed_id } not found" )
120181
121182
@@ -126,6 +187,7 @@ async def associate_listener(
126187 user = Depends (get_current_user ),
127188 admin = Depends (get_admin ),
128189 admin_mode = Depends (get_admin_mode ),
190+ allow : bool = Depends (FeedAuthorization ()),
129191):
130192 """Associate an existing Event Listener with a Feed, e.g. so it will be triggered on new Feed results.
131193
0 commit comments