Skip to content

FSPW-395: Harmonise DownloadBlob task#121

Open
jefim wants to merge 4 commits intomainfrom
harmonise/FSPW-395-download-blob
Open

FSPW-395: Harmonise DownloadBlob task#121
jefim wants to merge 4 commits intomainfrom
harmonise/FSPW-395-download-blob

Conversation

@jefim
Copy link
Copy Markdown
Contributor

@jefim jefim commented Apr 17, 2026

Summary

Harmonises Frends.AzureBlobStorage.DownloadBlob to align with Frends Task Development Guidelines (FSPW-395).

Breaking changes (major version bump: → 3.0.0)

  • Source and Destination tabs removed; fields consolidated into new Input tab
  • FileExistsAction.Error renamed to FileExistsAction.Throw
  • FileEncoding.WINDOWS1252 renamed to FileEncoding.Windows1252
  • Result.FileName and Result.Directory removed; Result.FullPath renamed to Result.FilePath
  • Encoding options moved from Source to new Options tab; EnableBOM removed in favour of FileEncoding.UTF8WithBOM

New features

  • New Options tab with Encoding (default UTF8WithBOM), OtherEncoding, ThrowErrorOnFailure (default true), ErrorMessageOnFailure
  • Result.Success (bool) added
  • Result.Error with Message and AdditionalInfo added
  • Standard ErrorHandler pattern implemented

Files changed

  • Definitions/Input.cs — new, replaces Source + Destination
  • Definitions/Options.cs — new
  • Definitions/Error.cs — new
  • Helpers/ErrorHandler.cs — new
  • Definitions/Enums.cs — enum renames + UTF8WithBOM added
  • Definitions/Result.cs — restructured
  • DownloadBlob.cs — signature + implementation updated
  • Definitions/Source.cs — deleted
  • Definitions/Destination.cs — deleted
  • CHANGELOG.md — updated
  • *.csproj — version bumped to 3.0.0
  • UnitTests.cs — migrated + new tests added

Test plan

  • Build succeeds with 0 warnings (dotnet build)
  • All new Input/Options structures compile correctly
  • Integration tests require Azure credentials (unchanged from pre-existing test setup)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced error handling with customizable error messages and failure behavior options
    • Added new encoding option with UTF-8 BOM support
    • Success and error information now available in download results
  • Breaking Changes

    • Parameter structure refactored: Source/Destination replaced with Input/Options
    • File existence action renamed from "Error" to "Throw"
    • Result object structure updated with FilePath instead of FullPath
    • Encoding option naming standardized (Windows-1252)
  • Tests

    • Added comprehensive error handling test suite
    • Updated test coverage for new API structure and encoding options

…, Error/Result refactor, ErrorHandler

Breaking changes (2.x → 3.0):
- Replace Source + Destination tabs with Input + Options
- FileExistsAction.Error → Throw; FileEncoding.WINDOWS1252 → Windows1252; new UTF8WithBOM value
- Result gains Success bool, FilePath (was FullPath), Error; loses FileName and Directory
- Add Options.ThrowErrorOnFailure (default true) and ErrorMessageOnFailure
- Add Helpers/ErrorHandler for standardised error handling
- Bump version to 3.0.0; update CHANGELOG and unit tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

Warning

Rate limit exceeded

@MichalFrends1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 51 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4c39209-493e-4197-9568-b85471d22718

📥 Commits

Reviewing files that changed from the base of the PR and between 69389af and 4dec790.

📒 Files selected for processing (2)
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs

Walkthrough

The PR refactors the DownloadBlob task's public API from Source/Destination to Input/Options, updates enums (FileExistsAction.ErrorThrow, adds UTF8WithBOM, renames WINDOWS1252), changes the result shape from FileName/Directory/FullPath to Success/FilePath/Error, and introduces error-handling options (ThrowErrorOnFailure, ErrorMessageOnFailure) with a new ErrorHandler utility. Version bumped to 3.0.0.

Changes

Major API Refactoring (v2.0.0 → v3.0.0)

