Skip to content

Frends.odf.write spreadsheet#8

Merged
MichalFrends1 merged 13 commits into
mainfrom
Frends.Odf.WriteSpreadsheet
Jun 15, 2026
Merged

Frends.odf.write spreadsheet#8
MichalFrends1 merged 13 commits into
mainfrom
Frends.Odf.WriteSpreadsheet

Conversation

@MichalFrends1

@MichalFrends1 MichalFrends1 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Please review my changes :)

Review Checklist

1. Frends Task Project File

  • Path: Frends.*/Frends.*/*.csproj
  • Contains required fields:
    • <TargetFramework>net8.0</TargetFramework>
    • <Version>x.0.0</Version>
    • <Authors>Frends</Authors>
    • <PackageLicenseExpression>MIT</PackageLicenseExpression>
    • <GenerateDocumentationFile>true</GenerateDocumentationFile>
    • <Description>
    • [ ]
      <RepositoryUrl>https://github.com/FrendsPlatform/Frends.SYSTEM/tree/main/Frends.SYSTEM.ACTION</RepositoryUrl>
    • <Nullable>disable</Nullable>
  • Contains required package references:
    • StyleCop.Analyzers v1.2.0-beta.556
    • FrendsTaskAnalyzers v1.*
  • Contains required files:
    • <Content Include="migration.json" PackagePath="/" Pack="true"/>
    • <Content Include="../CHANGELOG.md" PackagePath="/" Pack="true"/>
    • <AdditionalFiles Include="FrendsTaskMetadata.json" PackagePath="/" Pack="true"/>
  • Auto formatting applied

2. Frends Task Test Project File

  • Path: Frends.*/Frends.*.Tests/*.Tests.csproj
  • Contains required fields:
    • <TargetFramework>net8.0</TargetFramework>
    • <IsPackable>false</IsPackable>
    • <Nullable>disable</Nullable>
  • Contains required package references:
    • StyleCop.Analyzers v1.2.0-beta.556
  • Auto formatting applied

3. Additional Files

  • Present only one LICENSE file per repository
    • Should be MIT License unless otherwise specified
  • Present only one .gitignore file per repository
    • Includes .idea/ folders
  • Present: Frends.*/README.md
    • Contains badges (build, license, coverage)
    • Includes developer setup instructions
    • Includes test setup instructions
    • Does not include parameter descriptions
  • Present: Frends.*/CHANGELOG.md
    • Includes all functional changes
    • Indicates breaking changes with upgrade notes
    • Avoids non-functional notes like "refactored xyz"
    • Uses the KeepAChangelog format
  • Present: Frends.*/Frends.*/FrendsTaskMetadata.json
    • Contains task method reference Frends.System.Action.System.Action
  • Present: Frends.*/Frends.*/migration.json
    • Contains breaking change migration information for Frends if breaking changes exist
  • StyleCop.Analyzers suppression files added and setup:
    • Present: Frends.*/Frends.*/GlobalSuppressions.cs
    • Present: Frends.*/Frends.*.Tests/GlobalSuppressions.cs
    • Follows standards from Frends Task Template
  • Auto formatting applied

4. Source Code

  • Solution builds
  • File-scoped namespace applied
  • Usings placed before the namespace
  • Unused code is removed
  • Warnings resolved (if possible)
  • Follows Microsoft C# code conventions
  • Typos and grammar mistakes resolved
  • Auto formatting applied

