@@ -84,6 +84,16 @@ defmodule Transport.IRVE.Consolidation do
8484 process_resource ( resource )
8585 rescue
8686 error ->
87+ Logger . error (
88+ "IRVE consolidation: unexpected error for resource #{ resource . resource_id } (#{ resource . url } )\n " <>
89+ Exception . format ( :error , error , __STACKTRACE__ )
90+ )
91+
92+ Sentry . capture_exception ( error ,
93+ stacktrace: __STACKTRACE__ ,
94+ extra: % { resource_id: resource . resource_id , url: resource . url }
95+ )
96+
8797 { :error_occurred , error , resource }
8898 end
8999
@@ -105,28 +115,18 @@ defmodule Transport.IRVE.Consolidation do
105115 |> Map . put ( :estimated_pdc_count , estimated_pdc_count )
106116 |> Map . put ( :file_extension , extension )
107117
108- # The code is convoluted mostly because we didn't go far enough on the validator work.
109- # The validator will ultimately stop raising exceptions, and will instead return structures.
110- # But currently if a cheap check fails, an exception is thrown, and we would lose the estimated PDC count,
111- # something which is essential to report on for our current work.
112- try do
113- # Raise if the producer is not an organization. This check is not in the validator itself:
114- # it’s not linked to the file content/format, but to how it is published on data.gouv.fr.
115- # it is done after downloading the file in order to be able to report on the potential
116- # loss of PDC count.
117- ensure_producer_is_org! ( resource )
118-
119- validation_result = Transport.IRVE.Validator . validate ( path , extension )
120- file_valid? = validation_result |> Transport.IRVE.Validator . full_file_valid? ( )
121-
122- if file_valid? do
123- { Transport.IRVE.DatabaseImporter . try_write_to_db ( path , resource ) , resource }
124- else
125- { :not_compliant_with_schema , resource }
126- end
127- rescue
128- error ->
129- { :error_occurred , error , resource }
118+ # This producer_is_org_check is not in the validator itself:
119+ # it’s not linked to the file content/format, but to how it is published on data.gouv.fr.
120+ # it is done after downloading the file in order to be able to report on the potential
121+ # loss of PDC count.
122+ with :producer_is_an_organization <- producer_is_org ( resource ) ,
123+ % { valid: true } <- Transport.IRVE.Validator . validate_and_summarize ( path , extension ) ,
124+ import_status <- Transport.IRVE.DatabaseImporter . try_write_to_db ( path , resource ) do
125+ { import_status , resource }
126+ else
127+ :producer_not_an_organization -> { :producer_not_an_organization , resource }
128+ % { file_level_errors: [ _ | _ ] = errors } -> { :file_level_errors , resource , errors }
129+ % { file_level_errors: [ ] } -> { :not_compliant_with_schema , resource }
130130 end
131131 end )
132132 end
@@ -208,31 +208,38 @@ defmodule Transport.IRVE.Consolidation do
208208 # regular workflow: process the file then delete it afterwards, no matter what, to ensure
209209 # the files do not stack up on the production disk.
210210 def with_maybe_cached_download_on_disk ( resource , file_path , extension , false = _use_permanent_disk_cache , work_fn ) do
211- download! ( resource . resource_id , resource . url , file_path )
212- # NOTE: we need to pass the original extension (provided in the URL) because some heuristics use it afterwards.
213- # but the caching mechanism stores everything under the same `.dat` extension (so the file path is not enough
214- # to keep the extension around)
215- work_fn . ( file_path , extension )
211+ case download ( resource . resource_id , resource . url , file_path ) do
212+ # NOTE: we need to pass the original extension (provided in the URL) because some heuristics use it afterwards.
213+ # but the caching mechanism stores everything under the same `.dat` extension (so the file path is not enough
214+ # to keep the extension around)
215+ :ok -> work_fn . ( file_path , extension )
216+ { :error , message } -> { :download_failed , resource , message }
217+ end
216218 after
217219 File . rm! ( file_path )
218220 end
219221
220222 # variant for dev work, where it is important to support permanent disk caching (fully offline, no etag)
221223 def with_maybe_cached_download_on_disk ( resource , file_path , extension , true = _use_permanent_disk_cache , work_fn ) do
222- if ! File . exists? ( file_path ) , do: download! ( resource . resource_id , resource . url , file_path )
223- work_fn . ( file_path , extension )
224+ download_result =
225+ if File . exists? ( file_path ) , do: :ok , else: download ( resource . resource_id , resource . url , file_path )
226+
227+ case download_result do
228+ :ok -> work_fn . ( file_path , extension )
229+ { :error , message } -> { :download_failed , resource , message }
230+ end
224231 end
225232
226- def download! ( resource_id , url , file ) do
233+ # Returns `:ok` on success, or `{:error, message}` for a non-200 response.
234+ # Timeouts / transport errors still raise for now (caught upstream as `:error_occurred`).
235+ def download ( resource_id , url , file ) do
227236 Logger . info ( "Processing resource #{ resource_id } (url=#{ url } )" )
228237 % { status: status } = Transport.HTTPClient . get! ( url , compressed: false , decode_body: false , into: File . stream! ( file ) )
229238
230- unless status == 200 do
231- raise "Error processing resource (#{ resource_id } ) (http_status=#{ status } )"
232- end
239+ if status == 200 , do: :ok , else: { :error , "http_status=#{ status } " }
233240 end
234241
235- defp ensure_producer_is_org! ( % { dataset_organisation_id: "???" } ) , do: raise ( "producer is not an organization" )
236-
237- defp ensure_producer_is_org! ( _row ) , do: :ok
242+ defp producer_is_org ( % { dataset_organisation_id: org_id } ) do
243+ if org_id == "???" , do: :producer_not_an_organization , else: :producer_is_an_organization
244+ end
238245end
0 commit comments