55
66import logging
77from typing import Annotated
8- from uuid import UUID
98
109import yaml
1110from fastapi import APIRouter , Body , Depends , File , UploadFile , status
1413from fastapi .responses import FileResponse , Response
1514from pydantic import ValidationError
1615
17- from app .api .dependencies import get_source_id , get_source_update_service
18- from app .schemas import Source , SourceCreate
19- from app .schemas . source import SourceCreateAdapter
16+ from app .api .dependencies import get_source , get_source_update_service
17+ from app .api . schemas . source import SourceCreate , SourceCreateAdapter , SourceView , SourceViewAdapter
18+ from app .models import Source
2019from app .services import (
2120 ResourceInUseError ,
2221 ResourceNotFoundError ,
9897@router .post (
9998 "" ,
10099 status_code = status .HTTP_201_CREATED ,
101- response_model = Source ,
100+ response_model = SourceView ,
102101 responses = {
103102 status .HTTP_201_CREATED : {"description" : "Source created" },
104103 status .HTTP_400_BAD_REQUEST : {"description" : "Invalid source ID or request body" },
@@ -110,57 +109,58 @@ def create_source(
110109 SourceCreate , Body (description = CREATE_SOURCE_BODY_DESCRIPTION , openapi_examples = CREATE_SOURCE_BODY_EXAMPLES )
111110 ],
112111 source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
113- ) -> Source :
112+ ) -> SourceView :
114113 """Create and configure a new source"""
115114 try :
116- return source_update_service .create (source_create )
115+ source = source_update_service .create_source (
116+ name = source_create .name ,
117+ source_type = source_create .source_type ,
118+ config_data = source_create .config_data ,
119+ source_id = source_create .id ,
120+ )
121+ return SourceViewAdapter .validate_python (source , from_attributes = True )
117122 except (ResourceWithNameAlreadyExistsError , ResourceWithIdAlreadyExistsError ) as e :
118123 raise HTTPException (status_code = status .HTTP_409_CONFLICT , detail = str (e ))
119124
120125
121126@router .get (
122127 "" ,
123128 responses = {
124- status .HTTP_200_OK : {"description" : "List of available source configurations" , "model" : list [Source ]},
129+ status .HTTP_200_OK : {"description" : "List of available source configurations" , "model" : list [SourceView ]},
125130 },
126131)
127132def list_sources (
128133 source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
129- ) -> list [Source ]:
134+ ) -> list [SourceView ]:
130135 """List the available sources"""
131- return source_update_service .list_all ()
136+ sources = source_update_service .list_all ()
137+ return [SourceViewAdapter .validate_python (source , from_attributes = True ) for source in sources ]
132138
133139
134140@router .get (
135141 "/{source_id}" ,
136142 responses = {
137- status .HTTP_200_OK : {"description" : "Source found" , "model" : Source },
143+ status .HTTP_200_OK : {"description" : "Source found" , "model" : SourceView },
138144 status .HTTP_400_BAD_REQUEST : {"description" : "Invalid source ID" },
139145 status .HTTP_404_NOT_FOUND : {"description" : "Source not found" },
140146 },
141147)
142- def get_source (
143- source_id : Annotated [UUID , Depends (get_source_id )],
144- source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
145- ) -> Source :
148+ def get_source_view (source : Annotated [Source , Depends (get_source )]) -> SourceView :
146149 """Get info about a source"""
147- try :
148- return source_update_service .get_by_id (source_id )
149- except ResourceNotFoundError as e :
150- raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = str (e ))
150+ return SourceViewAdapter .validate_python (source , from_attributes = True )
151151
152152
153153@router .patch (
154154 "/{source_id}" ,
155155 responses = {
156- status .HTTP_200_OK : {"description" : "Source successfully updated" , "model" : Source },
156+ status .HTTP_200_OK : {"description" : "Source successfully updated" , "model" : SourceView },
157157 status .HTTP_400_BAD_REQUEST : {"description" : "Invalid source ID or request body" },
158158 status .HTTP_404_NOT_FOUND : {"description" : "Source not found" },
159159 status .HTTP_409_CONFLICT : {"description" : "Source already exists" },
160160 },
161161)
162162def update_source (
163- source_id : Annotated [UUID , Depends (get_source_id )],
163+ source : Annotated [Source , Depends (get_source )],
164164 source_config : Annotated [
165165 dict ,
166166 Body (
@@ -169,13 +169,20 @@ def update_source(
169169 ),
170170 ],
171171 source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
172- ) -> Source :
172+ ) -> SourceView :
173173 """Reconfigure an existing source"""
174174 if "source_type" in source_config :
175175 raise HTTPException (status_code = status .HTTP_400_BAD_REQUEST , detail = "The 'source_type' field cannot be changed" )
176+
176177 try :
177- source = source_update_service .get_by_id (source_id )
178- return source_update_service .update (source , source_config )
178+ updated_source : Source = source .model_copy (update = source_config )
179+ updated_source .config_data = updated_source .config_data .model_copy (update = source_config )
180+ source = source_update_service .update_source (
181+ source = source ,
182+ new_name = updated_source .name ,
183+ new_config_data = updated_source .config_data ,
184+ )
185+ return SourceViewAdapter .validate_python (source , from_attributes = True )
179186 except ResourceNotFoundError as e :
180187 raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = str (e ))
181188 except ResourceWithNameAlreadyExistsError as e :
@@ -196,29 +203,22 @@ def update_source(
196203 status .HTTP_404_NOT_FOUND : {"description" : "Source not found" },
197204 },
198205)
199- def export_source (
200- source_id : Annotated [UUID , Depends (get_source_id )],
201- source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
202- ) -> Response :
206+ def export_source (source : Annotated [Source , Depends (get_source )]) -> Response :
203207 """Export a source to file"""
204- source = source_update_service .get_by_id (source_id )
205- if not source :
206- raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = f"Source with ID { source_id } not found" )
207-
208208 yaml_content = yaml .safe_dump (source .model_dump (mode = "json" , exclude = {"id" }))
209209
210210 return Response (
211211 content = yaml_content .encode ("utf8" ),
212212 media_type = "application/x-yaml" ,
213- headers = {"Content-Disposition" : f"attachment; filename=source_{ source_id } .yaml" },
213+ headers = {"Content-Disposition" : f"attachment; filename=source_{ source . id } .yaml" },
214214 )
215215
216216
217217@router .post (
218218 ":import" ,
219219 status_code = status .HTTP_201_CREATED ,
220220 responses = {
221- status .HTTP_201_CREATED : {"description" : "Source imported successfully" , "model" : Source },
221+ status .HTTP_201_CREATED : {"description" : "Source imported successfully" , "model" : SourceView },
222222 status .HTTP_400_BAD_REQUEST : {"description" : "Invalid YAML format " },
223223 status .HTTP_409_CONFLICT : {"description" : "Source already exists" },
224224 status .HTTP_422_UNPROCESSABLE_ENTITY : {"description" : "Validation error(s)" },
@@ -227,13 +227,19 @@ def export_source(
227227def import_source (
228228 yaml_file : Annotated [UploadFile , File (description = "YAML file containing the source configuration" )],
229229 source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
230- ) -> Source :
230+ ) -> SourceView :
231231 """Import a source from file"""
232232 try :
233233 yaml_content = yaml_file .file .read ()
234234 source_data = yaml .safe_load (yaml_content )
235235 source_create = SourceCreateAdapter .validate_python (source_data )
236- return source_update_service .create (source_create )
236+ source = source_update_service .create_source (
237+ name = source_create .name ,
238+ source_type = source_create .source_type ,
239+ config_data = source_create .config_data ,
240+ source_id = source_create .id ,
241+ )
242+ return SourceViewAdapter .validate_python (source , from_attributes = True )
237243 except yaml .YAMLError as e :
238244 raise HTTPException (status_code = status .HTTP_400_BAD_REQUEST , detail = f"Invalid YAML format: { str (e )} " )
239245 except (ResourceWithNameAlreadyExistsError , ResourceWithIdAlreadyExistsError ) as e :
@@ -255,12 +261,12 @@ def import_source(
255261 },
256262)
257263def delete_source (
258- source_id : Annotated [UUID , Depends (get_source_id )],
264+ source : Annotated [Source , Depends (get_source )],
259265 source_update_service : Annotated [SourceUpdateService , Depends (get_source_update_service )],
260266) -> None :
261267 """Remove a source"""
262268 try :
263- source_update_service .delete_by_id ( source_id )
269+ source_update_service .delete_source ( source )
264270 except ResourceNotFoundError as e :
265271 raise HTTPException (status_code = status .HTTP_404_NOT_FOUND , detail = str (e ))
266272 except ResourceInUseError as e :
0 commit comments