Skip to content

Commit 8a3b101

Browse files
committed
Complete refactoring of error handling. Added error deduplication
1 parent 0e3da42 commit 8a3b101

22 files changed

+866
-342
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.11.0
1+
0.12.0

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>0.11.0</Version>
4-
<AssemblyVersion>0.11.0.0</AssemblyVersion>
5-
<FileVersion>0.11.0.0</FileVersion>
3+
<Version>0.12.0</Version>
4+
<AssemblyVersion>0.12.0.0</AssemblyVersion>
5+
<FileVersion>0.12.0.0</FileVersion>
66
</PropertyGroup>
77
</Project>

Neo4jExport/Neo4jExport.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525

2626
<ItemGroup>
2727
<!-- Order matters in F# - files are compiled in the order listed -->
28+
<Compile Include="src/NonEmptyList.fs" />
2829
<Compile Include="src/Types.fs" />
2930
<Compile Include="src/RecordTypes.fs" />
3031
<Compile Include="src/ErrorTracking.fs" />
3132
<Compile Include="src/JsonHelpers.fs" />
3233
<Compile Include="src/Constants.fs" />
34+
<Compile Include="src/ErrorAccumulation.fs" />
3335
<Compile Include="src/AppContext.fs" />
3436
<Compile Include="src/Log.fs" />
3537
<Compile Include="src/LabelStatsTracker.fs" />
@@ -54,6 +56,7 @@
5456
<Compile Include="src/Export/Serialization/GraphElements.fs" />
5557
<Compile Include="src/Export/Serialization/Path.fs" />
5658
<Compile Include="src/Export/Serialization/Engine.fs" />
59+
<Compile Include="src/Export/ErrorDeduplication.fs" />
5760
<Compile Include="src/Export/BatchProcessing.fs" />
5861
<Compile Include="src/Export/Core.fs" />
5962
<Compile Include="src/Export.fs" />

Neo4jExport/src/Cleanup.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ module Cleanup =
5555
with _ ->
5656
"unknown"
5757

58-
Log.warn (sprintf "Failed to terminate process %s: %s" procId ex.Message)
58+
Log.warn (sprintf "Failed to terminate process %s: %s" procId (ErrorAccumulation.exceptionToString ex))
5959

6060
for file in context.TempFiles do
6161
try
6262
if File.Exists(file) then
6363
File.Delete(file)
6464
Log.debug (sprintf "Deleted temp file: %s" file)
6565
with ex ->
66-
Log.warn (sprintf "Failed to delete temp file %s: %s" file ex.Message)
66+
Log.warn (sprintf "Failed to delete temp file %s: %s" file (ErrorAccumulation.exceptionToString ex))
6767

6868
Log.info "Cleanup completed"

Neo4jExport/src/Configuration.fs

Lines changed: 82 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace Neo4jExport
2424

2525
open System
2626
open Constants
27+
open ErrorAccumulation
2728

2829
module Configuration =
2930
module private SafeParsing =
@@ -47,25 +48,6 @@ module Configuration =
4748
| "0" -> Ok false
4849
| _ -> Error(sprintf "%s must be a valid boolean (true/false, yes/no, 1/0) (got: '%s')" name value)
4950

50-
51-
let private appErrorToString =
52-
function
53-
| ConfigError msg -> msg
54-
| SecurityError msg -> msg
55-
| FileSystemError(path, msg, _) -> sprintf "%s: %s" path msg
56-
| ConnectionError(msg, _) -> msg
57-
| AuthenticationError msg -> msg
58-
| QueryError(_, msg, _) -> msg
59-
| DataCorruptionError(line, msg, _) -> sprintf "Line %d: %s" line msg
60-
| DiskSpaceError(required, available) ->
61-
sprintf
62-
"Insufficient disk space: required %d GB, available %d GB"
63-
(required / 1073741824L)
64-
(available / 1073741824L)
65-
| MemoryError msg -> msg
66-
| ExportError(msg, _) -> msg
67-
| TimeoutError(operation, duration) -> sprintf "Operation '%s' timed out after %A" operation duration
68-
6951
let private validateUri uriStr =
7052
try
7153
let uri = Uri(uriStr)
@@ -80,7 +62,7 @@ module Configuration =
8062
else
8163
Result.Ok uri
8264
with ex ->
83-
Result.Error(sprintf "Invalid URI: %s" ex.Message)
65+
Result.Error(sprintf "Invalid URI: %s" (ErrorAccumulation.exceptionToString ex))
8466

