# IngestEbookCommandHandler

- File ID: file:lqr4h8:ingestebookcommandhandler-cs
- Path: NexusReader.Application/Commands/Library/IngestEbookCommandHandler.cs
- Role: handler
- Layer: application

## Purpose

Handles ingestion of an uploaded ebook: saves files to storage, resolves/creates author, creates Ebook entity and persists it to the repository, returning the new Ebook Id or a failure result.

## Key Details

Two-phase ingestion: first persist files to storage (with I/O error mapping), then create/resolve author and persist Ebook in DB (with DB error mapping). Uses MediatR IRequestHandler and repository/service abstractions to keep side effects encapsulated.

## Symbols

- IngestEbookCommandHandler: Handles ingestion of an uploaded ebook: saves files to storage, resolves/creates author, creates Ebook entity and persists it to the repository, returning the new Ebook Id or a failure result.
- IngestEbookCommandHandler(): Constructor; receives repository and storage service via DI and assigns to fields.
- Handle(): Orchestrates ebook ingestion: saves epub/cover to storage, resolves or creates author, constructs Ebook entity, persists it and returns the created Ebook Id or failure.

## Dependencies

- No resolved dependencies.

## Calls

- No resolved calls.

## Unresolved References

- dependency-target: NexusReader.Application.Abstractions.Persistence/IEbookRepository.cs
- dependency-target: NexusReader.Application.Abstractions.Services/IBookStorageService.cs
- dependency-target: NexusReader.Domain.Entities/Ebook.cs
- dependency-target: NexusReader.Domain.Entities/Author.cs
- call-target: NexusReader.Application.Abstractions.Services/IBookStorageService.cs
- call-target: NexusReader.Application.Abstractions.Services/IBookStorageService.cs
- call-target: NexusReader.Application.Abstractions.Persistence/IEbookRepository.cs
- call-target: NexusReader.Application.Abstractions.Persistence/IEbookRepository.cs
- call-target: NexusReader.Application.Abstractions.Persistence/IEbookRepository.cs
- call-target: NexusReader.Application.Abstractions.Persistence/IEbookRepository.cs
- interaction-target: var author = await _ebookRepository.FindAuthorByNameAsync(authorName, cancellationToken);
- interaction-target: Calls AddEbook and SaveChangesAsync to persist the new entity

## Coverage

- Symbols: 3
- Executable symbols: 2
- Executable symbols with control flow: 2
- Grounded edges: 3
- Unresolved references: 12
- Control-flow fragments: 2
- Unresolved jumps: 6
- Unsupported constructs: 0
- Control-flow coverage: 100%
- Required diagrams: 2
- Generated diagrams: 3

## Control Flow Fragments

- IngestEbookCommandHandler: 3 nodes, 2 edges, 0 jumps, 0 unsupported constructs
- Handle: 40 nodes, 42 edges, 6 jumps, 0 unsupported constructs

## Diagrams

### IngestEbookCommandHandler Neighborhood

```mermaid
flowchart LR
    class_1kfw737_ingestebookcommandhandler("IngestEbookCommandHandler")
    file_lqr4h8_ingestebookcommandhandler_cs("IngestEbookCommandHandler")
    method_gy7qh_handle[["Handle()"]]
    method_jlw375_ingestebookcommandhandler[["IngestEbookCommandHandler()"]]
```

### IngestEbookCommandHandler Execution Overview