Layer / File(s) Summary
Enum & Type Definitions
Definitions/Enums.cs, Definitions/Error.cs
FileExistsAction.Error renamed to Throw; FileEncoding adds UTF8WithBOM and renames WINDOWS1252 to Windows1252. New Error class added with Message and AdditionalInfo properties for failure reporting.
Input/Options Schema
Definitions/Input.cs, Definitions/Options.cs
Old Source (container, blob, encoding) and Destination (directory, file-exists behavior, target name) are replaced by new Input (container, blob, target directory, target filename, action-on-existing-file) and Options (encoding, throw-on-failure, custom error message).
Result Shape
Definitions/Result.cs
Result refactored from FileName/Directory/FullPath (constructor-initialized, private setters) to Success/FilePath/Error (internal setters) for explicit success/failure reporting.
Core Implementation & Error Handling
DownloadBlob.cs, Helpers/ErrorHandler.cs
DownloadBlob method signature updated to accept Input/Options; file-existence logic refactored to use input.ActionOnExistingFile with Throw/Rename/Overwrite modes; encoding detection made BOM-aware via CheckAndFixFileEncoding; GetEncoding updated to map enum values directly (including UTF8WithBOM distinction) instead of using enableBom parameter. New ErrorHandler.Handle() conditionally throws or returns failed Result based on ThrowErrorOnFailure.
Migration & Version
Frends.AzureBlobStorage.DownloadBlob.csproj, migration.json, CHANGELOG.md
Package version bumped from 2.0.0 to 3.0.0. Migration definition added mapping Source/Destination fields to Input/Options with enum conversions and defaults. Changelog documents breaking changes and upgrade instructions.
Tests
Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs, Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs
Existing tests refactored to use Input/Options with updated assertions (result.FilePath instead of result.FullPath), enum values (Throw instead of Error), and file-existence handling. New ErrorHandlerTest fixture added verifying exception-throwing vs failed-result behavior and custom error messages.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

Suggested Reviewers

  • MichalFrends1
  • MatteoDelOmbra
  • ttossavainen

Poem

🐰 A blob once lived in Azure's keep,
Its source and destination, oh so deep,
Now Input speaks, and Options too,
With success flags and errors true,
BOM-aware, the rabbit's code,
Makes version three a smoother road!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harmonise DownloadBlob task' accurately captures the main objective of the pull request—harmonising the DownloadBlob task to align with Frends Task Development Guidelines, including significant structural and API changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch harmonise/FSPW-395-download-blob

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (6)
Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs (3)

17-20: 💤 Low value

Redundant null assertion.

Assert.ThrowsAsync<Exception> already fails the test if no exception is thrown, so Assert.That(ex, Is.Not.Null) adds no coverage. Consider asserting on the exception's Message/InnerException instead to verify the error contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs`
around lines 17 - 20, Remove the redundant null check after Assert.ThrowsAsync
in ErrorHandlerTest.cs: the Assert.ThrowsAsync<Exception>(() =>
AzureBlobStorage.DownloadBlob(DefaultInput(), DefaultOptions(),
DefaultConnection(), CancellationToken.None)) already guarantees an exception,
so remove Assert.That(ex, Is.Not.Null) and replace it with a meaningful
assertion on the thrown exception (for example assert on ex.Message or
ex.InnerException) to validate the error contract coming from
AzureBlobStorage.DownloadBlob.

14-48: ⚡ Quick win

Tests depend on implicit task failure rather than exercising ErrorHandler directly.

Despite the fixture name ErrorHandlerTest, every test invokes the public AzureBlobStorage.DownloadBlob with empty Input/Connection and relies on whatever exception that throws first. This couples the tests to the internal validation order of DownloadBlob — a future change (e.g., earlier argument validation, different exception type) could pass for the wrong reason or break unrelated tests. Since ErrorHandler is internal, exposing it via InternalsVisibleTo and unit-testing ErrorHandler.Handle directly would give precise, fast, and stable coverage of the new error contract, keeping the public-API tests in UnitTests.cs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs`
around lines 14 - 48, The tests in ErrorHandlerTest.cs should call
ErrorHandler.Handle directly instead of invoking AzureBlobStorage.DownloadBlob;
make ErrorHandler internal visible to the test assembly (add InternalsVisibleTo
for the test project in the production assembly using AssemblyInfo or the
project file) and update the tests to construct appropriate inputs/Exceptions
and assert the returned/raised results from ErrorHandler.Handle (e.g., verify
behavior when ThrowErrorOnFailure is true/false and ErrorMessageOnFailure is
used) rather than depending on implicit failures from DownloadBlob.

22-29: ⚡ Quick win

Strengthen failure-path assertions.

Asserting only result.Success == false leaves the new Result/Error contract largely unverified. Per coding guidelines for result classes, the failure path should also confirm FilePath, Error.Message, and Error.AdditionalInfo are populated as ErrorHandler.Handle promises.

♻️ Suggested additional assertions
             var result = await AzureBlobStorage.DownloadBlob(DefaultInput(), options, DefaultConnection(), CancellationToken.None);
             Assert.That(result.Success, Is.False);
+            Assert.That(result.FilePath, Is.Null);
+            Assert.That(result.Error, Is.Not.Null);
+            Assert.That(result.Error.Message, Is.Not.Null.And.Not.Empty);
+            Assert.That(result.Error.AdditionalInfo, Is.InstanceOf<Exception>());

Also consider adding a complementary test for the non-throw + custom message path, since Should_Use_Custom_ErrorMessageOnFailure only covers the throwing branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs`
around lines 22 - 29, The test
Should_Return_Failed_Result_When_ThrowErrorOnFailure_Is_False only asserts
result.Success is false; update it to also verify the failure contract by
asserting Result.FilePath is non-null/empty, Result.Error.Message contains the
expected message, and Result.Error.AdditionalInfo is populated as produced by
ErrorHandler.Handle after calling AzureBlobStorage.DownloadBlob with
options.ThrowErrorOnFailure = false; additionally add a complementary test
(similar to Should_Use_Custom_ErrorMessageOnFailure) that exercises the
non-throw branch with a custom error message and asserts Error.Message equals
the custom message and AdditionalInfo is set.
Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Helpers/ErrorHandler.cs (2)