5. GitHub Actions Workflows

  • Path: .github/workflows/*.yml
  • Task has required workflow files:
    • *_release.yml
      • contains secret feed_api_key: ${{ secrets.TASKS_FEED_API_KEY }}
    • *_test_on_main.yml
      • contains secret badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
    • *_test_on_push.yml
      • contains secret badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
      • contains secret test_feed_api_key: ${{ secrets.TASKS_TEST_FEED_API_KEY }}
  • default permissions set for GITHUB_TOKEN
  • workdir: Frends.SYSTEM.ACTION
  • strict_analyzers: true
  • dotnet_version: 8.0.x
  • Docker setup included if task depends on external system (prebuild_command: docker-compose up -d)

Summary by CodeRabbit

Release Notes

  • New Features

    • New WriteSpreadsheet task to generate OpenDocument Spreadsheet (.ods) files from JSON payloads
    • Support for configurable header rows, custom error messages, and file overwrite handling
    • Automatic data type detection for strings, numbers, booleans, and dates
  • Chores

    • Added CI/CD workflows for automated testing and release processes
    • Added comprehensive test coverage and documentation

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR introduces the complete Frends.Odf.WriteSpreadsheet task from scratch. It adds public API definitions (Input, Options, Result, Error, ActionOnExistingFile), internal helpers (OdfSpreadsheetWriter, ErrorHandler, ValidationHandler), the main Odf.WriteSpreadsheet entry point, a full NUnit test suite, project/solution files, Frends task metadata, CI workflows, and documentation.

Changes

Frends.Odf.WriteSpreadsheet task

Layer / File(s) Summary
Public API contracts
Frends.Odf.WriteSpreadsheet/Definitions/Enums.cs, Definitions/Error.cs, Definitions/Input.cs, Definitions/Options.cs, Definitions/Result.cs
Defines ActionOnExistingFile enum, Input (Payload, FilePath, ActionOnExistingFile), Options (IncludeHeaderRow, ThrowErrorOnFailure, ErrorMessageOnFailure), Result (Success, FilePath, Error), and Error (Message, AdditionalInfo).
Validation and error-handling helpers
Helpers/ValidationHandler.cs, Helpers/ErrorHandler.cs
ValidationHandler.Run performs attribute-based validation on Input/Options and throws on failure. ErrorHandler.Handle converts exceptions to failed Result objects or rethrows based on Options, including cancellation and custom message handling.
ODF XML data injection
Helpers/OdfSpreadsheetWriter.cs
OdfSpreadsheetWriter.InjectData removes existing table rows from an ODF XML element, collects headers from the JSON array, optionally writes a header row, then generates per-row ODF cells with office:value-type attributes mapped from JTokenType (float, boolean, date, string).
WriteSpreadsheet entry point
Frends.Odf.WriteSpreadsheet.cs
Odf.WriteSpreadsheet validates inputs, normalises the file path, loads the embedded template.ods as a ZIP, rewrites content.xml via OdfSpreadsheetWriter, writes to a temp file, then commits to the destination using ActionOnExistingFile semantics with temp-file cleanup on failure.
Project wiring and Frends metadata
Frends.Odf.WriteSpreadsheet.csproj, FrendsTaskMetadata.json, migration.json, GlobalSuppressions.cs, Frends.Odf.WriteSpreadsheet.sln
Configures net8.0 target, NuGet metadata, Newtonsoft.Json and analyzer dependencies, embeds Resources\template.ods, and registers the task method in FrendsTaskMetadata.json with a 1.0.0 migration entry.
NUnit test suite
Tests/Frends.Odf.WriteSpreadsheet.Tests.csproj, Tests/TestBase.cs, Tests/Helpers/TestHelper.cs, Tests/ErrorHandlerTest.cs, Tests/FunctionalTests.cs, Tests/GlobalSuppressions.cs
TestBase manages temp file lifecycle; TestHelper.ReadOdsContent extracts content.xml from the output ZIP. ErrorHandlerTest covers throw-vs-result and custom message behavior. FunctionalTests (11 tests) covers write, headers, path/JSON errors, overwrite/throw, empty payload, unicode, formula strings, partial rows, and value-type assertions.
CI workflows and documentation
.github/workflows/WriteSpreadsheet_*.yml, Frends.Odf.WriteSpreadsheet/README.md, Frends.Odf.WriteSpreadsheet/CHANGELOG.md, README.md
Adds release, test-on-main, and test-on-push GitHub Actions workflows for the subproject, plus component and root README entries and the initial CHANGELOG entry.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Odf
  participant ValidationHandler
  participant OdfSpreadsheetWriter
  participant FileSystem

  Caller->>Odf: WriteSpreadsheet(input, options, ct)
  Odf->>ValidationHandler: Run(input, options)
  ValidationHandler-->>Odf: ok or ValidationException
  Odf->>Odf: Normalise path, verify .ods ext, check directory
  Odf->>Odf: Parse Payload as JArray
  Odf->>Odf: Load embedded template.ods as ZipArchive
  Odf->>Odf: Extract and parse content.xml
  Odf->>OdfSpreadsheetWriter: InjectData(firstTable, jArray, includeHeaderRow, namespaces, ct)
  OdfSpreadsheetWriter-->>Odf: ODF rows written into XElement
  Odf->>Odf: Replace content.xml entry in ZipArchive
  Odf->>FileSystem: Write updated archive to temp file
  alt ActionOnExistingFile.Overwrite
    Odf->>FileSystem: File.Move(temp → dest, overwrite=true)
  else ActionOnExistingFile.Throw
    Odf->>FileSystem: File.Move(temp → dest, overwrite=false)
  end
  FileSystem-->>Odf: committed
  Odf-->>Caller: Result { Success=true, FilePath }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • FrendsPlatform/Frends.Odf#5: Adds the same Frends.Odf.WriteSpreadsheet task structure including workflows, definitions, helpers, and tests — directly overlaps with this PR's content.

Poem

🐇 A spreadsheet from JSON, oh what a treat,
The rabbit hopped in and made columns neat!
ODF tables with types — float, bool, and date,
Templates embedded, the data looks great.
content.xml patched, the ZIP sealed with care,
A new .ods file blooms fresh in the air! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and uses non-standard formatting (mixed casing) without clearly conveying the main change or deliverable. Revise the title to clearly describe the primary change, e.g., 'Add WriteSpreadsheet task to Frends.Odf library' or 'Implement ODS file generation from JSON payload'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Frends.Odf.WriteSpreadsheet

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.

@MichalFrends1

Copy link
Copy Markdown
Contributor Author

@coderabbitai do review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Fetch PR details
gh pr view 8 --repo FrendsPlatform/Frends.Odf --json title,body,files,additions,deletions,commits

echo "=== PR FILES ==="
gh pr view 8 --repo FrendsPlatform/Frends.Odf --json files --jq '.files[].path'

Length of output: 13865


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List all files in the repo (PR branch)
tree -L 5 --noreport

Length of output: 3694


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read key project files
echo "=== CSPROJ FILES ==="
fd -e csproj --exec cat {}

echo ""
echo "=== SOLUTION FILE ==="
fd -e sln --exec cat {}

Length of output: 22342


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== WORKFLOWS ==="
fd -e yml -p ".github/workflows" --exec cat {}

echo ""
echo "=== CHANGELOG ==="
fd -g "CHANGELOG.md" --exec cat {}

echo ""
echo "=== README ==="
fd -g "README.md" --exec cat {}

Length of output: 6066


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== C# SOURCE FILES ==="
fd -e cs --exec echo "--- FILE: {}" \; --exec cat {} \;

echo ""
echo "=== FrendsTaskMetadata.json ==="
fd -g "FrendsTaskMetadata.json" --exec cat {}

echo ""
echo "=== migration.json ==="
fd -g "migration.json" --exec cat {}

echo ""
echo "=== GlobalSuppressions.cs ==="
fd -g "GlobalSuppressions.cs" --exec cat {}

Length of output: 139631


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== WORKFLOW FILES ==="
cat .github/workflows/WriteSpreadsheet_release.yml
echo "---"
cat .github/workflows/WriteSpreadsheet_test_on_main.yml
echo "---"
cat .github/workflows/WriteSpreadsheet_test_on_push.yml

echo ""
echo "=== .gitignore ==="
cat .gitignore 2>/dev/null || echo "(not found)"

Length of output: 2957



@MichalFrends1 Here is my review of the Frends.Odf.WriteSpreadsheet PR.


Review Checklist Results

✅ 1. Task Project File (Frends.Odf.WriteSpreadsheet.csproj)

All required fields are present and correct:

  • net8.0, 1.0.0, Frends, MIT, GenerateDocumentationFile, Description, RepositoryUrl, Nullable disable
  • StyleCop.Analyzers v1.2.0-beta.556, FrendsTaskAnalyzers v1.*
  • migration.json, CHANGELOG.md, FrendsTaskMetadata.json content references

Note: The last <ItemGroup> in the csproj uses tab indentation while the rest uses spaces — this is consistent with other tasks in the repo, but worth keeping in mind for future formatting passes.


✅ 2. Test Project File (Frends.Odf.WriteSpreadsheet.Tests.csproj)

  • net8.0, IsPackable false, Nullable disable, StyleCop.Analyzers v1.2.0-beta.556

✅ 3. Additional Files

File Status
LICENSE (one, root)
.gitignore (includes .idea/)
README.md (badges, build setup, no param descriptions)
CHANGELOG.md (KeepAChangelog format)
FrendsTaskMetadata.json (Frends.Odf.WriteSpreadsheet.Odf.WriteSpreadsheet)
migration.json
GlobalSuppressions.cs (main + tests)
Root README.md updated

⚠️ 4. Source Code — Issues Found

1. Dead code in OdfSpreadsheetWriter.cs (second loop redundant guard)

The first foreach loop over jsonArray already validates every item is a JObject and throws ArgumentException if not. The second loop's guard if (item is not JObject jObject) can never be reached:

// In the second foreach loop — this throw is unreachable
if (item is not JObject jObject)
    throw new ArgumentException("The JSON payload must contain valid JSON objects.");

Consider removing it to keep the code clean.


2. Missing early-exit for ActionOnExistingFile.Throw when file already exists (Frends.Odf.WriteSpreadsheet.cs)

Unlike WriteTextDocument, which checks File.Exists before doing any template processing, WriteSpreadsheet reads the embedded template, builds the full in-memory archive, and writes a temp file — only to fail at File.Move when the destination exists and ActionOnExistingFile is Throw. Adding a pre-check saves unnecessary work:

if (input.ActionOnExistingFile == ActionOnExistingFile.Throw && File.Exists(normalizedPath))
    throw new IOException($"File already exists: {normalizedPath}");

Place this after the directory validation, before opening the embedded template.


3. Misleading test name in FunctionalTests.cs

Should_Escape_Formula_Injection implies sanitization/escaping is happening. In reality, no escaping occurs — the values are simply stored as office:value-type="string" ODS cells, which prevents formula execution. Consider renaming to Should_Store_Formula_Like_Values_As_Strings to avoid confusion.


4. Date ToString("s") omits timezone info (OdfSpreadsheetWriter.cs)

cell.Add(new XAttribute(officeNamespace + "date-value",
    ((DateTime)property.Value).ToString("s")));

The "s" format produces "yyyy-MM-ddTHH:mm:ss" with no UTC offset. The ODF specification recommends including timezone information (e.g., appending Z for UTC). Consider "yyyy-MM-ddTHH:mm:ssZ" or "o" format to be spec-compliant.


5. Newtonsoft.Json version inconsistency

Frends.Odf.WriteSpreadsheet.csproj references Newtonsoft.Json v13.0.4, while Frends.Odf.WriteTextDocument.csproj uses v13.0.3. Consider aligning to the same version across all tasks in the repo.


✅ 5. GitHub Actions Workflows

All three workflow files are correct:

Workflow Required secrets workdir strict_analyzers dotnet_version
WriteSpreadsheet_release.yml TASKS_FEED_API_KEY
WriteSpreadsheet_test_on_main.yml BADGE_SERVICE_API_KEY
WriteSpreadsheet_test_on_push.yml BADGE_SERVICE_API_KEY + TASKS_TEST_FEED_API_KEY
  • Default permissions set correctly (contents: write for release, contents: read for tests) ✅
  • No Docker needed (no external dependencies) ✅

Summary

The implementation is solid and follows the patterns of the other tasks in this repository. All checklist requirements pass. The items above are non-blocking improvements — issues 1, 2, and 3 are the most worth addressing before merge.

@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

🧹 Nitpick comments (2)
Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.cs (1)

139-146: 💤 Low value

IOException catch may produce misleading error message.

The catch block at line 143-146 wraps all IOException instances with "File already exists" message. However, File.Move can throw IOException for reasons other than file existence (e.g., permissions, path too long, disk I/O errors). While the original exception is preserved as InnerException, the wrapper message could misdirect debugging.

Consider checking the IOException.HResult or simply preserving the original message:

♻️ Suggested improvement
                     try
                     {
                         File.Move(tempFilePath, normalizedPath, false);
                     }
                     catch (IOException ex)
                     {
-                        throw new IOException($"File already exists: {normalizedPath}", ex);
+                        throw new IOException($"Cannot write to destination: {normalizedPath}. {ex.Message}", ex);
                     }
🤖 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.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.cs`
around lines 139 - 146, The IOException catch block at the File.Move call site
is wrapping all IOException instances with a generic "File already exists"
message, even though File.Move can fail for multiple reasons such as permissions
or disk I/O errors. Modify the catch block to check the specific cause of the
IOException before deciding how to handle it. Check the HResult property or
other IOException properties to determine if the error is specifically about
file existence, and only wrap with the "File already exists" message in that
case. For other IOException causes, either re-throw the original exception or
provide a more appropriate error message that reflects the actual problem.
Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.csproj (1)

28-28: Pin FrendsTaskAnalyzers to an exact version.

Line 28 uses a floating version (1.*), which can make analyzer behavior drift across restores/CI runs. Pinning an explicit tested version (e.g., 1.2.0) ensures deterministic builds. Note: This pattern appears across all .csproj files in the repository; consider addressing them consistently.

🤖 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.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.csproj`
at line 28, The FrendsTaskAnalyzers PackageReference uses a floating version
constraint (Version="1.*") which can cause non-deterministic builds across
different restores and CI runs. Change the Version attribute from "1.*" to an
exact version number (e.g., "1.2.0") to pin it to a specific tested version.
This same fix should be applied consistently across all other .csproj files in
the repository that use the same floating version pattern for this package.
🤖 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 @.github/workflows/WriteSpreadsheet_release.yml:
- Line 10: Replace all mutable `@main` branch references with immutable commit
SHAs in the reusable workflow calls to mitigate supply-chain risk. In
.github/workflows/WriteSpreadsheet_release.yml at line 10, replace the ref
`@main` in the uses statement for release.yml with a specific commit SHA. In
.github/workflows/WriteSpreadsheet_test_on_main.yml at line 15, replace the ref
`@main` in the uses statement for linux_build_main.yml with a specific commit
SHA. In .github/workflows/WriteSpreadsheet_test_on_push.yml at line 15, replace
the ref `@main` in the uses statement for linux_build_test.yml with a specific
commit SHA.

In `@Frends.Odf.WriteSpreadsheet/CHANGELOG.md`:
- Around line 3-7: The 1.0.0 changelog entry in the CHANGELOG.md file is too
generic with only "Initial implementation" listed. Replace this vague
description with specific functional changes that were actually shipped, such as
the ability to create .ods spreadsheet files from JSON payload, details about
overwrite vs. throw behavior when files already exist, header row options, and
any other concrete features. Ensure the description follows the Keep a Changelog
format and add upgrade notes indicating any breaking changes (relevant for
future releases). Make the entry detailed enough that users understand exactly
what functionality is available in version 1.0.0.

In
`@Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ErrorHandler.cs`:
- Around line 26-36: The ThrowIfCanceled method uses throw exception which
resets the stack trace, making debugging harder. Change this to use bare throw
statement to preserve the original stack trace. Similarly, in the
ThrowBaseException method, when no custom message is provided (when
string.IsNullOrEmpty returns true), rethrow the original exception using a bare
throw statement instead of wrapping it in a new Exception object, which
unnecessarily changes the exception type and loses the original exception
context. Reserve wrapping in a new Exception only when a custom message is
actually provided.

In
`@Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/OdfSpreadsheetWriter.cs`:
- Around line 70-76: Remove the unreachable guard clause that checks if an item
is not a JObject in the second foreach loop within the method. This validation
is dead code because the first foreach loop already validates that all items in
the JSON array are JObject instances and throws an ArgumentException if any item
fails this check. Since the array contents don't change between the two loops,
the identical check in the second loop will never execute and should be deleted
to maintain code cleanliness per guidelines.

---

Nitpick comments:
In
`@Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.cs`:
- Around line 139-146: The IOException catch block at the File.Move call site is
wrapping all IOException instances with a generic "File already exists" message,
even though File.Move can fail for multiple reasons such as permissions or disk
I/O errors. Modify the catch block to check the specific cause of the
IOException before deciding how to handle it. Check the HResult property or
other IOException properties to determine if the error is specifically about
file existence, and only wrap with the "File already exists" message in that
case. For other IOException causes, either re-throw the original exception or
provide a more appropriate error message that reflects the actual problem.

In
`@Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.csproj`:
- Line 28: The FrendsTaskAnalyzers PackageReference uses a floating version
constraint (Version="1.*") which can cause non-deterministic builds across
different restores and CI runs. Change the Version attribute from "1.*" to an
exact version number (e.g., "1.2.0") to pin it to a specific tested version.
This same fix should be applied consistently across all other .csproj files in
the repository that use the same floating version pattern for this package.
🪄 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: 22bc026d-b66e-4ed1-a9cf-f17a7fff9330

📥 Commits

Reviewing files that changed from the base of the PR and between 123a6b8 and 93de945.

📒 Files selected for processing (27)
  • .github/workflows/WriteSpreadsheet_release.yml
  • .github/workflows/WriteSpreadsheet_test_on_main.yml
  • .github/workflows/WriteSpreadsheet_test_on_push.yml
  • Frends.Odf.WriteSpreadsheet/CHANGELOG.md
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/ErrorHandlerTest.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/Frends.Odf.WriteSpreadsheet.Tests.csproj
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/FunctionalTests.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/GlobalSuppressions.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/Helpers/TestHelper.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.Tests/TestBase.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.sln
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Definitions/Enums.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Definitions/Error.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Definitions/Input.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Definitions/Options.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Definitions/Result.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet.csproj
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/FrendsTaskMetadata.json
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/GlobalSuppressions.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ErrorHandler.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/OdfSpreadsheetWriter.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ValidationHandler.cs
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Resources/template.ods
  • Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/migration.json
  • Frends.Odf.WriteSpreadsheet/README.md
  • README.md


jobs:
build:
uses: FrendsPlatform/FrendsTasks/.github/workflows/release.yml@main

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the workflow files exist and read the relevant lines
cd .github/workflows

echo "=== WriteSpreadsheet_release.yml ===" 
if [ -f WriteSpreadsheet_release.yml ]; then
  sed -n '8,12p' WriteSpreadsheet_release.yml
else
  echo "File not found"
fi

echo ""
echo "=== WriteSpreadsheet_test_on_main.yml ===" 
if [ -f WriteSpreadsheet_test_on_main.yml ]; then
  sed -n '13,17p' WriteSpreadsheet_test_on_main.yml
else
  echo "File not found"
fi

echo ""
echo "=== WriteSpreadsheet_test_on_push.yml ===" 
if [ -f WriteSpreadsheet_test_on_push.yml ]; then
  sed -n '13,17p' WriteSpreadsheet_test_on_push.yml
else
  echo "File not found"
fi

Repository: FrendsPlatform/Frends.Odf

Length of output: 630


Pin all three reusable-workflow refs to immutable commit SHAs instead of mutable @main branches. Mutable refs allow upstream changes to affect your workflows without awareness, creating a supply-chain risk.

  • .github/workflows/WriteSpreadsheet_release.yml#L10: replace release.yml@main with release.yml@<commit_sha>
  • .github/workflows/WriteSpreadsheet_test_on_main.yml#L15: replace linux_build_main.yml@main with linux_build_main.yml@<commit_sha>
  • .github/workflows/WriteSpreadsheet_test_on_push.yml#L15: replace linux_build_test.yml@main with linux_build_test.yml@<commit_sha>
🧰 Tools
🪛 zizmor (1.25.2)

[error] 10-10: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

📍 Affects 3 files
  • .github/workflows/WriteSpreadsheet_release.yml#L10-L10 (this comment)
  • .github/workflows/WriteSpreadsheet_test_on_main.yml#L15-L15
  • .github/workflows/WriteSpreadsheet_test_on_push.yml#L15-L15
🤖 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 @.github/workflows/WriteSpreadsheet_release.yml at line 10, Replace all
mutable `@main` branch references with immutable commit SHAs in the reusable
workflow calls to mitigate supply-chain risk. In
.github/workflows/WriteSpreadsheet_release.yml at line 10, replace the ref
`@main` in the uses statement for release.yml with a specific commit SHA. In
.github/workflows/WriteSpreadsheet_test_on_main.yml at line 15, replace the ref
`@main` in the uses statement for linux_build_main.yml with a specific commit
SHA. In .github/workflows/WriteSpreadsheet_test_on_push.yml at line 15, replace
the ref `@main` in the uses statement for linux_build_test.yml with a specific
commit SHA.

Source: Linters/SAST tools

Comment on lines +3 to +7
## [1.0.0] - 2026-06-08

### Added

- Initial implementation

Copy link
Copy Markdown

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

Expand the 1.0.0 entry with concrete functional changes.

The current note (“Initial implementation”) is too generic. Please list the shipped behavior (e.g., creating .ods from JSON payload, overwrite/throw behavior, header options), and add upgrade notes if any future release introduces breaking changes.

As per coding guidelines, “Validate format against Keep a Changelog … Include all functional changes and indicate breaking changes with upgrade notes.”

🤖 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.Odf.WriteSpreadsheet/CHANGELOG.md` around lines 3 - 7, The 1.0.0
changelog entry in the CHANGELOG.md file is too generic with only "Initial
implementation" listed. Replace this vague description with specific functional
changes that were actually shipped, such as the ability to create .ods
spreadsheet files from JSON payload, details about overwrite vs. throw behavior
when files already exist, header row options, and any other concrete features.
Ensure the description follows the Keep a Changelog format and add upgrade notes
indicating any breaking changes (relevant for future releases). Make the entry
detailed enough that users understand exactly what functionality is available in
version 1.0.0.

Source: Coding guidelines

Comment on lines +26 to +36
private static void ThrowIfCanceled(Exception exception, bool throwCanceled = true)
{
if (throwCanceled && exception is OperationCanceledException) throw exception;
}

private static void ThrowBaseException(Exception exception, string customMessage = null)
{
if (string.IsNullOrEmpty(customMessage))
throw new Exception(exception.Message, exception);

throw new Exception(customMessage, exception);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In C#, does throw ex;reset stack trace, and isExceptionDispatchInfo.Capture(ex).Throw() the recommended way to preserve stack trace when rethrowing outside a catch block?

💡 Result:

Yes, using throw ex; resets the stack trace in C# [1][2][3]. When you use this syntax, the runtime treats the exception as if it were thrown from the current catch block, causing the original stack trace information to be lost and replaced with the current location [1][4][5]. To preserve the stack trace when rethrowing an exception: 1. Within a catch block: Use the parameterless throw; statement [3][6]. This is the recommended and standard way to rethrow an exception while maintaining the original stack trace [4][7][5]. 2. Outside of a catch block: Using ExceptionDispatchInfo.Capture(ex).Throw is indeed the recommended and correct approach [6]. This allows you to capture an exception object at one point in time and rethrow it later, effectively preserving the original stack trace as if it had flowed from the point of capture to the point of rethrow [8][9][6]. Using throw ex; is widely discouraged because it makes debugging significantly more difficult by obscuring the true source of the error [1][7]. Always prefer throw; when inside a catch block, or ExceptionDispatchInfo when you need to capture and defer the rethrow [6].

Citations:


🏁 Script executed:

# Check if the file exists and examine its structure
fd -i "errorhandler.cs" --type f

Repository: FrendsPlatform/Frends.Odf

Length of output: 389


🏁 Script executed:

# Read the ErrorHandler.cs file to verify the code shown in the review
cat -n "Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ErrorHandler.cs"

Repository: FrendsPlatform/Frends.Odf

Length of output: 2499


🏁 Script executed:

# Search for usages of the ThrowIfCanceled and ThrowBaseException methods to understand the impact
rg "ThrowIfCanceled|ThrowBaseException" -n

Repository: FrendsPlatform/Frends.Odf

Length of output: 2737


🏁 Script executed:

# Check the actual imports in the ErrorHandler.cs file
head -20 "Frends.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ErrorHandler.cs"

Repository: FrendsPlatform/Frends.Odf

Length of output: 997


Preserve original exception type and stack trace when rethrowing.

Line 28 uses throw exception; which resets the stack trace, making debugging significantly harder. Line 33–34 unnecessarily wraps the exception in a new Exception object, changing the exception type even when no custom message is provided. When no custom message is set, rethrow the original exception with its type and stack trace intact.

Proposed fix
 using System;
+using System.Runtime.ExceptionServices;
 using Frends.Odf.WriteSpreadsheet.Definitions;
@@
     private static void ThrowIfCanceled(Exception exception, bool throwCanceled = true)
     {
-        if (throwCanceled && exception is OperationCanceledException) throw exception;
+        if (throwCanceled && exception is OperationCanceledException)
+            ExceptionDispatchInfo.Capture(exception).Throw();
     }
@@
     private static void ThrowBaseException(Exception exception, string customMessage = null)
     {
         if (string.IsNullOrEmpty(customMessage))
-            throw new Exception(exception.Message, exception);
+            ExceptionDispatchInfo.Capture(exception).Throw();
 
         throw new Exception(customMessage, exception);
     }
🤖 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.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/ErrorHandler.cs`
around lines 26 - 36, The ThrowIfCanceled method uses throw exception which
resets the stack trace, making debugging harder. Change this to use bare throw
statement to preserve the original stack trace. Similarly, in the
ThrowBaseException method, when no custom message is provided (when
string.IsNullOrEmpty returns true), rethrow the original exception using a bare
throw statement instead of wrapping it in a new Exception object, which
unnecessarily changes the exception type and loses the original exception
context. Reserve wrapping in a new Exception only when a custom message is
actually provided.

Comment on lines +70 to +76
foreach (var item in jsonArray)
{
cancellationToken.ThrowIfCancellationRequested();

if (item is not JObject jObject)
throw new ArgumentException("The JSON payload must contain valid JSON objects.");

Copy link
Copy Markdown

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

Remove unreachable guard clause.

The check at lines 74-75 is dead code. The first foreach loop (lines 34-52) already validates that every item is a JObject and throws ArgumentException if not. Since the array hasn't changed between loops, this second check can never trigger.

♻️ Suggested simplification
         foreach (var item in jsonArray)
         {
             cancellationToken.ThrowIfCancellationRequested();

-            if (item is not JObject jObject)
-                throw new ArgumentException("The JSON payload must contain valid JSON objects.");
+            var jObject = (JObject)item;

             var dataRow = new XElement(tableNamespace + "table-row");

As per coding guidelines: "Clean structure and no unused code".

📝 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
foreach (var item in jsonArray)
{
cancellationToken.ThrowIfCancellationRequested();
if (item is not JObject jObject)
throw new ArgumentException("The JSON payload must contain valid JSON objects.");
foreach (var item in jsonArray)
{
cancellationToken.ThrowIfCancellationRequested();
var jObject = (JObject)item;
🤖 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.Odf.WriteSpreadsheet/Frends.Odf.WriteSpreadsheet/Helpers/OdfSpreadsheetWriter.cs`
around lines 70 - 76, Remove the unreachable guard clause that checks if an item
is not a JObject in the second foreach loop within the method. This validation
is dead code because the first foreach loop already validates that all items in
the JSON array are JObject instances and throws an ArgumentException if any item
fails this check. Since the array contents don't change between the two loops,
the identical check in the second loop will never execute and should be deleted
to maintain code cleanliness per guidelines.

Source: Coding guidelines

@MichalFrends1 MichalFrends1 merged commit 3a801cb into main Jun 15, 2026
6 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