8567
let private validateOutputDirectory path =
8668
// IMPORTANT: This function intentionally creates the directory during configuration validation.
@@ -344,85 +326,84 @@ module Configuration =
344326
MaxLabelsInReferenceMode = labelsRef
345327
MaxLabelsInPathCompact = labelsCompact }
346328
| _ ->
347-
// Collect all errors from the Results
348-
let errors =
349-
[ match uriResult with
350-
| Error e -> Some e
351-
| _ -> None
352-
match outputResult with
353-
| Error e -> Some(appErrorToString e)
354-
| _ -> None
355-
match minDiskGb with
356-
| Error e -> Some e
357-
| _ -> None
358-
match maxMemoryMb with
359-
| Error e -> Some e
360-
| _ -> None
361-
match skipSchema with
362-
| Error e -> Some e
363-
| _ -> None
364-
match maxRetries with
365-
| Error e -> Some e
366-
| _ -> None
367-
match retryDelayMs with
368-
| Error e -> Some e
369-
| _ -> None
370-
match maxRetryDelayMs with
371-
| Error e -> Some e
372-
| _ -> None
373-
match queryTimeout with
374-
| Error e -> Some e
375-
| _ -> None
376-
match enableDebug with
377-
| Error e -> Some e
378-
| _ -> None
379-
match validateJson with
380-
| Error e -> Some e
381-
| _ -> None
382-
match allowInsecure with
383-
| Error e -> Some e
384-
| _ -> None
385-
match batchSize with
386-
| Error e -> Some e
387-
| _ -> None
388-
match jsonBufferSizeKb with
389-
| Error e -> Some e
390-
| _ -> None
391-
match maxPathLength with
392-
| Error e -> Some e
393-
| _ -> None
394-
match pathFullModeLimit with
395-
| Error e -> Some e
396-
| _ -> None
397-
match pathCompactModeLimit with
398-
| Error e -> Some e
399-
| _ -> None
400-
match pathPropertyDepth with
401-
| Error e -> Some e
402-
| _ -> None
403-
match maxNestedDepth with
404-
| Error e -> Some e
405-
| _ -> None
406-
match nestedShallowModeDepth with
407-
| Error e -> Some e
408-
| _ -> None
409-
match nestedReferenceModeDepth with
410-
| Error e -> Some e
411-
| _ -> None
412-
match maxCollectionItems with
413-
| Error e -> Some e
414-
| _ -> None
415-
match maxLabelsPerNode with
416-
| Error e -> Some e
417-
| _ -> None
418-
match maxLabelsInReferenceMode with
419-
| Error e -> Some e
420-
| _ -> None
421-
match maxLabelsInPathCompact with
422-
| Error e -> Some e
423-
| _ -> None ]
424-
|> List.choose id
425-
426-
Result.Error(ConfigError(String.concat "; " errors))
329+
// Collect all Result values, converting string errors to ConfigError
330+
let allResults: Result<obj, AppError> list =
331+
[ uriResult
332+
|> Result.mapError ConfigError
333+
|> Result.map box
334+
outputResult |> Result.map box
335+
minDiskGb
336+
|> Result.mapError ConfigError
337+
|> Result.map box
338+
maxMemoryMb
339+
|> Result.mapError ConfigError
340+
|> Result.map box
341+
skipSchema
342+
|> Result.mapError ConfigError
343+
|> Result.map box
344+
maxRetries
345+
|> Result.mapError ConfigError
346+
|> Result.map box
347+
retryDelayMs
348+
|> Result.mapError ConfigError
349+
|> Result.map box
350+
maxRetryDelayMs
351+
|> Result.mapError ConfigError
352+
|> Result.map box
353+
queryTimeout
354+
|> Result.mapError ConfigError
355+
|> Result.map box
356+
enableDebug
357+
|> Result.mapError ConfigError
358+
|> Result.map box
359+
validateJson
360+
|> Result.mapError ConfigError
361+
|> Result.map box
362+
allowInsecure
363+
|> Result.mapError ConfigError
364+
|> Result.map box
365+
batchSize
366+
|> Result.mapError ConfigError
367+
|> Result.map box
368+
jsonBufferSizeKb
369+
|> Result.mapError ConfigError
370+
|> Result.map box
371+
maxPathLength
372+
|> Result.mapError ConfigError
373+
|> Result.map box
374+
pathFullModeLimit
375+
|> Result.mapError ConfigError
376+
|> Result.map box
377+
pathCompactModeLimit
378+
|> Result.mapError ConfigError
379+
|> Result.map box
380+
pathPropertyDepth
381+
|> Result.mapError ConfigError
382+
|> Result.map box
383+
maxNestedDepth
384+
|> Result.mapError ConfigError
385+
|> Result.map box
386+
nestedShallowModeDepth
387+
|> Result.mapError ConfigError
388+
|> Result.map box
389+
nestedReferenceModeDepth
390+
|> Result.mapError ConfigError
391+
|> Result.map box
392+
maxCollectionItems
393+
|> Result.mapError ConfigError
394+
|> Result.map box
395+
maxLabelsPerNode
396+
|> Result.mapError ConfigError
397+
|> Result.map box
398+
maxLabelsInReferenceMode
399+
|> Result.mapError ConfigError
400+
|> Result.map box
401+
maxLabelsInPathCompact
402+
|> Result.mapError ConfigError
403+
|> Result.map box ]
404+
405+
match ErrorAccumulation.fromResults allResults with
406+
| None -> failwith "Validation logic error: should have at least one error"
407+
| Some acc -> Result.Error(ErrorAccumulation.toConfigError acc)
427408
with ex ->
428-
Result.Error(ConfigError(sprintf "Invalid configuration: %s" ex.Message))
409+
Result.Error(ConfigError(sprintf "Invalid configuration: %s" (ErrorAccumulation.exceptionToString ex)))

0 commit comments

Comments
 (0)