10-15: ⚡ Quick win

Avoid throwing bare System.Exception; prefer rethrowing or a specific type.

Wrapping every failure in new Exception(...) discards the original exception type (e.g., RequestFailedException, IOException), making it impossible for callers to catch by specific type. When no custom message is supplied, the wrapper adds nothing — the original exception should just propagate so the stack trace and type are preserved.

♻️ Suggested refactor
         if (throwOnFailure)
         {
-            if (string.IsNullOrEmpty(errorMessageOnFailure))
-                throw new Exception(exception.Message, exception);
-            throw new Exception(errorMessageOnFailure, exception);
+            if (string.IsNullOrEmpty(errorMessageOnFailure))
+                ExceptionDispatchInfo.Capture(exception).Throw();
+            throw new Exception(errorMessageOnFailure, exception);
         }

Add using System.Runtime.ExceptionServices; if applying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Helpers/ErrorHandler.cs`
around lines 10 - 15, The current code wraps every failure in new Exception(...)
losing the original exception type; change this so that when
errorMessageOnFailure is null/empty you rethrow the original exception
preserving type and stack (use ExceptionDispatchInfo.Capture(exception).Throw();
with using System.Runtime.ExceptionServices), and when a custom message is
provided throw a more specific wrapper (e.g., InvalidOperationException or a
domain-specific Exception) with the original exception as InnerException
(replace new Exception(errorMessageOnFailure, exception) with new
InvalidOperationException(errorMessageOnFailure, exception)); keep references to
throwOnFailure, errorMessageOnFailure and exception variables in the
ErrorHandler logic.

17-19: 💤 Low value

Inconsistent message formatting between throw and non-throw paths.

When throwOnFailure=true with a custom message, the thrown exception's Message is just errorMessageOnFailure and the original message is preserved only via InnerException. When throwOnFailure=false, the returned Error.Message becomes "{errorMessageOnFailure}: {exception.Message}". Consumers comparing strings or logs across the two modes will see different formats for the same underlying failure. Consider unifying — either include the original message in both, or in neither.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Helpers/ErrorHandler.cs`
around lines 17 - 19, The formatting diverges between the throw and non-throw
paths: build a single formatted string (use the existing errorMessage variable
built from errorMessageOnFailure and exception.Message) and use that same string
both for the returned Error.Message and as the message for the thrown exception
when throwOnFailure is true (keep the original exception as InnerException).
Update the throw path to use errorMessage instead of errorMessageOnFailure so
thrown Exception.Message and Error.Message are identical; reference symbols:
errorMessage, errorMessageOnFailure, exception, throwOnFailure, and the thrown
Exception/returned Error in ErrorHandler.cs.
Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs (1)

40-47: ⚡ Quick win

Replace repeated Split('.') calls with Path API methods; blob names with virtual paths will produce incorrect rename paths.

Two separate problems here:

  1. Virtual path in blob name (e.g., "subfolder/image.png"): blobFileName.Split('.')[…] leaves the directory prefix in fileName, so the rename counter produces subfolder/image(1).png. When combined with input.TargetDirectory, this resolves to a path whose intermediate directory doesn't exist, causing DownloadToAsync to fail. Path.GetFileName(blobFileName) strips the prefix correctly.

  2. Multiple dots are handled correctly by the current code, but splitting the same string 5 times is fragile and noisy. Path.GetExtension / Path.GetFileNameWithoutExtension handle all edge cases natively.

