Skip to content

feat: add native AzureBlob upload handler and receiver - #193

Merged
hamzahalq merged 2 commits into
releases/r8.0from
hamza/feature/native-azureblob-adapters
Jul 9, 2026
Merged

feat: add native AzureBlob upload handler and receiver#193
hamzahalq merged 2 commits into
releases/r8.0from
hamza/feature/native-azureblob-adapters

Conversation

@hamzahalq

Copy link
Copy Markdown
Contributor

Port the serverless AzureBlob handler and receiver into in-process native adapters, with collision-safe filenames, batch/folder limits, and a server-side copy for move-on-delete.

Port the serverless AzureBlob handler and receiver into in-process
native adapters, with collision-safe filenames, batch/folder limits,
and a server-side copy for move-on-delete.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

What changed:

  • Added native Azure Blob upload handler (NativeAzureBlobUploadHandler) and receiver (NativeAzureBlobReceiver) under SW.Bitween.NativeAdapters.
  • Introduced input models:
    • AzureBlobUploadHandlerInput (secure/required ConnectionString, required ContainerName, optional FileName/FileExtension).
    • AzureBlobReceiverInput (secure/required ConnectionString, required ContainerName, optional FolderName, BatchSize default 50, ResponseEncoding default utf8, optional DeleteMovesFileTo).
  • Upload behavior:
    • Generates collision-safe blob names using UTC timestamp + GUID when FileName isn’t provided.
    • Uploads with overwrite: true.
  • Receiver behavior:
    • Folder-scoped listing uses a true prefix boundary (FolderName + "/") and enforces BatchSize as the max number of returned blob names.
    • GetFile downloads blob content and returns either UTF-8 text (utf8) or Base64 string (base64).
    • DeleteFile supports move-on-delete via server-side copy (SyncCopyFromUriAsync) into DeleteMovesFileTo, preserving relative paths under FolderName to avoid destination collisions.
    • Move-on-delete is retry-safe by first checking whether the source blob still exists; if it’s already gone, the operation returns without error.
  • Hardening:
    • Trims whitespace from ConnectionString and ContainerName when creating the BlobContainerClient.

Risk: risk:medium

Security-sensitive areas touched:

  • Handling of Azure storage connection strings ([Secure] inputs) and direct access to Azure Blob Storage (read/download, write/upload, delete, and server-side copy within a container).
  • Move-on-delete (DeleteMovesFileTo) can relocate data within the same container; misconfiguration could change where deleted items end up or impact data retention/access patterns.

Test coverage impact:

  • No test changes are reflected in the provided summary; correctness depends on integration coverage for listing prefix boundaries, BatchSize limiting, encoding modes (utf8/base64), and the move-on-delete retry/idempotency behavior.

Deployment / migration / rollback / operational concerns:

  • Requires runtime availability of the new Azure.Storage.Blobs dependency.
  • The adapters are registered via DI; deployments that enable these registrations will start using Azure Blob operations when configured.
  • Rollback (code/config) won’t revert already uploaded/moved blobs in Azure; moved/copied objects remain unless later cleaned up externally.
  • Ensure the configured Azure credentials have permissions for listing (scoped by prefix), read/download, upload (overwrite), delete, and copy/move semantics (SyncCopyFromUriAsync).

Walkthrough

This PR adds Azure Blob Storage native adapters for receiving and uploading blobs, plus their configuration models, DI registration, and the Azure.Storage.Blobs package dependency.

Changes

Azure Blob native adapters

Layer / File(s) Summary
Azure Blob receiver: config and lifecycle
SW.Bitween.NativeAdapters/AzureBlobReceiver/AzureBlobReceiverInput.cs, SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs
Defines receiver configuration and initializes the Azure Blob container client from startup settings.
Azure Blob receiver: file operations
SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs
Lists blobs by prefix with batch limits, downloads blobs with configurable encoding, and deletes or moves blobs before deletion.
Azure Blob upload handler: config and Handle flow
SW.Bitween.NativeAdapters/AzureBlobUploadHandler/AzureBlobUploadHandlerInput.cs, SW.Bitween.NativeAdapters/AzureBlobUploadHandler/NativeAzureBlobUploadHandler.cs
Defines upload configuration and uploads XchangeFile data to Azure Blob Storage, returning the created blob reference.
DI registration and package dependency
SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs, SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj
Registers both adapters in dependency injection and adds the Azure.Storage.Blobs package reference.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: infra, security, risk:high