```mermaid
flowchart TD
    class_1kfw737_ingestebookcommandhandler("IngestEbookCommandHandler")
    method_gy7qh_handle[["Handle()"]]
    method_jlw375_ingestebookcommandhandler[["IngestEbookCommandHandler()"]]
    unresolved_unresolved_100nowa_interaction_target{"Unresolved: var author = await _ebookRepository.FindAuthorByNameAsync(authorName, cancellationToken);"}
    unresolved_unresolved_1foxj00_call_target{"Unresolved: FindAuthorByNameAsync"}
    unresolved_unresolved_1hkxnh3_call_target{"Unresolved: SaveEbookAsync"}
    unresolved_unresolved_1jnlalo_call_target{"Unresolved: SaveCoverAsync"}
    unresolved_unresolved_1o7j1ya_call_target{"Unresolved: AddAuthor"}
    unresolved_unresolved_6ikjt_call_target{"Unresolved: AddEbook"}
    unresolved_unresolved_syqfv0_call_target{"Unresolved: SaveChangesAsync"}
    unresolved_unresolved_yctv9m_interaction_target{"Unresolved: Calls AddEbook and SaveChangesAsync to persist the new entity"}
    class_1kfw737_ingestebookcommandhandler -->|contains| method_gy7qh_handle
    class_1kfw737_ingestebookcommandhandler -->|contains| method_jlw375_ingestebookcommandhandler
    class_1kfw737_ingestebookcommandhandler -->|unresolved interaction| unresolved_unresolved_100nowa_interaction_target
    class_1kfw737_ingestebookcommandhandler -->|unresolved interaction| unresolved_unresolved_yctv9m_interaction_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_1foxj00_call_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_1hkxnh3_call_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_1jnlalo_call_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_1o7j1ya_call_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_6ikjt_call_target
    method_gy7qh_handle -->|unresolved call| unresolved_unresolved_syqfv0_call_target
```

### Handle() Control Flow

_Warnings: diagram method_gy7qh_handle__fragment_diagram exceeds preferred complexity thresholds (40 nodes, 42 edges)._