♻️ Proposed refactor
-            var blobFileName = string.IsNullOrWhiteSpace(input.TargetFileName)
-                ? input.BlobName
-                : input.TargetFileName;
+            var rawName = string.IsNullOrWhiteSpace(input.TargetFileName)
+                ? Path.GetFileName(input.BlobName) // strip virtual-directory prefix
+                : input.TargetFileName;
+            var blobFileName = rawName;
             var fullDestinationPath = Path.Combine(input.TargetDirectory, blobFileName);
-            var fileName = blobFileName.Split('.')[0];
-            var fileExtension = "";
-
-            if (blobFileName.Split('.').Length > 1)
-            {
-                fileName = string.Join(".", blobFileName.Split('.').Take(blobFileName.Split('.').Length - 1).ToArray());
-                fileExtension = "." + blobFileName.Split('.').Last();
-            }
+            var fileName = Path.GetFileNameWithoutExtension(blobFileName);
+            var fileExtension = Path.GetExtension(blobFileName); // includes leading dot, or "" if none
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs`
around lines 40 - 47, Replace the repeated blobFileName.Split('.') logic with
System.IO.Path helpers: use Path.GetFileName(blobFileName) to strip any virtual
folder prefix, then use Path.GetExtension(...) and
Path.GetFileNameWithoutExtension(...) to derive fileExtension and fileName
(respectively); update any rename/collision logic that currently uses
fileName/fileExtension so it constructs filenames from these Path-derived values
and ensures directories are created before calling DownloadToAsync. This change
touches the blobFileName handling and the variables fileName and fileExtension
(and the code that combines them when renaming/saving).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs`:
- Around line 220-227: The UploadTestFiles helper currently calls
BlobClient.UploadAsync(_testFilePath, default) which passes overwrite=false and
will fail if a previous test left the blob; update the UploadTestFiles method
(reference: UploadTestFiles and the blockBlob/BlobClient usage) to call
UploadAsync with overwrite: true (and keep the cancellation token/default for
the last parameter) so the upload succeeds even when the blob already exists.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Input.cs`:
- Around line 47-48: The property ActionOnExistingFile is only decorated with
[DefaultValue] (UI metadata) but lacks a runtime initializer, so set its default
explicitly by adding an initializer: change the property declaration for
ActionOnExistingFile to include " = FileExistsAction.Throw;" so programmatic
callers get the correct default (mirror how ThrowErrorOnFailure is initialized
in Options.cs).
- Around line 32-33: Add the [NotEmptyString] validation attribute to the
TargetDirectory property (same as ContainerName and BlobName) so empty string
input is rejected; update the TargetDirectory declaration to include
[NotEmptyString] and ensure the same validation namespace/import used by
ContainerName/BlobName is present so the attribute resolves correctly.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Options.cs`:
- Around line 16-17: The Encoding property on the Options class lacks a runtime
initializer so new Options() gets the enum's zero value instead of
FileEncoding.UTF8WithBOM; update the Encoding auto-property to include an
explicit initializer (= FileEncoding.UTF8WithBOM) similar to how
ThrowErrorOnFailure and ErrorMessageOnFailure use their default-value
initializers so both UI and programmatic callers see the intended default;
locate the Options class and the Encoding property and add the initializer
referencing the FileEncoding enum.
- Around line 25-26: The OtherEncoding property is left null which causes
GetEncoding(null) to produce a misleading error; initialize OtherEncoding to an
empty string to match the ErrorMessageOnFailure pattern and avoid null-flow when
Encoding == FileEncoding.Other—update the OtherEncoding auto-property in
Options.cs to have a default value of string.Empty (keeping GetEncoding in
DownloadBlob.cs and the Encoding/FileEncoding check unchanged).

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs`:
- Around line 117-119: The current loop using StreamReader.ReadLine() and
StreamWriter.WriteLine() (variables sr and sw) appends a spurious trailing
newline to the final line; replace the ReadLine/WriteLine loop with a verbatim
copy by calling sr.ReadToEnd() and writing that to sw (i.e., use
sw.Write(sr.ReadToEnd())) so the file content is copied byte-for-byte without
adding or removing line terminators.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/migration.json`:
- Around line 86-102: The migration currently maps Source.Encoding directly to
Options.Encoding and copies Source.FileEncodingString to Options.OtherEncoding
but ignores Source.EnableBOM, causing UTF8+EnableBOM to lose BOM; update the
migration.json to add a conditional mapping step that checks Source.Encoding ==
"UTF8" and Source.EnableBOM == true and sets Options.Encoding to "UTF8WithBOM"
(falling back to the existing Map rule for other cases), or if conditional steps
are not supported by your migration system, update the upgrade docs to
explicitly state that Source.EnableBOM must be manually re-applied to
Options.Encoding (UTF8WithBOM) after migration; reference the existing keys
Source.Encoding, Source.EnableBOM, Options.Encoding and the Map/Copy mapping
rules when making the change.