Suggested reviewers: AhmadRAbuhussein

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding native Azure Blob upload and receiver adapters.
Description check ✅ Passed The description matches the implemented Azure Blob native adapters and highlights key behaviors like batching, collision-safe names, and move-on-delete.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs`:
- Around line 56-75: The DeleteFile move-on-delete flow in
NativeAzureBlobReceiver is not retry-safe because it always attempts
SyncCopyFromUriAsync before deleting, even when the source blob was already
moved by a prior successful call. Update DeleteFile so it treats an
already-missing source fileId as a no-op when _options.DeleteMovesFileTo is set,
using the existing sourceBlob/targetBlob path logic to detect and skip the
copy-and-delete if the blob is gone. Keep the behavior localized to
NativeAzureBlobReceiver.DeleteFile and preserve the relative-path handling based
on _options.FolderName.
- Around line 24-36: The ListFiles method in NativeAzureBlobReceiver is using
_options.FolderName as a raw prefix for GetBlobsAsync, which can match sibling
blobs outside the intended virtual folder. Update the prefix used in ListFiles
so it is folder-scoped with a trailing slash before calling GetBlobsAsync, and
keep the batching logic in place so only blobs under the intended folder are
returned.
- Around line 67-71: The move logic in NativeAzureBlobReceiver should not use
the synchronous copy API with a bare source URI. Update the copy path in the
receiver method that builds sourceBlob and targetBlob to use
StartCopyFromUriAsync instead of SyncCopyFromUriAsync, and make sure the source
URI is authorized for private blobs (for example by using a SAS-backed URI or
equivalent authorization) before starting the copy.

In `@SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj`:
- Line 17: The Azure.Storage.Blobs package reference is outdated, so update the
PackageReference in the project file to the current NuGet version. Adjust the
existing Azure.Storage.Blobs entry in SW.Bitween.NativeAdapters.csproj from
12.23.0 to 12.29.1, keeping the same package name and location.
🪄 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: Repository: simplify9/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f36a5a6-63cb-4149-b11c-d2054f7e48bf

📥 Commits

Reviewing files that changed from the base of the PR and between b0fc0c9 and 7701738.

📒 Files selected for processing (6)
  • SW.Bitween.NativeAdapters/AzureBlobReceiver/AzureBlobReceiverInput.cs
  • SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs
  • SW.Bitween.NativeAdapters/AzureBlobUploadHandler/AzureBlobUploadHandlerInput.cs
  • SW.Bitween.NativeAdapters/AzureBlobUploadHandler/NativeAzureBlobUploadHandler.cs
  • SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj
  • SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs
📜 Review details
🔇 Additional comments (7)
SW.Bitween.NativeAdapters/AzureBlobReceiver/AzureBlobReceiverInput.cs (1)

1-25: LGTM!

SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs (2)

13-22: LGTM!


38-54: LGTM!

Also applies to: 77-85

SW.Bitween.NativeAdapters/AzureBlobUploadHandler/AzureBlobUploadHandlerInput.cs (1)

1-18: LGTM!

SW.Bitween.NativeAdapters/AzureBlobUploadHandler/NativeAzureBlobUploadHandler.cs (2)

30-38: LGTM!


11-28: 🩺 Stability & Availability

Ensure the blob container is available before upload. If this handler can run without external provisioning, add CreateIfNotExistsAsync; otherwise document the pre-created container contract.

SW.Bitween.NativeAdapters/ServiceCollectionExtensions.cs (1)

2-3: LGTM!

Also applies to: 51-56

Comment on lines +67 to +71
// Server-side copy: Azure moves the blob internally, so no bytes are
// downloaded or re-uploaded through this process.
var sourceBlob = _container.GetBlobClient(fileId);
var targetBlob = _container.GetBlobClient(targetName);
await targetBlob.SyncCopyFromUriAsync(sourceBlob.Uri);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current Azure.Storage.Blobs SyncCopyFromUriAsync size limits / auth model
curl -s "https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/main/articles/storage/blobs/storage-blob-copy.md" | grep -i -A3 "size limit\|same storage account"

Repository: simplify9/Bitween-api

Length of output: 1994


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo root =="
pwd
echo

echo "== locate NativeAzureBlobReceiver.cs =="
fd -a 'NativeAzureBlobReceiver.cs' . || true
echo

echo "== search for SyncCopyFromUriAsync usage =="
rg -n "SyncCopyFromUriAsync|StartCopyFromUriAsync|CopyFromUriAsync" . || true
echo

echo "== Azure docs excerpts =="
python3 - <<'PY'
import urllib.request, re
url = "https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/main/articles/storage/blobs/storage-blob-copy.md"
text = urllib.request.urlopen(url, timeout=20).read().decode("utf-8", "replace")
for pat in [
    r"(?is).{0,200}size limit.{0,500}",
    r"(?is).{0,200}same storage account.{0,500}",
    r"(?is).{0,200}private source.{0,500}",
    r"(?is).{0,200}StartCopyFromUriAsync.{0,500}",
]:
    m = re.search(pat, text)
    print("MATCH" if m else "NO MATCH", pat.split(".{0,200}")[-1][:40])
    if m:
        snippet = m.group(0)
        print(snippet[:1200])
        print("---")
PY

Repository: simplify9/Bitween-api

Length of output: 1965


🌐 Web query:

Azure Storage blob copy SyncCopyFromUriAsync size limit same storage account private source URI StartCopyFromUriAsync

💡 Result:

When copying blobs in Azure Storage using the.NET SDK, there is a critical distinction between SyncCopyFromUriAsync and StartCopyFromUriAsync regarding source blob size limits and operation behavior [1][2][3]. SyncCopyFromUriAsync This method wraps the Copy Blob From URL REST API [2][3]. It operates synchronously and is subject to a strict source blob size limit of 256 MiB [1][2]. If the source blob exceeds this size, the operation will fail with a 409 (Conflict) error [2][4]. Additionally, this method typically requires the source blob to be public or authorized via a Shared Access Signature (SAS), even when the source is in the same storage account [2][3][5]. StartCopyFromUriAsync This method is designed for asynchronous, long-running copy operations [6][7]. It does not have the same 256 MiB size limitation as the synchronous version [8][6][7]. When using this method, the copy process is initiated and runs in the background; you can track its progress using the returned CopyFromUriOperation object [6][9][7]. Summary Recommendation For source blobs larger than 256 MiB, you must use StartCopyFromUriAsync [2][6]. If you have a requirement to move data within the same storage account and prefer synchronous-like behavior or need to handle larger files, consider using methods that support partial updates (such as StageBlockFromUri combined with PutBlockList), which provide greater flexibility and avoid the size constraints of the single-request Copy Blob From URL operation [10].

Citations:


🌐 Web query:

Azure Storage Copy Blob From URL same account source URI SAS public access SyncCopyFromUriAsync authorization docs

💡 Result:

When performing a synchronous copy operation using SyncCopyFromUriAsync (or related methods like SyncUploadFromUriAsync) within the same Azure storage account, the operation essentially uses the Copy Blob REST API underneath [1][2]. Authorization Requirements: The underlying REST API requires that the source blob be accessible to the copy service [3]. If the source blob is not public, the URI provided to the operation must be authorized [3][4]. Even within the same account, simply providing the base URI of a private source blob will often fail with a 409 (CannotVerifyCopySource) or similar authorization error [3]. To authorize the copy, you should typically append a Shared Access Signature (SAS) token to the source blob URI [5][3]. The SAS token must grant at least Read ('r') permissions [4]. While Microsoft Entra ID (RBAC) is recommended for overall service client authorization, the copy operation specifically requires that the source URL itself provides the necessary access for the storage service to perform the read [3][4]. Key Considerations: 1. SAS Token: Generating a SAS URI using the source blob client is the standard approach to provide the required authorization for the operation [5]. 2. Public Access: If the container or blob is configured for public access, a SAS token may not be strictly required, though using one is considered a best practice for security [3]. 3. Sync vs. Async: SyncCopyFromUriAsync is intended for synchronous copies of smaller blobs (up to 256 MiB) [6][7]. For larger operations or scenarios where asynchronous scheduling is preferred, other methods like StartCopyFromUriAsync should be used [4]. In summary, to successfully use SyncCopyFromUriAsync for a private blob within the same account, you must ensure the source URI includes a valid SAS token with at least read permissions [5][3].

Citations:


Use StartCopyFromUriAsync here
SyncCopyFromUriAsync is limited to 256 MiB and will fail for private source blobs unless the source URI is authorized. sourceBlob.Uri is a bare private URL, so this move path will break unless the blob/container is public or you append a SAS; the synchronous call also ties up the request thread for the copy duration.

🤖 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 `@SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs`
around lines 67 - 71, The move logic in NativeAzureBlobReceiver should not use
the synchronous copy API with a bare source URI. Update the copy path in the
receiver method that builds sourceBlob and targetBlob to use
StartCopyFromUriAsync instead of SyncCopyFromUriAsync, and make sure the source
URI is authorized for private blobs (for example by using a SAS-backed URI or
equivalent authorization) before starting the copy.

</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
curl -s https://api.nuget.org/v3-flatcontainer/azure.storage.blobs/index.json | jq '.versions[-5:]'
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NUGET, package: "Azure.Storage.Blobs") {
    nodes {
      advisory { summary severity publishedAt }
      vulnerableVersionRange
      firstPatchedVersion { identifier }
    }
  }
}'

Repository: simplify9/Bitween-api

Length of output: 1994


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== latest versions =="
curl -fsSL https://api.nuget.org/v3-flatcontainer/azure.storage.blobs/index.json \
  | jq -r '.versions[-10:][]'

echo
echo "== vulnerability feed metadata =="
curl -fsSL https://api.nuget.org/v3/vulnerabilities/index.json \
  | jq '{vulnerabilities: (.vulnerabilities // .packageVulnerabilities // .data // .)}' | sed -n '1,160p'

Repository: simplify9/Bitween-api

Length of output: 1965


🌐 Web query:

Azure.Storage.Blobs NuGet latest version vulnerability advisories

💡 Result:

As of July 8, 2026, the latest version of the Azure.Storage.Blobs NuGet package is 12.29.1 [1][2][3]. Regarding security and vulnerability advisories: Official Security Reporting: Microsoft manages security vulnerabilities for the Azure SDK for.NET through the Microsoft Security Response Center (MSRC) [4][5]. Users are advised not to report vulnerabilities via public GitHub issues but to use the official MSRC channels [4][6]. Historical Vulnerabilities: The package has had historical security disclosures, such as CVE-2022-30187, which involved potential information exposure when using client-side encryption [7][8][9]. This was addressed in version 12.13.0 [7]. Additionally, broader vulnerabilities affecting the Azure SDK for.NET, such as information disclosure (CVE-2022-26907) [10] or issues in related dependencies like Azure Identity (e.g., CVE-2023-36414) [11], have historically impacted the ecosystem. Recommended Best Practices: 1. Always Use the Latest Version: Microsoft strongly recommends keeping client library versions up to date to receive the latest security patches, bug fixes, and performance improvements [12]. 2. Monitor Official Sources: For the most accurate and up-to-date vulnerability information, consult the Microsoft Security Update Guide (MSRC) [5] or the official Azure SDK for.NET GitHub repository security page [4]. 3. Changelog Review: You can review specific release notes and changes in the project's official CHANGELOG.md on GitHub [3].

Citations:


Bump Azure.Storage.Blobs 12.23.0 is several releases behind the current NuGet version (12.29.1); update it to keep the dependency current.

🤖 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 `@SW.Bitween.NativeAdapters/SW.Bitween.NativeAdapters.csproj` at line 17, The
Azure.Storage.Blobs package reference is outdated, so update the
PackageReference in the project file to the current NuGet version. Adjust the
existing Azure.Storage.Blobs entry in SW.Bitween.NativeAdapters.csproj from
12.23.0 to 12.29.1, keeping the same package name and location.

Trim whitespace from connection settings, scope folder listing to a
true prefix boundary, and make move-on-delete safe to retry.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs (1)

42-48: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Dispose the Content stream after copy.

download.Value.Content is never disposed, leaking the download stream. Use await using to ensure cleanup even on exception.

🔧 Proposed fix
         var blobClient = _container.GetBlobClient(fileId);
         var download = await blobClient.DownloadAsync();

-        using var memoryStream = new MemoryStream();
-        await download.Value.Content.CopyToAsync(memoryStream);
-        var bytes = memoryStream.ToArray();
+        await using var content = download.Value.Content;
+        using var memoryStream = new MemoryStream();
+        await content.CopyToAsync(memoryStream);
+        var bytes = memoryStream.ToArray();
🤖 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 `@SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs`
around lines 42 - 48, The GetFile method in NativeAzureBlobReceiver leaves
download.Value.Content undisposed after CopyToAsync, which can leak the download
stream. Update the download handling in GetFile to wrap download.Value.Content
in await using (or otherwise ensure it is disposed in a finally block) while
preserving the existing MemoryStream copy and XchangeFile construction.
🤖 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.

Outside diff comments:
In `@SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs`:
- Around line 42-48: The GetFile method in NativeAzureBlobReceiver leaves
download.Value.Content undisposed after CopyToAsync, which can leak the download
stream. Update the download handling in GetFile to wrap download.Value.Content
in await using (or otherwise ensure it is disposed in a finally block) while
preserving the existing MemoryStream copy and XchangeFile construction.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: simplify9/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: df15e195-e20c-405d-9a18-4e880c724fe8

📥 Commits

Reviewing files that changed from the base of the PR and between 7701738 and 2fbb8eb.

📒 Files selected for processing (1)
  • SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs
📜 Review details
🔇 Additional comments (3)
SW.Bitween.NativeAdapters/AzureBlobReceiver/NativeAzureBlobReceiver.cs (3)

78-81: 🩺 Stability & Availability | 🏗️ Heavy lift

SyncCopyFromUriAsync issue still unresolved.

This was flagged in a prior review: SyncCopyFromUriAsync is limited to 256 MiB source blobs and will fail for private source blobs when passed a bare URI (sourceBlob.Uri) without SAS authorization. The code still uses this API unchanged. Consider switching to StartCopyFromUriAsync (no size limit, async) or appending a read-scoped SAS token to the source URI.


24-30: LGTM!


60-67: LGTM!

@hamzahalq
hamzahalq merged commit 58673ae into releases/r8.0 Jul 9, 2026
2 checks passed
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.

2 participants