```mermaid
---
config:
  flowchart:
    defaultRenderer: "elk"
---
flowchart TD
    method_gy7qh_handle__call_31_SaveEbookAsync[["SaveEbookAsync"]]
    method_gy7qh_handle__call_33_SaveCoverAsync[["SaveCoverAsync"]]
    method_gy7qh_handle__call_52_FindAuthorByNameAsync[["FindAuthorByNameAsync"]]
    method_gy7qh_handle__call_56_AddAuthor[["AddAuthor"]]
    method_gy7qh_handle__call_72_AddEbook[["AddEbook"]]
    method_gy7qh_handle__call_73_SaveChangesAsync[["SaveChangesAsync"]]
    method_gy7qh_handle__action_28_handle_try_28_0__try("Begins protected execution")
    method_gy7qh_handle__action_28_try_catch_28__linear("Storage save with I/O and generic exception handling")
    method_gy7qh_handle__action_31_handle_await_31_1__await[["Waits for async work"]]
    method_gy7qh_handle__action_32_branch_32__condition{"request.CoverImage != null && request.CoverImage.Length > 0"}
    method_gy7qh_handle__action_32_branch_32__false("no cover (coverUrl = null)")
    method_gy7qh_handle__action_32_branch_32__merge("merge")
    method_gy7qh_handle__action_32_branch_32__true("save cover and set coverUrl")
    method_gy7qh_handle__action_33_handle_await_33_2__await[["Waits for async work"]]
    method_gy7qh_handle__action_36_handle_catch_36_3__catch("catch IOException ex")
    method_gy7qh_handle__action_40_handle_catch_40_5__catch("catch Exception ex")
    method_gy7qh_handle__action_45_handle_try_45_7__try("Begins protected execution")
    method_gy7qh_handle__action_48_guard_clause_48__condition{"string.IsNullOrWhiteSpace(request.AuthorName)"}
    method_gy7qh_handle__action_48_guard_clause_48__false("use trimmed request.AuthorName")
    method_gy7qh_handle__action_48_guard_clause_48__merge("merge")
    method_gy7qh_handle__action_48_guard_clause_48__true("use 'Unknown Author'")
    method_gy7qh_handle__action_52_external_repository_read_52__linear("Find author case-insensitively (via repository)")
    method_gy7qh_handle__action_52_handle_await_52_9__await[["Waits for async work"]]
    method_gy7qh_handle__action_53_branch_fallback_53__linear("Create missing author")
    method_gy7qh_handle__action_53_handle_branch_53_10__condition{"author == null"}
    method_gy7qh_handle__action_53_handle_branch_53_10__false("false")
    method_gy7qh_handle__action_53_handle_branch_53_10__merge("merge")
    method_gy7qh_handle__action_53_handle_branch_53_10__true("true")
    method_gy7qh_handle__action_60_entity_construction_60__linear("Construct Ebook entity")
    method_gy7qh_handle__action_73_handle_await_73_11__await[["Waits for async work"]]
    method_gy7qh_handle__action_77_handle_catch_77_13__catch("catch Exception ex")
    method_gy7qh_handle__action_77_try_catch_77__linear("Database error handling during persistence")
    method_gy7qh_handle__entry(["Handle entry"])
    method_gy7qh_handle__action_52_handle_repository_read_52_8__linear[("Reads repository or persistence state")]
    method_gy7qh_handle__action_72_repository_write_72__linear[("Add ebook and persist")]
    method_gy7qh_handle__action_38_handle_return_38_4__return(["Returns result"])
    method_gy7qh_handle__action_42_handle_return_42_6__return(["Returns result"])
    method_gy7qh_handle__action_75_handle_return_75_12__return(["Returns result"])
    method_gy7qh_handle__action_75_return_75__return(["Return success with Ebook Id"])
    method_gy7qh_handle__action_79_handle_return_79_14__return(["Returns result"])
    method_gy7qh_handle__action_28_handle_try_28_0__try -->|next| method_gy7qh_handle__action_31_handle_await_31_1__await
    method_gy7qh_handle__action_28_try_catch_28__linear -->|next| method_gy7qh_handle__action_28_handle_try_28_0__try
    method_gy7qh_handle__action_31_handle_await_31_1__await -->|request.EpubData, filename = request.Title .epub| method_gy7qh_handle__call_31_SaveEbookAsync
    method_gy7qh_handle__action_32_branch_32__condition -->|no cover coverUrl = null| method_gy7qh_handle__action_32_branch_32__false
    method_gy7qh_handle__action_32_branch_32__condition -->|save cover and set coverUrl| method_gy7qh_handle__action_32_branch_32__true
    method_gy7qh_handle__action_32_branch_32__false -->|next| method_gy7qh_handle__action_32_branch_32__merge
    method_gy7qh_handle__action_32_branch_32__merge -->|next| method_gy7qh_handle__action_33_handle_await_33_2__await
    method_gy7qh_handle__action_32_branch_32__true -->|next| method_gy7qh_handle__action_32_branch_32__merge
    method_gy7qh_handle__action_33_handle_await_33_2__await -->|request.CoverImage, filename = request.Title _cover.jpg called only if CoverImage present| method_gy7qh_handle__call_33_SaveCoverAsync
    method_gy7qh_handle__action_36_handle_catch_36_3__catch -->|return| method_gy7qh_handle__action_38_handle_return_38_4__return
    method_gy7qh_handle__action_38_handle_return_38_4__return -->|exception| method_gy7qh_handle__action_40_handle_catch_40_5__catch
    method_gy7qh_handle__action_40_handle_catch_40_5__catch -->|return| method_gy7qh_handle__action_42_handle_return_42_6__return
    method_gy7qh_handle__action_42_handle_return_42_6__return -->|next| method_gy7qh_handle__action_45_handle_try_45_7__try
    method_gy7qh_handle__action_45_handle_try_45_7__try -->|next| method_gy7qh_handle__action_48_guard_clause_48__condition
    method_gy7qh_handle__action_48_guard_clause_48__condition -->|use trimmed request.AuthorName| method_gy7qh_handle__action_48_guard_clause_48__false
    method_gy7qh_handle__action_48_guard_clause_48__condition -->|transforms input| method_gy7qh_handle__action_48_guard_clause_48__true
    method_gy7qh_handle__action_48_guard_clause_48__false -->|next| method_gy7qh_handle__action_48_guard_clause_48__merge
    method_gy7qh_handle__action_48_guard_clause_48__merge -->|next| method_gy7qh_handle__action_52_handle_await_52_9__await
    method_gy7qh_handle__action_48_guard_clause_48__true -->|next| method_gy7qh_handle__action_48_guard_clause_48__merge
    method_gy7qh_handle__action_52_external_repository_read_52__linear -->|calls| method_gy7qh_handle__call_52_FindAuthorByNameAsync
    method_gy7qh_handle__action_52_handle_await_52_9__await -->|next| method_gy7qh_handle__action_52_handle_repository_read_52_8__linear
    method_gy7qh_handle__action_52_handle_repository_read_52_8__linear -->|next| method_gy7qh_handle__action_52_external_repository_read_52__linear
    method_gy7qh_handle__action_53_branch_fallback_53__linear -->|new Author Name = authorName when author not found| method_gy7qh_handle__call_56_AddAuthor
    method_gy7qh_handle__action_53_handle_branch_53_10__condition -->|false| method_gy7qh_handle__action_53_handle_branch_53_10__false
    method_gy7qh_handle__action_53_handle_branch_53_10__condition -->|true| method_gy7qh_handle__action_53_handle_branch_53_10__true
    method_gy7qh_handle__action_53_handle_branch_53_10__false -->|next| method_gy7qh_handle__action_53_handle_branch_53_10__merge
    method_gy7qh_handle__action_53_handle_branch_53_10__merge -->|next| method_gy7qh_handle__action_53_branch_fallback_53__linear
    method_gy7qh_handle__action_53_handle_branch_53_10__true -->|next| method_gy7qh_handle__action_53_handle_branch_53_10__merge
    method_gy7qh_handle__action_60_entity_construction_60__linear -->|next| method_gy7qh_handle__action_72_repository_write_72__linear
    method_gy7qh_handle__action_72_repository_write_72__linear -->|ebook Ebook entity constructed from request + paths + metadata| method_gy7qh_handle__call_72_AddEbook
    method_gy7qh_handle__action_73_handle_await_73_11__await -->|cancellationToken| method_gy7qh_handle__call_73_SaveChangesAsync
    method_gy7qh_handle__action_75_handle_return_75_12__return -->|return| method_gy7qh_handle__action_75_return_75__return
    method_gy7qh_handle__action_75_return_75__return -->|exception| method_gy7qh_handle__action_77_handle_catch_77_13__catch
    method_gy7qh_handle__action_77_handle_catch_77_13__catch -->|next| method_gy7qh_handle__action_77_try_catch_77__linear
    method_gy7qh_handle__action_77_try_catch_77__linear -->|return| method_gy7qh_handle__action_79_handle_return_79_14__return
    method_gy7qh_handle__call_31_SaveEbookAsync -->|next| method_gy7qh_handle__action_32_branch_32__condition
    method_gy7qh_handle__call_33_SaveCoverAsync -->|exception| method_gy7qh_handle__action_36_handle_catch_36_3__catch
    method_gy7qh_handle__call_52_FindAuthorByNameAsync -->|next| method_gy7qh_handle__action_53_handle_branch_53_10__condition
    method_gy7qh_handle__call_56_AddAuthor -->|next| method_gy7qh_handle__action_60_entity_construction_60__linear
    method_gy7qh_handle__call_72_AddEbook -->|next| method_gy7qh_handle__action_73_handle_await_73_11__await
    method_gy7qh_handle__call_73_SaveChangesAsync -->|return| method_gy7qh_handle__action_75_handle_return_75_12__return
    method_gy7qh_handle__entry -->|next| method_gy7qh_handle__action_28_try_catch_28__linear
```

## Diagram Validation

- Status: warn
- Warning: Control-flow extraction left 6 unresolved jumps.
- Warning: control-flow: diagram method_gy7qh_handle__fragment_diagram exceeds preferred complexity thresholds (40 nodes, 42 edges).