---

Nitpick comments:
In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs`:
- Around line 17-20: Remove the redundant null check after Assert.ThrowsAsync in
ErrorHandlerTest.cs: the Assert.ThrowsAsync<Exception>(() =>
AzureBlobStorage.DownloadBlob(DefaultInput(), DefaultOptions(),
DefaultConnection(), CancellationToken.None)) already guarantees an exception,
so remove Assert.That(ex, Is.Not.Null) and replace it with a meaningful
assertion on the thrown exception (for example assert on ex.Message or
ex.InnerException) to validate the error contract coming from
AzureBlobStorage.DownloadBlob.
- Around line 14-48: The tests in ErrorHandlerTest.cs should call
ErrorHandler.Handle directly instead of invoking AzureBlobStorage.DownloadBlob;
make ErrorHandler internal visible to the test assembly (add InternalsVisibleTo
for the test project in the production assembly using AssemblyInfo or the
project file) and update the tests to construct appropriate inputs/Exceptions
and assert the returned/raised results from ErrorHandler.Handle (e.g., verify
behavior when ThrowErrorOnFailure is true/false and ErrorMessageOnFailure is
used) rather than depending on implicit failures from DownloadBlob.
- Around line 22-29: The test
Should_Return_Failed_Result_When_ThrowErrorOnFailure_Is_False only asserts
result.Success is false; update it to also verify the failure contract by
asserting Result.FilePath is non-null/empty, Result.Error.Message contains the
expected message, and Result.Error.AdditionalInfo is populated as produced by
ErrorHandler.Handle after calling AzureBlobStorage.DownloadBlob with
options.ThrowErrorOnFailure = false; additionally add a complementary test
(similar to Should_Use_Custom_ErrorMessageOnFailure) that exercises the
non-throw branch with a custom error message and asserts Error.Message equals
the custom message and AdditionalInfo is set.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs`:
- Around line 40-47: Replace the repeated blobFileName.Split('.') logic with
System.IO.Path helpers: use Path.GetFileName(blobFileName) to strip any virtual
folder prefix, then use Path.GetExtension(...) and
Path.GetFileNameWithoutExtension(...) to derive fileExtension and fileName
(respectively); update any rename/collision logic that currently uses
fileName/fileExtension so it constructs filenames from these Path-derived values
and ensures directories are created before calling DownloadToAsync. This change
touches the blobFileName handling and the variables fileName and fileExtension
(and the code that combines them when renaming/saving).

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Helpers/ErrorHandler.cs`:
- Around line 10-15: The current code wraps every failure in new Exception(...)
losing the original exception type; change this so that when
errorMessageOnFailure is null/empty you rethrow the original exception
preserving type and stack (use ExceptionDispatchInfo.Capture(exception).Throw();
with using System.Runtime.ExceptionServices), and when a custom message is
provided throw a more specific wrapper (e.g., InvalidOperationException or a
domain-specific Exception) with the original exception as InnerException
(replace new Exception(errorMessageOnFailure, exception) with new
InvalidOperationException(errorMessageOnFailure, exception)); keep references to
throwOnFailure, errorMessageOnFailure and exception variables in the
ErrorHandler logic.
- Around line 17-19: The formatting diverges between the throw and non-throw
paths: build a single formatted string (use the existing errorMessage variable
built from errorMessageOnFailure and exception.Message) and use that same string
both for the returned Error.Message and as the message for the thrown exception
when throwOnFailure is true (keep the original exception as InnerException).
Update the throw path to use errorMessage instead of errorMessageOnFailure so
thrown Exception.Message and Error.Message are identical; reference symbols:
errorMessage, errorMessageOnFailure, exception, throwOnFailure, and the thrown
Exception/returned Error in ErrorHandler.cs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3ef65b0-a2df-44d2-90c9-126b05785f22

📥 Commits

Reviewing files that changed from the base of the PR and between 1eca46d and 69389af.

📒 Files selected for processing (14)
  • Frends.AzureBlobStorage.DownloadBlob/CHANGELOG.md
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/ErrorHandlerTest.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Destination.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Enums.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Error.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Input.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Options.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Result.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Source.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.csproj
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Helpers/ErrorHandler.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/migration.json
💤 Files with no reviewable changes (2)
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Destination.cs
  • Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Source.cs

Comment on lines +220 to +227
private async Task UploadTestFiles(string containerName)
{
var blobServiceClient = new BlobServiceClient(TestHelper.ConnectionString);
var container = blobServiceClient.GetBlobContainerClient(containerName);
await container.CreateIfNotExistsAsync(PublicAccessType.None, null, null);
var blockBlob = container.GetBlobClient(_testBlob);
await blockBlob.UploadAsync(_testFilePath, default);
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, explore the test file to understand the structure and context
find . -type f -name "UnitTests.cs" | grep -i "azureblobstorage"

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 836


🏁 Script executed:

# Once we find the file, let's read it to see the SetUp/TearDown and test structure
# First get the file path
fd -type f -name "UnitTests.cs" | grep -i blobstorage

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 253


🌐 Web query:

BlobClient.UploadAsync method signature Azure SDK .NET

💡 Result:

The BlobClient.UploadAsync method in the Azure.Storage.Blobs.NET SDK has multiple overloads. The official Microsoft documentation lists the following primary async overloads [1][2]: public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( string path, bool overwrite = false, System.Threading.CancellationToken cancellationToken = default); [1] public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( System.IO.Stream content, bool overwrite = false, System.Threading.CancellationToken cancellationToken = default); [1] public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( BinaryData content, bool overwrite = false, System.Threading.CancellationToken cancellationToken = default); [1] Additional overloads include those accepting BlobUploadOptions: public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( string path, Azure.Storage.Blobs.Models.BlobUploadOptions options, System.Threading.CancellationToken cancellationToken = default); [1] public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( System.IO.Stream content, Azure.Storage.Blobs.Models.BlobUploadOptions options, System.Threading.CancellationToken cancellationToken = default); [1] public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Storage.Blobs.Models.BlobContentInfo>> UploadAsync( BinaryData content, Azure.Storage.Blobs.Models.BlobUploadOptions options, System.Threading.CancellationToken cancellationToken = default); [1] These create a new block blob or overwrite an existing one if specified, returning BlobContentInfo [1]. Usage examples include uploading from file path, stream, or BinaryData [3].

Citations:


🏁 Script executed:

cat -n ./Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs | head -100

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 4218


🏁 Script executed:

# Get the file size first to understand its extent
wc -l ./Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 181


🏁 Script executed:

# Look for SetUp, TearDown, and UploadTestFiles context
grep -n -A 10 "SetUp\|TearDown\|UploadTestFiles" ./Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs | head -150

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 1722


Pass overwrite: true to ensure blob upload succeeds even if a previous test left the blob in the container.

BlobClient.UploadAsync(string path, bool overwrite = false, ...) defaults the second positional parameter to overwrite: false. When default is passed as the second argument, it resolves to false, which causes a RequestFailedException if the blob already exists. The TearDown uses DeleteBlobsInContainer() to empty the const container (_container), but if cleanup fails or is incomplete, the next SetUp call will fail when attempting to re-upload the blob.

Proposed fix
-        await blockBlob.UploadAsync(_testFilePath, default);
+        await blockBlob.UploadAsync(_testFilePath, overwrite: true);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob.Tests/UnitTests.cs`
around lines 220 - 227, The UploadTestFiles helper currently calls
BlobClient.UploadAsync(_testFilePath, default) which passes overwrite=false and
will fail if a previous test left the blob; update the UploadTestFiles method
(reference: UploadTestFiles and the blockBlob/BlobClient usage) to call
UploadAsync with overwrite: true (and keep the cancellation token/default for
the last parameter) so the upload succeeds even when the blob already exists.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichalFrends1 lets add that, no harm and give us more stable environment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +32 to +33
[DisplayFormat(DataFormatString = "Text")]
public string TargetDirectory { get; set; }
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for ValidationHandler and see if TargetDirectory is validated there
ast-grep --pattern 'class ValidationHandler {
  $$$
}'

rg -n "TargetDirectory" --type cs

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 1134


🏁 Script executed:

cat -n Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Input.cs

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 2187


🏁 Script executed:

rg -n "ValidationHandler" --type cs -A 20

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 50394


🏁 Script executed:

rg -n "\[NotEmptyString\]" --type cs -B 1

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 3278


Add [NotEmptyString] validation attribute to TargetDirectory.

ContainerName and BlobName are both guarded with [NotEmptyString], but TargetDirectory is not. An empty string silently resolves Path.Combine("", blobFileName) to a relative path in the working directory, which is almost never the intended behaviour.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Input.cs`
around lines 32 - 33, Add the [NotEmptyString] validation attribute to the
TargetDirectory property (same as ContainerName and BlobName) so empty string
input is rejected; update the TargetDirectory declaration to include
[NotEmptyString] and ensure the same validation namespace/import used by
ContainerName/BlobName is present so the attribute resolves correctly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, @MichalFrends1 lets add it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +47 to +48
[DefaultValue(FileExistsAction.Throw)]
public FileExistsAction ActionOnExistingFile { get; set; }
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

ActionOnExistingFile is missing an explicit property initializer.

[DefaultValue] is UI/tooling metadata only — it does not set the runtime value. ThrowErrorOnFailure in Options.cs correctly pairs its [DefaultValue(true)] with = true. The same pattern should be applied here; if FileExistsAction.Throw is not ordinal 0, programmatic callers get a silently wrong default.

🛠 Proposed fix
-    public FileExistsAction ActionOnExistingFile { get; set; }
+    public FileExistsAction ActionOnExistingFile { get; set; } = FileExistsAction.Throw;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Input.cs`
around lines 47 - 48, The property ActionOnExistingFile is only decorated with
[DefaultValue] (UI metadata) but lacks a runtime initializer, so set its default
explicitly by adding an initializer: change the property declaration for
ActionOnExistingFile to include " = FileExistsAction.Throw;" so programmatic
callers get the correct default (mirror how ThrowErrorOnFailure is initialized
in Options.cs).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, @MichalFrends1 lets add it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +16 to +17
[DefaultValue(FileEncoding.UTF8WithBOM)]
public FileEncoding Encoding { get; set; }
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Encoding is missing an explicit property initializer — programmatic callers silently get the wrong default.

[DefaultValue] is consumed by the Frends UI tooling only. ThrowErrorOnFailure and ErrorMessageOnFailure both pair their [DefaultValue] with a = initializer precisely to cover runtime callers. Encoding skips this: new Options() will resolve to whatever enum ordinal 0 is, which may not be FileEncoding.UTF8WithBOM.

🛠 Proposed fix
-    public FileEncoding Encoding { get; set; }
+    public FileEncoding Encoding { get; set; } = FileEncoding.UTF8WithBOM;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[DefaultValue(FileEncoding.UTF8WithBOM)]
public FileEncoding Encoding { get; set; }
[DefaultValue(FileEncoding.UTF8WithBOM)]
public FileEncoding Encoding { get; set; } = FileEncoding.UTF8WithBOM;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Options.cs`
around lines 16 - 17, The Encoding property on the Options class lacks a runtime
initializer so new Options() gets the enum's zero value instead of
FileEncoding.UTF8WithBOM; update the Encoding auto-property to include an
explicit initializer (= FileEncoding.UTF8WithBOM) similar to how
ThrowErrorOnFailure and ErrorMessageOnFailure use their default-value
initializers so both UI and programmatic callers see the intended default;
locate the Options class and the Encoding property and add the initializer
referencing the FileEncoding enum.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, @MichalFrends1 lets add it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +25 to +26
[DisplayFormat(DataFormatString = "Text")]
public string OtherEncoding { get; set; }
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

OtherEncoding is uninitialized — inconsistent with the ErrorMessageOnFailure pattern and exposes a null-flow risk.

ErrorMessageOnFailure is explicitly initialized to string.Empty; OtherEncoding is left at null. When Encoding == FileEncoding.Other and OtherEncoding is never set, GetEncoding(null) in DownloadBlob.cs receives null, which results in a somewhat misleading error message "Invalid encoding string '': …" instead of a clear null validation message.

🛠 Proposed fix
-    public string OtherEncoding { get; set; }
+    public string OtherEncoding { get; set; } = string.Empty;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[DisplayFormat(DataFormatString = "Text")]
public string OtherEncoding { get; set; }
[DisplayFormat(DataFormatString = "Text")]
public string OtherEncoding { get; set; } = string.Empty;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/Definitions/Options.cs`
around lines 25 - 26, The OtherEncoding property is left null which causes
GetEncoding(null) to produce a misleading error; initialize OtherEncoding to an
empty string to match the ErrorMessageOnFailure pattern and avoid null-flow when
Encoding == FileEncoding.Other—update the OtherEncoding auto-property in
Options.cs to have a default value of string.Empty (keeping GetEncoding in
DownloadBlob.cs and the Encoding/FileEncoding check unchanged).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, @MichalFrends1 lets add it

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +86 to +102
{
"Type": "Map",
"Source": "Source.Encoding",
"Target": "Options.Encoding",
"Maps": {
"UTF8": "UTF8",
"Default": "Default",
"ASCII": "ASCII",
"WINDOWS1252": "Windows1252",
"Other": "Other"
}
},
{
"Type": "Copy",
"Source": "Source.FileEncodingString",
"Target": "Options.OtherEncoding"
},
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect existing migration.json patterns in sibling tasks for conditional/composite mapping idioms
fd -t f migration.json | xargs -I{} sh -c 'echo "=== {} ==="; cat {}'

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 15165


🏁 Script executed:

# Find CHANGELOG file
fd -t f CHANGELOG | head -5

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 344


🏁 Script executed:

# Search for EnableBOM references in the codebase
rg -i "enablebom" --type-list

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 472


🏁 Script executed:

# Find the DownloadBlob task definition to check Encoding enum and EnableBOM field
fd -t f -i "downloadblob" | grep -E "\.(cs|json)$" | head -10

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 172


🏁 Script executed:

# Check DownloadBlob CHANGELOG for EnableBOM and UTF8WithBOM references
cat Frends.AzureBlobStorage.DownloadBlob/CHANGELOG.md

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 3930


🏁 Script executed:

# Search for EnableBOM anywhere in the codebase
rg -i "enablebom"

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 2460


🏁 Script executed:

# Check the DownloadBlob.cs task definition for Encoding enum and EnableBOM field
cat Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/DownloadBlob.cs

Repository: FrendsPlatform/Frends.AzureBlobStorage

Length of output: 6735


Migration silently loses EnableBOM=true behavior.

The CHANGELOG (3.0.0 upgrade instruction step 5) documents converting Source.Encoding=UTF8 + EnableBOM=true to Options.Encoding=UTF8WithBOM, but the migration.json only maps Source.Encoding directly and never examines the removed Source.EnableBOM field. Existing tasks with UTF-8 + BOM will migrate to UTF-8 without BOM, which contradicts the documented upgrade path.

The migration system does not appear to support conditional mapping across the codebase. Either implement a conditional migration step that maps to UTF8WithBOM when Source.EnableBOM is true and Source.Encoding is UTF8, or explicitly document this limitation in the upgrade instructions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.AzureBlobStorage.DownloadBlob/Frends.AzureBlobStorage.DownloadBlob/migration.json`
around lines 86 - 102, The migration currently maps Source.Encoding directly to
Options.Encoding and copies Source.FileEncodingString to Options.OtherEncoding
but ignores Source.EnableBOM, causing UTF8+EnableBOM to lose BOM; update the
migration.json to add a conditional mapping step that checks Source.Encoding ==
"UTF8" and Source.EnableBOM == true and sets Options.Encoding to "UTF8WithBOM"
(falling back to the existing Map rule for other cases), or if conditional steps
are not supported by your migration system, update the upgrade docs to
explicitly state that Source.EnableBOM must be manually re-applied to
Options.Encoding (UTF8WithBOM) after migration; reference the existing keys
Source.Encoding, Source.EnableBOM, Options.Encoding and the Map/Copy mapping
rules when making the change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichalFrends1 well, it's kinda true that's case we dont support with current migration logic, maybe ask Jefim if that's ok to just leave it or maybe add some note or idk

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

- Added `Result.Success` (bool) and `Result.Error` properties.
- Added `Options.ThrowErrorOnFailure` (default `true`) and `Options.ErrorMessageOnFailure` for non-throwing error handling.

### Upgrade instructions (2.x → 3.0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove those instrucions - we dont do that in changelog

- [Breaking] `Options.Encoding` replaces `Source.Encoding`; added `UTF8WithBOM` value; `WINDOWS1252` renamed to `Windows1252`.
- [Breaking] `Options.OtherEncoding` replaces `Source.FileEncodingString`; separate `EnableBOM` property removed (use `UTF8WithBOM`).
- [Breaking] `Result.FilePath` replaces `Result.FullPath`; `Result.FileName` and `Result.Directory` removed.
- Added `Result.Success` (bool) and `Result.Error` properties.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add second "Added" header and move there what was added

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants