Skip to content

[DRAFT] feat: Text*Formatter impl & ASP.NET Core API example#224

Merged
berezovskyi merged 23 commits intomainfrom
b67-TextOutputFormatter
Jul 6, 2025
Merged

[DRAFT] feat: Text*Formatter impl & ASP.NET Core API example#224
berezovskyi merged 23 commits intomainfrom
b67-TextOutputFormatter

Conversation

@berezovskyi
Copy link
Member

@berezovskyi berezovskyi commented Nov 9, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a .NET Core API example server with OSLC endpoints for catalog, service providers, and root services, supporting multiple RDF formats (RDF/XML, Turtle, JSON-LD, N-Triples).
    • Added input and output formatters for RDF serialization/deserialization in ASP.NET Core.
    • Provided configuration files for development, Docker, and test environments.
    • Implemented comprehensive integration and model tests for OSLC endpoints and RDF serialization.
    • Added support for content negotiation and dynamic URL generation in OSLC responses.
    • Added new controllers for ChangeRequest and resource management with dynamic resource instantiation.
    • Enhanced root services with OAuth endpoint metadata and XML serialization.
    • Included Docker multi-stage build setup for the example server.
    • Added support for N-Triples media type in OSLC content handling.
    • Introduced new test fixtures and Aspire-hosted integration test setup for distributed application testing.
    • Added extension methods for type inspection related to OSLC resource shapes.
  • Bug Fixes

    • None.
  • Documentation

    • Added sample HTTP request files and test data for OSLC endpoints.
  • Chores

    • Updated solution and project files to include new server and test projects.
    • Added Docker and development tool configurations.
    • Refined project and solution structure with SDK-style projects and updated GUIDs.
    • Added .dockerignore to optimize Docker build context.

TODOs:

  • add to the Aspire test suite
  • implement input formatter
  • refactor the code to avoid duplication with mediatype formatter
  • make magic by default configurable (like setting the base URI of the response graph)
  • allow Graphs to be send directly
  • for both formatters implement a check if they can handle the object in advance
  • JSON-LD
  • consider how to update the client - dragging ASP.NET in is not always desirable, potentially we will need to keep the mediaformatter in place
  • consider renaming to OSLC4Net.Server.Formatters

Closes #67

@berezovskyi berezovskyi self-assigned this Nov 9, 2024
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 9, 2024

Walkthrough

This change introduces a new .NET Core API example server with OSLC (Open Services for Lifecycle Collaboration) endpoints, including controllers, models, and configuration for Docker and Aspire hosting. It ports the previous MediaTypeFormatter logic to new ASP.NET Core TextInputFormatter and TextOutputFormatter classes, adds comprehensive tests, and updates solution/project files accordingly.

Changes

File(s) Change Summary
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs, OslcRdfInputFormatter.cs, RdfFormat.cs, SerializationContext.cs Added new ASP.NET Core TextOutputFormatter and TextInputFormatter for RDF, plus supporting types
OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs, Extensions.cs Refactored/ported helper logic for formatter support; updated method visibility and naming
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/* Added new .NET Core API example project with controllers, models, config, Dockerfile, and HTTP files
OSLC4Net_SDK/OSLC4Net.Core/Model/OslcMediaType.cs Added constant for N-Triples media type
OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj New project file for server providers
OSLC4Net_SDK/OSLC4Net.Core.sln, Directory.Packages.props, .dockerignore, .config/dotnet-tools.json Updated solution, package versions, Docker, and tool configuration
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/, OSLC4NetExamples.Server.Tests.AspireHost/ Added new test projects, fixtures, integration and model tests, and test data

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ASPNETCore
    participant OslcRdfInputFormatter
    participant Controller
    participant OslcRdfOutputFormatter

    Client->>ASPNETCore: HTTP Request (Accepts/Content-Type: RDF)
    ASPNETCore->>OslcRdfInputFormatter: Parse request body (if input)
    OslcRdfInputFormatter-->>ASPNETCore: CLR Model
    ASPNETCore->>Controller: Invoke action with model
    Controller-->>ASPNETCore: Returns result (model/graph)
    ASPNETCore->>OslcRdfOutputFormatter: Serialize response to RDF
    OslcRdfOutputFormatter-->>ASPNETCore: RDF-formatted response
    ASPNETCore-->>Client: HTTP Response (RDF)
Loading

Assessment against linked issues

Objective Addressed Explanation
Port our MediaTypeFormatter to be a TextOutputFormatter (#67)

Poem

In the warren where code bunnies dwell,
A formatter hopped from old to new shell.
Now RDF flows, in and out,
Through Core's bright tunnels, there's never a doubt!
With tests and controllers, our garden expands—
Hooray for the work of these clever paws and hands!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Nov 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 50.54%. Comparing base (a70aabd) to head (03b1230).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #224      +/-   ##
==========================================
- Coverage   50.62%   50.54%   -0.09%     
==========================================
  Files         166      166              
  Lines       15046    15071      +25     
  Branches     1016     1021       +5     
==========================================
  Hits         7617     7617              
- Misses       7149     7174      +25     
  Partials      280      280              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@berezovskyi berezovskyi changed the title feat: Text*Formatter impl & ASP.NET Core API example [DRAFT] feat: Text*Formatter impl & ASP.NET Core API example Nov 9, 2024
Copy link
Contributor

@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: 14

🧹 Outside diff range and nitpick comments (24)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.http (3)

1-2: Consider adding HTTPS configuration for production.

The HTTP configuration is acceptable for development. However, for production deployments, you should consider adding an HTTPS variant of the host address.

Consider adding:

@OSLC4NetExamples.Server.NetCoreApi_HostAddress = http://localhost:5065
+# For production
+@OSLC4NetExamples.Server.NetCoreApi_HostAddress_Prod = https://localhost:7065

3-3: Consider removing the trailing slash from the endpoint URL.

The trailing slash in /weatherforecast/ might cause unnecessary redirects in some frameworks. Consider using /weatherforecast instead.

-GET {{OSLC4NetExamples.Server.NetCoreApi_HostAddress}}/weatherforecast/
+GET {{OSLC4NetExamples.Server.NetCoreApi_HostAddress}}/weatherforecast

3-6: Add test cases for different response formats.

Since this is part of an OSLC implementation, consider adding test cases for different response formats (e.g., RDF/XML, Turtle, JSON-LD) to ensure content negotiation works correctly.

Consider adding:

### Get weather forecast in RDF/XML
GET {{OSLC4NetExamples.Server.NetCoreApi_HostAddress}}/weatherforecast
Accept: application/rdf+xml

### Get weather forecast in Turtle
GET {{OSLC4NetExamples.Server.NetCoreApi_HostAddress}}/weatherforecast
Accept: text/turtle

### Get weather forecast in JSON-LD
GET {{OSLC4NetExamples.Server.NetCoreApi_HostAddress}}/weatherforecast
Accept: application/ld+json
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/WeatherForecast.cs (2)

1-4: Consider adding XML documentation comments.

Since this is an example/demo class that others might learn from, adding XML documentation comments would help explain its purpose and usage.

 namespace OSLC4NetExamples.Server.NetCoreApi;

+/// <summary>
+/// Represents a daily weather forecast with temperature readings and conditions.
+/// </summary>
 public class WeatherForecast

11-11: Consider adding validation for the Summary property.

While nullable strings are acceptable, you might want to add validation for maximum length or content requirements.

-    public string? Summary { get; set; }
+    private string? summary;
+    public string? Summary
+    {
+        get => summary;
+        set
+        {
+            if (value?.Length > 100)
+                throw new ArgumentException("Summary cannot exceed 100 characters");
+            summary = value;
+        }
+    }
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1)

6-12: Add XML documentation for public API.

Add XML documentation to describe the formatter's purpose, supported media types, and behavior.

Add this documentation:

+/// <summary>
+/// Formats RDF input according to OSLC specifications.
+/// </summary>
+/// <remarks>
+/// Supports the following media types:
+/// - application/rdf+xml
+/// - text/turtle
+/// </remarks>
 public class OslcRdfInputFormatter : TextInputFormatter
 {
+    /// <summary>
+    /// Reads the request body and converts it to the specified type.
+    /// </summary>
+    /// <param name="context">The input formatter context.</param>
+    /// <param name="encoding">The encoding used to read the request body.</param>
+    /// <returns>A task that returns the formatted input.</returns>
+    /// <exception cref="InputFormatterException">Thrown when the input cannot be parsed.</exception>
     public override async Task<InputFormatterResult> ReadRequestBodyAsync(...)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs (2)

1-2: Consider adding explicit imports for clarity.

While the code works due to implicit imports, consider adding explicit imports for better code maintainability and clarity:

+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
 using OSLC4Net.Server.Providers;

13-14: Review Swagger configuration for development environment.

Swagger is configured but commented out in the pipeline. If this is intended for development purposes, consider moving the Swagger configuration to a development-only service registration.

-builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();

+if (builder.Environment.IsDevelopment())
+{
+    builder.Services.AddEndpointsApiExplorer();
+    builder.Services.AddSwaggerGen();
+}

Also applies to: 21-22

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/WeatherForecastController.cs (2)

4-7: Consider improving API documentation and routing.

The controller should have:

  1. XML documentation to describe its purpose and endpoints
  2. A more specific base route that reflects the OSLC service nature (e.g., [Route("api/[controller]")] or [Route("services/[controller]")])
 namespace OSLC4NetExamples.Server.NetCoreApi.Controllers;
+/// <summary>
+/// Controller providing weather forecast data and OSLC service provider information.
+/// </summary>
 [ApiController]
-[Route("[controller]")]
+[Route("api/[controller]")]
 public class WeatherForecastController : ControllerBase

9-19: Enhance configuration management and logging.

Consider the following improvements:

  1. Move the weather summaries to application configuration (appsettings.json)
  2. Add logging statements in the action methods for better observability

Would you like me to provide an example of how to implement these improvements?

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile (1)

25-25: Consider adding build output verification.

The publish command could benefit from additional flags to ensure consistent builds:

-RUN dotnet publish "./OSLC4NetExamples.Server.NetCoreApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+RUN dotnet publish "./OSLC4NetExamples.Server.NetCoreApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false --no-restore --no-build --verify-no-changes
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json (2)

31-41: Document container port mappings and limit port exposure

The current configuration publishes all ports ("publishAllPorts": true), which might expose unnecessary services. Consider:

  1. Explicitly documenting the port mappings (8080/8081) in the repository's README
  2. Only exposing the required ports in the Dockerfile instead of publishing all ports

Example Dockerfile port configuration:

EXPOSE 8080
EXPOSE 8081

44-50: Review authentication requirements

The current configuration allows anonymous access to all endpoints. Consider:

  1. Implementing appropriate authentication middleware for securing endpoints
  2. Defining CORS policies if the API will be accessed from different origins

Example authentication configuration in Program.cs:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer();

app.UseAuthentication();
app.UseAuthorization();
OSLC4Net_SDK/Directory.Packages.props (2)

13-13: Consider moving Azure Containers Tools package to "Examples only" section.

The package is specifically for container support in the example API project, so it would be more organized to move it under the "Examples only" section with other example-specific dependencies.


43-43: Review Swashbuckle.AspNetCore version and placement.

Two suggestions:

  1. Consider upgrading to the latest stable version (6.5.0) for better compatibility with .NET 8
  2. Move this package to the "Examples only" section since it's specifically for the API example
OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs (4)

425-430: Add XML documentation for the public method.

The implementation looks good, but since this method is now public, it should include XML documentation describing its purpose, parameters, and return value.

Add documentation like this:

+    /// <summary>
+    /// Determines whether the specified type implements or inherits from a generic type.
+    /// </summary>
+    /// <param name="genericType">The generic type to check against.</param>
+    /// <param name="typeToTest">The type to test.</param>
+    /// <returns>True if typeToTest implements or inherits from genericType; otherwise, false.</returns>
     public static bool ImplementsGenericType(Type genericType, Type typeToTest)

Line range hint 432-450: Add XML documentation for the public method.

The implementation is solid, but since this method is now public, it should include XML documentation describing its purpose, parameters, and return value.

Add documentation like this:

+    /// <summary>
+    /// Determines whether the specified type implements or inherits from a generic type, with control over generic type resolution.
+    /// </summary>
+    /// <param name="genericType">The generic type to check against.</param>
+    /// <param name="typeToTest">The type to test.</param>
+    /// <param name="isParentGeneric">If true, resolves generic type definitions before comparison.</param>
+    /// <returns>True if typeToTest implements or inherits from genericType; otherwise, false.</returns>
     public static bool ImplementsGenericType(Type genericType, Type typeToTest, bool isParentGeneric)

Line range hint 452-470: Fix potential infinite loop and null reference issues.

The implementation has critical issues that need to be addressed:

  1. Missing null check for parentType
  2. No exit condition if genericType is not found in the type hierarchy
  3. Could lead to infinite loop or NullReferenceException

Apply this fix:

     public static Type[] GetChildClassParameterArguments(Type genericType, Type typeToTest)
     {
         var isParentGeneric = genericType.IsGenericType;
 
         while (true)
         {
             var parentType = typeToTest.BaseType;
+            if (parentType == null)
+            {
+                throw new ArgumentException($"Type {typeToTest} does not inherit from {genericType}");
+            }
+
             var parentToTest = isParentGeneric && parentType.IsGenericType
                 ? parentType.GetGenericTypeDefinition()
                 : parentType;
 
             if (parentToTest == genericType)
             {
                 return typeToTest.GetGenericArguments();
             }
 
             typeToTest = parentType;
         }
     }

Also add XML documentation:

+    /// <summary>
+    /// Gets the generic type arguments from a derived type that inherits from a specific generic base type.
+    /// </summary>
+    /// <param name="genericType">The generic base type.</param>
+    /// <param name="typeToTest">The derived type to extract arguments from.</param>
+    /// <returns>An array of Type objects representing the generic arguments.</returns>
+    /// <exception cref="ArgumentException">Thrown when typeToTest does not inherit from genericType.</exception>
     public static Type[] GetChildClassParameterArguments(Type genericType, Type typeToTest)

Line range hint 477-487: Add XML documentation for the public class.

The implementation is correct, but since this class is now public, it should include XML documentation explaining its purpose and behavior.

Add documentation like this:

+    /// <summary>
+    /// A StreamWriter that prevents the underlying stream from being closed when Close() is called.
+    /// This is particularly useful when working with dotNetRDF writers that attempt to close the output stream.
+    /// </summary>
     public class NonClosingStreamWriter : StreamWriter
     {
+        /// <summary>
+        /// Initializes a new instance of the NonClosingStreamWriter class.
+        /// </summary>
+        /// <param name="stream">The stream to write to.</param>
         public NonClosingStreamWriter(Stream stream)
             : base(stream)
         {
         }
 
+        /// <summary>
+        /// Overrides the Close method to prevent closing the underlying stream.
+        /// Instead, it only flushes the buffer to ensure all data is written.
+        /// </summary>
         public override void Close()
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (5)

37-39: Remove Unused Variable buffer to Clean Up Code

The buffer variable is instantiated but never used within the WriteResponseBodyAsync method. This may be leftover code and can be safely removed to clean up the code.

Apply this diff to remove the unused variable:

- var buffer = new StringBuilder();

47-51: Use Strongly-Typed Access Instead of Reflection to Enhance Performance

The code uses reflection with GetProperty and GetValue to access the Resource and Properties properties. Relying on string literals for property names can lead to runtime errors if property names change and can impact performance. Consider using strongly-typed access or interfaces to improve reliability and efficiency.


60-61: Address the FIXME: Configure Subject URI Based on Service Provider Info

There's a FIXME comment indicating that the subject URI for the collection should be set by the app based on service provider information. Implementing this will enhance flexibility and ensure correct URI construction.

Would you like assistance in updating the code to set the subject URI based on the service provider configuration? I can help generate the necessary code or open a GitHub issue to track this task.


112-113: Complete the TODO: Set the Default Content Type

There's a TODO comment indicating the need to set a default content type. This ensures that the formatter can handle responses even when the content type is not specified in the context.

Would you like assistance in setting the default content type? I can help suggest appropriate defaults or open a GitHub issue to track this task.


168-176: Evaluate the Necessity of AsMsNetType Extension Method

The comment on line 170 suggests reconsidering the need for the AsMsNetType extension method. If this method doesn't provide significant value or duplicates existing functionality, consider removing it to simplify the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7dae8db and a80048c.

📒 Files selected for processing (16)
  • OSLC4Net_SDK/.dockerignore (1 hunks)
  • OSLC4Net_SDK/Directory.Packages.props (2 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/WeatherForecastController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.http (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/WeatherForecast.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.Development.json (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.json (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.sln (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • OSLC4Net_SDK/.dockerignore
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.Development.json
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.json
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj
🔇 Additional comments (10)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj (3)

17-19: Verify OSLC4Net.Server.Providers project compatibility.

The project reference is correctly configured. Since this is a new API project depending on OSLC4Net.Server.Providers, we should verify framework compatibility.

Let's check the target framework of the referenced project:

✅ Verification successful

Project reference compatibility verified

Both projects target .NET 8.0, ensuring full framework compatibility between OSLC4NetExamples.Server.NetCoreApi and OSLC4Net.Server.Providers.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify framework compatibility between projects
# Expected: Find TargetFramework in the referenced project file

rg -A 1 "<TargetFramework>" "OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj"

Length of output: 296


Script:

#!/bin/bash
# Check target frameworks in both projects
echo "=== Server.Providers Framework ==="
rg "<TargetFramework>" "OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj"

echo -e "\n=== NetCoreApi Framework ==="
rg "<TargetFramework>" "OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj"

Length of output: 464


1-10: LGTM! Project configuration follows best practices.

The project is well-configured with:

  • Latest .NET 8.0 LTS
  • Security features (nullable references, user secrets)
  • Docker support

Let's verify the Docker setup is complete:

✅ Verification successful

Docker configuration is properly set up and follows best practices

The verification confirms that:

  • Dockerfile exists in the correct location with proper multi-stage build configuration
  • .dockerignore is present at the root level
  • Docker setup follows security best practices:
    • Uses official Microsoft images
    • Runs as non-root 'app' user
    • Multi-stage build to minimize final image size
    • Proper port exposure (8080, 8081)
    • Correct project restoration and build process
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Docker configuration files exist
# Expected: Find Dockerfile and .dockerignore in the correct location

# Check for Dockerfile
fd -p Dockerfile$ "$(dirname "OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi")/.."

# Check for .dockerignore
fd -p .dockerignore$ "$(dirname "OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi")/.."

Length of output: 296


Script:

#!/bin/bash
# Let's verify Docker setup with a more precise search
# Check for Dockerfile in the specific project directory
find OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi -name "Dockerfile" -type f

# Check for Dockerfile in the parent directories as per DockerfileContext
find OSLC4Net_SDK -maxdepth 2 -name "Dockerfile" -type f

# Check for .dockerignore
find OSLC4Net_SDK -maxdepth 2 -name ".dockerignore" -type f

# Let's also check the content of any Dockerfile if it exists
if [ -f OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile ]; then
    cat OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile
fi

Length of output: 1951


12-15: Verify package version constraints in Directory.Packages.props.

Package references look good, but since versions are managed centrally, let's ensure they're properly configured.

Let's verify the package versions are defined:

✅ Verification successful

Package versions are properly defined in Directory.Packages.props

Both packages have their versions centrally managed:

  • Microsoft.VisualStudio.Azure.Containers.Tools.Targets: 1.21.0
  • Swashbuckle.AspNetCore: 6.6.2
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if package versions are defined in Directory.Packages.props
# Expected: Find version entries for both packages

rg -A 1 "Microsoft\.VisualStudio\.Azure\.Containers\.Tools\.Targets|Swashbuckle\.AspNetCore" "Directory.Packages.props"

Length of output: 296


Script:

#!/bin/bash
# Let's first check if Directory.Packages.props exists and its content
fd "Directory.Packages.props"

# Also check for other common package version management files
fd "Directory.Build.props"
fd "global.json"

# Let's also check the project file itself for version specifications
cat "OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj"

Length of output: 1031


Script:

#!/bin/bash
# Let's check the content of Directory.Packages.props since we confirmed it exists
cat "OSLC4Net_SDK/Directory.Packages.props"

# Also check the root Directory.Build.props as it might contain version information
cat "OSLC4Net_SDK/Directory.Build.props"

Length of output: 3824

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs (1)

7-11: Verify content type handling for OSLC RDF formatters.

The formatters are correctly prioritized by inserting at index 0, but consider explicitly configuring supported media types and content negotiation.

Consider adding explicit media type configuration:

 builder.Services.AddControllers(options =>
 {
     options.InputFormatters.Insert(0, new OslcRdfInputFormatter());
     options.OutputFormatters.Insert(0, new OslcRdfOutputFormatter());
+    options.RespectBrowserAcceptHeader = true;
+    options.ReturnHttpNotAcceptable = true;
 });
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile (3)

28-31: Consider adding HEALTHCHECK instruction.

The final stage would benefit from a Docker HEALTHCHECK instruction to enable container health monitoring.

Add after the ENTRYPOINT:

ENTRYPOINT ["dotnet", "OSLC4NetExamples.Server.NetCoreApi.dll"]
+HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
+    CMD curl -f http://localhost:8080/health || exit 1

Note: Ensure that a health endpoint is implemented in the application.

Let's verify if a health endpoint exists:

#!/bin/bash
# Check for health endpoint implementation
rg -l "health" "Examples/OSLC4NetExamples.Server.NetCoreApi/"

18-19: Verify .dockerignore configuration for build optimization.

The COPY . . command copies all files from the context. Ensure that the .dockerignore file properly excludes unnecessary files to optimize build time and image size.

Let's verify the .dockerignore configuration:

#!/bin/bash
# Check .dockerignore content and common build artifacts
if [ -f .dockerignore ]; then
    echo "Content of .dockerignore:"
    cat .dockerignore
else
    echo "Warning: .dockerignore file not found"
fi

# Check for common build output directories that should be ignored
fd -t d "^(bin|obj|TestResults)$"

7-8: Consider adding HTTPS configuration for security.

The Dockerfile exposes ports 8080 and 8081 for HTTP traffic. For production deployments, it's recommended to configure HTTPS to ensure secure communication.

Let's verify if HTTPS configuration exists in the project:

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json (1)

3-22: Consider security implications of exposed endpoints

While the development configuration is appropriate for local development:

  1. The HTTP endpoint (port 5065) should be disabled in production to prevent unencrypted traffic
  2. Swagger UI exposure should be conditional based on environment

Consider adding environment-specific configuration in Program.cs:

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
OSLC4Net_SDK/Directory.Packages.props (1)

12-12: Verify logging replacement before removing log4net.

The package is marked for removal, but ensure there's a replacement logging solution in place (e.g., Microsoft.Extensions.Logging) to maintain logging capabilities across the codebase.

OSLC4Net_SDK/OSLC4Net.Core.sln (1)

191-214: LGTM: Build configurations and solution structure.

The build configurations for the new projects are properly set up with all necessary platform targets. The NetCoreApi project is correctly nested under the Examples solution folder.

Also applies to: 226-226

Copy link
Contributor

@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: 0

🧹 Outside diff range and nitpick comments (4)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (4)

37-38: Remove unused StringBuilder

The buffer variable is created but never used. The commented out code at line 116 suggests it was part of an earlier implementation.

- var buffer = new StringBuilder();

31-117: Consider breaking down the WriteResponseBodyAsync method

The method is handling multiple responsibilities including type checking, graph creation, and response writing. Consider extracting the graph creation logic into separate methods for better maintainability.

Example refactoring:

+ private IGraph CreateGraphFromFilteredResource(object value, Type type, HttpContext httpContext)
+ {
+     // Extract filtered resource handling logic here
+ }

+ private IGraph CreateGraphFromEnumerable(object value)
+ {
+     // Extract enumerable handling logic here
+ }

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context,
    Encoding selectedEncoding)
{
    var httpContext = context.HttpContext;
    var type = context.ObjectType;
    var value = context.Object;
    
    IGraph graph;
    if (ImplementsGenericType(typeof(FilteredResource<>), type))
    {
-        // Current filtered resource handling logic
+        graph = CreateGraphFromFilteredResource(value, type, httpContext);
    }
    else if (InheritedGenericInterfacesHelper.ImplementsGenericInterface(
                 typeof(IEnumerable<>), value.GetType()))
    {
-        // Current enumerable handling logic
+        graph = CreateGraphFromEnumerable(value);
    }
    // ... rest of the method
}

112-114: Address the TODO comment about default content type

The TODO comment suggests that default content type handling needs to be implemented. This should be addressed to ensure robust content type handling.

Would you like me to help implement the default content type handling? We can:

  1. Set a default content type in the constructor
  2. Use it when the context's content type is null/empty

132-138: Extract duplicate RdfXmlWriter configuration

The RdfXmlWriter configuration is duplicated. Consider extracting it to a separate method.

+ private RdfXmlWriter CreateRdfXmlWriter()
+ {
+     return new RdfXmlWriter
+     {
+         UseDtd = false,
+         PrettyPrintMode = false,
+         CompressionLevel = 20
+     };
+ }

- var rdfXmlWriter = new RdfXmlWriter
- {
-     UseDtd = false, PrettyPrintMode = false, CompressionLevel = 20
- };
+ var rdfXmlWriter = CreateRdfXmlWriter();

Also applies to: 155-161

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a80048c and c00998d.

📒 Files selected for processing (1)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
🔇 Additional comments (2)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (2)

20-29: LGTM: Constructor properly initializes formatter configuration

The constructor correctly sets up supported media types and encodings for RDF/XML and Turtle formats.


172-177: Address the REVISIT comment about duplicate implementations

The comment suggests there might be two similar media type conversion implementations. This should be investigated to avoid maintaining duplicate code.

Let's search for similar implementations:

@berezovskyi berezovskyi force-pushed the b67-TextOutputFormatter branch from c00998d to cbbeeb5 Compare November 10, 2024 10:26
Copy link
Contributor

@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: 1

🧹 Outside diff range and nitpick comments (2)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/WeatherForecastController.cs (1)

4-19: Consider moving hardcoded values to configuration.

The static weather summaries array should be moved to configuration for better maintainability and flexibility.

Consider applying this change:

-    private static readonly string[] Summaries = new[]
-    {
-        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
-    };
+    private readonly string[] _summaries;
+
+    public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration)
+    {
+        _logger = logger;
+        _summaries = configuration.GetSection("WeatherSummaries").Get<string[]>() ?? 
+            throw new InvalidOperationException("Weather summaries configuration is required");
+    }
OSLC4Net_SDK/OSLC4Net.Core.sln (1)

41-42: Consider organizing OSLC4Net.Server.Providers in a solution folder.

The project appears to be a core provider implementation but is not organized within any solution folder. Consider creating a new "Providers" solution folder or placing it in an appropriate existing folder to maintain a clean solution structure.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c00998d and cbbeeb5.

📒 Files selected for processing (16)
  • OSLC4Net_SDK/.dockerignore (1 hunks)
  • OSLC4Net_SDK/Directory.Packages.props (2 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/WeatherForecastController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.http (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/WeatherForecast.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.Development.json (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.json (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.sln (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (14)
  • OSLC4Net_SDK/.dockerignore
  • OSLC4Net_SDK/Directory.Packages.props
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Dockerfile
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.http
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/WeatherForecast.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.Development.json
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/appsettings.json
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OSLC4Net.Server.Providers.csproj
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs
🔇 Additional comments (4)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/WeatherForecastController.cs (2)

21-29: Previous OSLC implementation concerns are still valid.

The concerns raised in the previous review about content negotiation, service provider details, and URL sanitization remain applicable.


31-42: Previous API robustness suggestions are still valid.

The suggestions for improving API robustness with caching, configuration, and input parameters remain applicable.

OSLC4Net_SDK/OSLC4Net.Core.sln (2)

41-44: Previous comment about SDK-style format is still applicable.


191-214: LGTM: Build configurations and solution structure.

The build configurations for the new projects are properly set up with all necessary platform targets, and OSLC4NetExamples.Server.NetCoreApi is correctly organized in the Examples solution folder.

Also applies to: 226-226

@berezovskyi berezovskyi force-pushed the b67-TextOutputFormatter branch from cbbeeb5 to 5ef32eb Compare November 13, 2024 07:45
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
0.0% Coverage on New Code (required ≥ 80%)
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@berezovskyi
Copy link
Member Author

@codecov

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 4, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
0.0% Coverage on New Code (required ≥ 80%)
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Signed-off-by: Andrew Berezovskyi <andriib@kth.se>
Signed-off-by: Andrew Berezovskyi <andriib@kth.se>
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
0.0% Coverage on New Code (required ≥ 80%)
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Contributor

@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: 9

♻️ Duplicate comments (1)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs (1)

11-13: Same route-template caveat

Remove the leading slash to keep route composition flexible.

🧹 Nitpick comments (9)
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ProviderController.cs (2)

7-8: Avoid absolute template paths in [Route] attributes

Prefixing the route template with / makes it absolute and bypasses any route prefixes configured at higher levels (e.g., MapControllerRoute, versioning prefixes).
Prefer a relative template ("services/provider") unless you explicitly want to opt-out of all future route composition.

-[Route("/services/provider")]
+[Route("services/provider")]

8-19: logger is injected but never used

Dead‐code parameters are misleading and will trigger CA1801/IDE0060 warnings.
Either use the logger (recommended) or remove the constructor parameter.

-public class ProviderController(ILogger<ProviderController> logger) : ControllerBase
+public class ProviderController(ILogger<ProviderController> logger) : ControllerBase
 {
     [HttpGet]
     [Route("{id}")]
-    public OSLC4Net.Core.Model.ServiceProvider GetProvider(string id)
+    public OSLC4Net.Core.Model.ServiceProvider GetProvider(string id)
     {
-        var sp = new OSLC4Net.Core.Model.ServiceProvider();
-        sp.SetAbout(new Uri(Request.GetEncodedUrl()));
-        sp.SetDescription("test me");
-        return sp;
+        var sp = new OSLC4Net.Core.Model.ServiceProvider();
+        sp.SetAbout(new Uri(Request.GetEncodedUrl()));
+        sp.SetDescription("Example Service Provider");   // TODO: replace with real data
+
+        logger.LogDebug("Returned ServiceProvider for id {Id}", id);
+        return sp;
     }
 }
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/RootServicesController.cs (2)

7-9: Absolute [Route] template limits composition

Same remark as for ProviderController: use a relative template unless you intentionally bypass parent prefixes.


41-47: Consider FileContentResult for correct content-type/headers

Returning Content() forces the whole payload into memory as a string.
FileContentResult with a byte[] allows streaming large RDF documents and sets the Content-Length header.

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Models/RootServicesDto.cs (2)

198-211: XML declaration is never emitted

doc.WriteContentTo(xmlWriter) writes only the root element; XmlWriterSettings.OmitXmlDeclaration = false is ignored because no WriteStartDocument() call exists.
Use doc.Save(xmlWriter) or call xmlWriter.WriteStartDocument() first.

-doc.WriteContentTo(xmlWriter);
+xmlWriter.WriteStartDocument();
+doc.WriteContentTo(xmlWriter);

138-144: Duplicate namespace maintenance

The record already maintains Namespaces but you hard-code the same URIs here.
Consider iterating over Namespaces to avoid drift.

OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/Models/RootServicesDtoTests.cs (3)

20-22: Remove Console.WriteLine from tests

Unit-test output pollutes CI logs. Use ITestOutputHelper if diagnostics are required.


50-56: File path resolution brittle

File.ReadAllText("TestData/…") depends on the working directory.
Use Path.Combine(AppContext.BaseDirectory, "TestData", "rootservices.rdf") or embed the files as resources.


82-107: Repeated serialization pattern could be extracted

The boilerplate could live in a helper to reduce cognitive load and duplication.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a0c20c and 84219d4.

📒 Files selected for processing (12)
  • OSLC4Net_SDK/Directory.Packages.props (2 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ProviderController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ChangeRequestController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ResourceController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/RootServicesController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Models/RootServicesDto.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/Models/RootServicesDtoTests.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/OSLC4NetExamples.Server.Tests.csproj (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestData/rootservices.rdf (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestData/rootservices2.rdf (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestData/rootservices2.rdf
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestData/rootservices.rdf
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/OSLC4NetExamples.Server.Tests.csproj
🚧 Files skipped from review as they are similar to previous changes (1)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines from "Building Maintainable Software" by Joost Visser. Point out deviation...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/RootServicesController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ChangeRequestController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ProviderController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ResourceController.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/Models/RootServicesDtoTests.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Models/RootServicesDto.cs
🔇 Additional comments (2)
OSLC4Net_SDK/Directory.Packages.props (1)

36-36:

Details

❌ Incorrect review comment

Verify that FluentAssertions v8.3.0 actually exists on NuGet.

The latest publicly-available major line for FluentAssertions is still 6.* (currently 6.13.x). A restore will fail if the version is mistyped or unpublished.

-<PackageVersion Include="FluentAssertions" Version="8.3.0" />
+<PackageVersion Include="FluentAssertions" Version="6.13.0" />

Double-check the intended version and update accordingly to avoid CI breakage.


Let’s verify the existence of both versions on NuGet:


🏁 Script executed:

#!/usr/bin/env bash
# Fetch all available FluentAssertions versions and check for 8.3.0 and 6.13.0
VERSIONS_JSON=$(curl -s https://api.nuget.org/v3-flatcontainer/fluentassertions/index.json)

echo "Checking for FluentAssertions v8.3.0:"
echo "$VERSIONS_JSON" | grep -o '"8\.3\.0"' || echo "→ v8.3.0 not found"

echo "Checking for FluentAssertions v6.13.0:"
echo "$VERSIONS_JSON" | grep -o '"6\.13\.0"' || echo "→ v6.13.0 not found"

Length of output: 7134


FluentAssertions v8.3.0 exists on NuGet – no change required
A check of the NuGet feed (https://api.nuget.org/v3-flatcontainer/fluentassertions/index.json) confirms that v8.3.0 is published and available, whereas v6.13.0 does not exist. You can safely keep:

<PackageVersion Include="FluentAssertions" Version="8.3.0" />

Likely an incorrect or invalid review comment.

OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs (1)

12-24: Unused logger and placeholder implementation

logger is unused and the controller returns hard-coded data.
Either:

  1. Inject a real catalog service (your TODO) and log at least at debug level, or
  2. Drop the parameter to silence analyzers until real behaviour is added.

Comment on lines +9 to +10
[HttpGet]
public abstract T GetResource(string id);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Return an ActionResult<T> and make routing‐contract explicit

  1. ApiController conventions expect an ActionResult<T> so that 404/400/500 etc. can be surfaced instead of always 200.
  2. Binding source for id should be explicit ([FromRoute]) to avoid accidental model-binding surprises.
-    [HttpGet]
-    public abstract T GetResource(string id);
+    [HttpGet("{id}")]
+    public abstract ActionResult<T> GetResource([FromRoute] string id);
📝 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
[HttpGet]
public abstract T GetResource(string id);
[HttpGet("{id}")]
public abstract ActionResult<T> GetResource([FromRoute] string id);
🤖 Prompt for AI Agents
In
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ResourceController.cs
around lines 9 to 10, change the return type of the GetResource method from T to
ActionResult<T> to properly support HTTP status codes like 404 or 400. Also, add
the [FromRoute] attribute to the id parameter to explicitly specify that it
comes from the route, preventing any unintended model binding issues.

Comment on lines +12 to +18
[HttpGet]
[Route("{id}")]
public override ChangeRequest GetResource(string id)
{
var tInstance = ActivatorUtilities.CreateInstance<ChangeRequest>(HttpContext.RequestServices, id);
return tInstance;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prefer async and proper status codes

Creating the model synchronously and always returning 200 limits scalability and hides “not-found” cases.

-    public override ChangeRequest GetResource(string id)
-    {
-        var tInstance = ActivatorUtilities.CreateInstance<ChangeRequest>(HttpContext.RequestServices, id);
-        return tInstance;
-    }
+    public override ActionResult<ChangeRequest> GetResource(string id)
+    {
+        var cr = ActivatorUtilities.CreateInstance<ChangeRequest>(HttpContext.RequestServices, id);
+        return cr is null ? NotFound() : Ok(cr);
+    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ChangeRequestController.cs
between lines 12 and 18, the GetResource method is synchronous and always
returns a 200 status code, which limits scalability and does not handle "not
found" cases properly. Refactor the method to be asynchronous by returning a
Task<ChangeRequest>, use async/await for any asynchronous operations, and modify
the response to return appropriate HTTP status codes such as 404 when the
resource is not found and 200 when it is successfully retrieved.

Copy link
Contributor

@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: 1

♻️ Duplicate comments (3)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (3)

147-168: ⚠️ Potential issue

Media-type comparison should be case-insensitive

HTTP header values are case-insensitive. requestedType.Equals(...) defaults to ordinal, so "application/rdf+xml" vs "Application/RDF+XML" will miss.

-if (requestedType.Equals(OslcMediaType.APPLICATION_RDF_XML))
+if (requestedType.Equals(OslcMediaType.APPLICATION_RDF_XML, StringComparison.OrdinalIgnoreCase))
 ...
-else if (requestedType.Equals(OslcMediaType.TEXT_TURTLE))
+else if (requestedType.Equals(OslcMediaType.TEXT_TURTLE, StringComparison.OrdinalIgnoreCase))

134-137: ⚠️ Potential issue

context.ContentType may be null – add a default

A null ContentType hits new MediaType(contentType) inside SerializeGraph, throwing.
Set a safe default (e.g., APPLICATION_RDF_XML) when the header is missing:

-var contentType = context.ContentType;
+var contentType = StringSegment.IsNullOrEmpty(context.ContentType)
+    ? OslcMediaType.APPLICATION_RDF_XML_TYPE.MediaType
+    : context.ContentType;

69-78: ⚠️ Potential issue

portNum nullable handling is unsafe

Request.Host.Port returns int?. When it is null your current logic falls through to ":" + portNum, producing ":" and corrupting the URI.
Use .HasValue (or pattern-matching) before comparisons:

-var portNum = httpContext.Request.Host.Port;
-string? portString;
-if (portNum == 80 || portNum == 443)
-{
-    portString = "";
-}
-else
-{
-    portString = ":" + portNum;
-}
+int? portNum = httpContext.Request.Host.Port;
+string portString = portNum is null || portNum is 80 or 443
+    ? string.Empty
+    : $":{portNum.Value}";
🧹 Nitpick comments (1)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1)

37-39: Remove dead buffer allocation

var buffer = new StringBuilder(); is never used (remaining WriteAsync call is commented out). Drop the variable to save a needless allocation and keep the method focused.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84219d4 and 9eb8293.

📒 Files selected for processing (6)
  • OSLC4Net_SDK/.config/dotnet-tools.json (1 hunks)
  • OSLC4Net_SDK/Directory.Packages.props (2 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.sln (3 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • OSLC4Net_SDK/.config/dotnet-tools.json
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs
  • OSLC4Net_SDK/OSLC4Net.Core.sln
🚧 Files skipped from review as they are similar to previous changes (2)
  • OSLC4Net_SDK/Directory.Packages.props
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines from "Building Maintainable Software" by Joost Visser. Point out deviation...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: Analyze (csharp)

@berezovskyi berezovskyi mentioned this pull request Jun 24, 2025
Copy link
Contributor

@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: 1

♻️ Duplicate comments (3)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (3)

48-49: Add null check for contentType to prevent exceptions.

The contentType variable may be null if not provided in the context, which would cause an exception when passed to the MediaType constructor.


101-110: Handle nullable portNum to prevent null reference exceptions.

The portNum obtained from httpContext.Request.Host.Port is nullable and should be checked before comparison.


173-175: Respect the negotiated encoding.

The HttpResponseStreamWriter is hardcoded to use UTF-8, ignoring the negotiated encoding passed to WriteResponseBodyAsync.

🧹 Nitpick comments (3)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (3)

41-41: Remove commented code.

The commented logger declaration should be removed if not needed, or uncommented if logging is required.

-        // var logger = serviceProvider.GetRequiredService<ILogger<OslcRdfOutputFormatter>>();

65-65: Address the TODO: Make pretty print configurable.

The pretty print setting is currently hardcoded. Consider making this configurable through options or request headers.

Would you like me to generate an implementation that makes the pretty print setting configurable?


172-172: Address the TODO: Deal with namespaces.

The namespace handling is not implemented. This could affect RDF serialization quality and compactness.

Would you like me to generate an implementation for proper namespace handling in RDF serialization?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 317b9a9 and f8accf5.

📒 Files selected for processing (4)
  • OSLC4Net_SDK/OSLC4Net.Core/Model/OslcMediaType.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/RdfFormat.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/SerializationContext.cs (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/RdfFormat.cs
  • OSLC4Net_SDK/OSLC4Net.Core/Model/OslcMediaType.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/SerializationContext.cs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: Analyze (csharp)

@berezovskyi berezovskyi linked an issue Jul 2, 2025 that may be closed by this pull request
Copy link
Contributor

@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: 7

🧹 Nitpick comments (3)
OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/Extensions.cs (1)

31-34: Optimize attribute existence check for better performance.

The current implementation creates an array and checks its length, which is inefficient. Use IsDefined method for better performance when only checking attribute existence.

 public static bool IsOslcSingleton(this Type type)
 {
-    return type.GetCustomAttributes(typeof(OslcResourceShape), false).Length > 0;
+    return type.IsDefined(typeof(OslcResourceShape), false);
 }
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (2)

44-52: Replace switch expression with a dictionary for better maintainability.

The switch expression for format mapping could be replaced with a static dictionary to improve performance and maintainability, following the "Avoid Complex Units" guideline.

+private static readonly Dictionary<string, RdfFormat> MediaTypeToFormatMap = new()
+{
+    [OslcMediaType.APPLICATION_RDF_XML] = RdfFormat.RdfXml,
+    [OslcMediaType.TEXT_TURTLE] = RdfFormat.Turtle,
+    [OslcMediaType.APPLICATION_NTRIPLES] = RdfFormat.NTriples,
+    [OslcMediaType.APPLICATION_JSON_LD] = RdfFormat.JsonLd
+};

-var format = requestedType switch
-{
-    OslcMediaType.APPLICATION_RDF_XML => RdfFormat.RdfXml,
-    OslcMediaType.TEXT_TURTLE => RdfFormat.Turtle,
-    OslcMediaType.APPLICATION_NTRIPLES => RdfFormat.NTriples,
-    OslcMediaType.APPLICATION_JSON_LD => RdfFormat.JsonLd,
-    _ => throw new ArgumentOutOfRangeException(nameof(requestedType),
-        "Unknown RDF format"),
-};
+if (!MediaTypeToFormatMap.TryGetValue(requestedType, out var format))
+{
+    throw new ArgumentOutOfRangeException(nameof(requestedType), "Unknown RDF format");
+}

4-5: Address incomplete implementation indicated by TODO comments.

The TODO and REVISIT comments indicate that RDFS reasoning and schema loading are not fully implemented, which could affect the correctness of RDF processing.

The reasoning configuration and schema loading appear to be placeholders. This could lead to incomplete RDF processing or unexpected behavior in production.

Would you like me to help design a configuration system for RDFS reasoning and schema loading, or create an issue to track this implementation?

Also applies to: 132-136, 168-172

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8accf5 and c2115ac.

📒 Files selected for processing (3)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/Extensions.cs (2 hunks)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs (7 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/RdfXmlMediaTypeFormatter.cs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/Extensions.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: Analyze (csharp)

Comment on lines +36 to +66
public static Type? GetMemberType(this Type type)
{
if (type.IsArray)
{
return type.GetElementType();
}

if (InheritedGenericInterfacesHelper.ImplementsGenericInterface(typeof(IEnumerable<>),
type))
{
var interfaces = type.GetInterfaces();

foreach (var iface in interfaces)
{
if (iface.IsGenericType && iface.GetGenericTypeDefinition() ==
typeof(IEnumerable<object>).GetGenericTypeDefinition())
{
var memberType = iface.GetGenericArguments()[0];

if (memberType.GetCustomAttributes(typeof(OslcResourceShape), false).Length > 0)
{
return memberType;
}

return null;
}
}
}

return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Reduce complexity and fix potential bug in generic type comparison.

This method has several issues that impact maintainability and correctness:

  1. Potential bug on line 51: The comparison uses typeof(IEnumerable<object>).GetGenericTypeDefinition() but should use typeof(IEnumerable<>) directly.

  2. High cyclomatic complexity: The method violates the "keep methods short" principle from Building Maintainable Software.

  3. Code duplication: The attribute checking logic is duplicated from IsOslcSingleton.

 public static Type? GetMemberType(this Type type)
 {
     if (type.IsArray)
     {
-        return type.GetElementType();
+        var elementType = type.GetElementType();
+        return elementType?.IsOslcSingleton() == true ? elementType : null;
     }

     if (InheritedGenericInterfacesHelper.ImplementsGenericInterface(typeof(IEnumerable<>), type))
     {
-        var interfaces = type.GetInterfaces();
-
-        foreach (var iface in interfaces)
-        {
-            if (iface.IsGenericType && iface.GetGenericTypeDefinition() ==
-                typeof(IEnumerable<object>).GetGenericTypeDefinition())
-            {
-                var memberType = iface.GetGenericArguments()[0];
-
-                if (memberType.GetCustomAttributes(typeof(OslcResourceShape), false).Length > 0)
-                {
-                    return memberType;
-                }
-
-                return null;
-            }
-        }
+        return GetEnumerableElementType(type);
     }

     return null;
 }
+
+private static Type? GetEnumerableElementType(Type type)
+{
+    var enumerableInterface = type.GetInterfaces()
+        .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
+    
+    if (enumerableInterface == null) return null;
+    
+    var elementType = enumerableInterface.GetGenericArguments()[0];
+    return elementType.IsOslcSingleton() ? elementType : null;
+}
📝 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
public static Type? GetMemberType(this Type type)
{
if (type.IsArray)
{
return type.GetElementType();
}
if (InheritedGenericInterfacesHelper.ImplementsGenericInterface(typeof(IEnumerable<>),
type))
{
var interfaces = type.GetInterfaces();
foreach (var iface in interfaces)
{
if (iface.IsGenericType && iface.GetGenericTypeDefinition() ==
typeof(IEnumerable<object>).GetGenericTypeDefinition())
{
var memberType = iface.GetGenericArguments()[0];
if (memberType.GetCustomAttributes(typeof(OslcResourceShape), false).Length > 0)
{
return memberType;
}
return null;
}
}
}
return null;
}
public static Type? GetMemberType(this Type type)
{
if (type.IsArray)
{
var elementType = type.GetElementType();
return elementType?.IsOslcSingleton() == true ? elementType : null;
}
if (InheritedGenericInterfacesHelper.ImplementsGenericInterface(typeof(IEnumerable<>), type))
{
return GetEnumerableElementType(type);
}
return null;
}
private static Type? GetEnumerableElementType(Type type)
{
var enumerableInterface = type.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (enumerableInterface == null)
return null;
var elementType = enumerableInterface.GetGenericArguments()[0];
return elementType.IsOslcSingleton() ? elementType : null;
}
🤖 Prompt for AI Agents
In OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/Extensions.cs between lines 36
and 66, fix the generic type comparison by replacing
typeof(IEnumerable<object>).GetGenericTypeDefinition() with
typeof(IEnumerable<>). Refactor the method to reduce cyclomatic complexity by
extracting attribute checking and interface filtering into separate helper
methods. Reuse the existing IsOslcSingleton method to check for the
OslcResourceShape attribute instead of duplicating the logic, improving
maintainability and clarity.

Comment on lines +21 to +23
public OslcRdfInputFormatter(DotNetRdfHelper? rdfHelper = null)
{
_rdfHelper = rdfHelper ?? Activator.CreateInstance<DotNetRdfHelper>();
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using Activator.CreateInstance for dependency injection.

The use of Activator.CreateInstance<DotNetRdfHelper>() circumvents the dependency injection container and makes testing more difficult. This violates the Dependency Inversion Principle from SOLID.

Replace with proper dependency injection:

-public OslcRdfInputFormatter(DotNetRdfHelper? rdfHelper = null)
+public OslcRdfInputFormatter(DotNetRdfHelper rdfHelper)
{
-    _rdfHelper = rdfHelper ?? Activator.CreateInstance<DotNetRdfHelper>();
+    _rdfHelper = Guard.IsNotNull(rdfHelper, nameof(rdfHelper));

Register the dependency in your DI container configuration.

📝 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
public OslcRdfInputFormatter(DotNetRdfHelper? rdfHelper = null)
{
_rdfHelper = rdfHelper ?? Activator.CreateInstance<DotNetRdfHelper>();
public OslcRdfInputFormatter(DotNetRdfHelper rdfHelper)
{
_rdfHelper = Guard.IsNotNull(rdfHelper, nameof(rdfHelper));
// …any other initialization (e.g. SupportedMediaTypes, SupportedEncodings)
}
🤖 Prompt for AI Agents
In OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs around lines
21 to 23, avoid using Activator.CreateInstance to instantiate DotNetRdfHelper as
it bypasses dependency injection and hinders testing. Instead, remove the
default instantiation and require DotNetRdfHelper to be injected via the
constructor. Then, register DotNetRdfHelper in the dependency injection
container configuration so it is provided automatically when
OslcRdfInputFormatter is constructed.

Copy link
Contributor

@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: 7

🧹 Nitpick comments (3)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (3)

29-29: Remove or implement commented UTF-16 encoding support.

The commented code for UTF-16 encoding should either be removed or properly implemented. Keeping commented-out code reduces maintainability and violates the DRY principle.

-        // SupportedEncodings.Add(Encoding.Unicode);

38-38: Remove or implement commented logger code.

Either implement proper logging or remove the commented code to maintain code cleanliness.

-        // var logger = serviceProvider.GetRequiredService<ILogger<OslcRdfOutputFormatter>>();

169-169: Address TODO: Implement namespace handling.

The TODO comment indicates missing namespace handling functionality that should be implemented or tracked in an issue.

Would you like me to help implement namespace handling or create an issue to track this requirement?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2115ac and b304928.

📒 Files selected for processing (12)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ChangeRequestController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ResourceController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/RootServicesController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ServiceProviderController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Models/RootServicesDto.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/RdfFormat.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/SerializationContext.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/SerializationContext.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ChangeRequestController.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/RdfFormat.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/OSLC4NetExamples.Server.NetCoreApi.csproj
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/RootServicesController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/Resources/ResourceController.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Program.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Models/RootServicesDto.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ServiceProviderController.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)

Copy link
Contributor

@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: 2

♻️ Duplicate comments (5)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (5)

22-24: Remove Activator.CreateInstance to follow dependency injection best practices.

The constructor uses Activator.CreateInstance<DotNetRdfHelper>() which circumvents the dependency injection container and violates the Dependency Inversion Principle from SOLID guidelines.


34-78: Break down this method to improve maintainability.

This method is 44 lines long, violating the "Write Short Units of Code" principle (should be ≤15 lines). It handles multiple responsibilities: validation, format detection, URI resolution, deserialization, and object conversion.


56-60: Improve URI resolution robustness and error handling.

The comment explicitly states this needs improvement, and the current implementation lacks proper error handling for URI parsing failures. The ToSafeUri() extension method may mask exceptions that should be handled explicitly.


80-109: Reduce complexity and eliminate reflection usage.

This method violates multiple maintainability principles:

  • Heavy use of reflection with GetType(), GetProperty(), GetMethod() calls
  • Complex branching logic reducing readability
  • Mixed responsibilities (type checking, conversion, collection handling)

131-136: Eliminate code duplication in reasoning logic.

The RDFS reasoning code is duplicated between DeserializeRdfTriplesAsync and DeserializeRdfQuadsAsync methods, violating the DRY principle.

Also applies to: 166-171

🧹 Nitpick comments (2)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (2)

111-139: Consider making async method truly asynchronous.

The method is marked async but doesn't perform any asynchronous operations. The RDF parsing operations are synchronous, making the async keyword misleading.

Either remove async/await or use asynchronous parsing if available:

-private static async Task<IGraph> DeserializeRdfTriplesAsync(StreamReader streamReader,
+private static Task<IGraph> DeserializeRdfTriplesAsync(StreamReader streamReader,
     RdfFormat format, Uri baseUri)
 {
     // ... existing synchronous code ...
-    return graph;
+    return Task.FromResult(graph);
 }

26-30: Consider adding validation for media type constants.

The code directly uses OslcMediaType constants without verifying they are valid media type strings. If these constants are malformed, MediaTypeHeaderValue.Parse() will throw exceptions during formatter initialization.

Add validation or use try-parse methods:

-SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(OslcMediaType.APPLICATION_RDF_XML));
-SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(OslcMediaType.TEXT_TURTLE));
-SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(OslcMediaType.APPLICATION_JSON_LD));
-SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(OslcMediaType.APPLICATION_NTRIPLES));
+AddSupportedMediaType(OslcMediaType.APPLICATION_RDF_XML);
+AddSupportedMediaType(OslcMediaType.TEXT_TURTLE);
+AddSupportedMediaType(OslcMediaType.APPLICATION_JSON_LD);
+AddSupportedMediaType(OslcMediaType.APPLICATION_NTRIPLES);

+private void AddSupportedMediaType(string mediaType)
+{
+    if (MediaTypeHeaderValue.TryParse(mediaType, out var parsed))
+    {
+        SupportedMediaTypes.Add(parsed);
+    }
+    else
+    {
+        throw new ArgumentException($"Invalid media type: {mediaType}");
+    }
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b304928 and 635d63e.

📒 Files selected for processing (2)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Properties/launchSettings.json
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (1)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfInputFormatter.cs (1)

94-98: Replace reflection-based collection access with type-safe approach.

The code attempts to access collection properties using reflection, which is error-prone and reduces type safety. The current implementation already uses pattern matching for IList { Count: > 0 } but inconsistently.

Apply this fix to use consistent type-safe collection access:

-if (output is IList { Count: > 0 } list)
-{
-    return list[0];
-}
-return null;
+return output is IList { Count: > 0 } list ? list[0] : null;

Likely an incorrect or invalid review comment.

Copy link
Contributor

@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: 0

♻️ Duplicate comments (5)
OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (5)

44-45: Add null check for ContentType to prevent exceptions.

The context.ContentType could be null, which would cause issues when calling ToString() and passing to new MediaType().

Apply this diff to handle null ContentType:

-var contentType = context.ContentType.ToString();
-var requestedMediaType = new MediaType(contentType);
+var contentType = context.ContentType?.ToString();
+if (string.IsNullOrEmpty(contentType))
+{
+    contentType = OslcMediaType.APPLICATION_RDF_XML;
+}
+var requestedMediaType = new MediaType(contentType);

97-106: Handle nullable Port to prevent null reference exceptions.

The httpContext.Request.Host.Port returns a nullable integer. Direct comparison without null checking can cause issues.

Apply this diff to properly handle nullable port:

var portNum = httpContext.Request.Host.Port;
string? portString;
-if (portNum == 80 || portNum == 443)
+if (!portNum.HasValue || portNum == 80 || portNum == 443)
{
    portString = "";
}
else
{
-    portString = ":" + portNum;
+    portString = ":" + portNum.Value;
}

67-163: Refactor complex method to improve maintainability.

This 96-line method violates the Single Responsibility Principle and the "Building Maintainable Software" guideline to keep methods short. Consider extracting logic into smaller, focused methods.

Extract methods like:

  • BuildRequestUri(HttpContext httpContext, HttpRequest httpRequest)
  • ConvertFilteredResourceToGraph(Type type, object value, HttpContext httpContext, HttpRequest httpRequest)
  • ConvertResponseInfoToGraph(object value, string descriptionAbout, string responseInfoAbout)
  • ConvertEnumerableToGraph(object value)

169-171: Respect the negotiated encoding instead of hard-coding UTF-8.

The method receives selectedEncoding parameter from ASP.NET Core content negotiation but ignores it.

Apply this diff:

var responseStreamWriter = new HttpResponseStreamWriter(
    httpContextResponse.BodyWriter.AsStream(),
-    Encoding.UTF8);
+    selectedEncoding ?? Encoding.UTF8);

46-58: Use case-insensitive comparison for media types.

HTTP headers can vary in case. The current switch expression is case-sensitive.

Apply this diff to ensure case-insensitive matching:

-var requestedType = $"{requestedMediaType.Type}/{requestedMediaType.SubType}";
+var requestedType = $"{requestedMediaType.Type}/{requestedMediaType.SubType}".ToLowerInvariant();

var ctx = new SerializationContext
{
    Format = requestedType switch
    {
-        OslcMediaType.APPLICATION_RDF_XML => RdfFormat.RdfXml,
-        OslcMediaType.TEXT_TURTLE => RdfFormat.Turtle,
-        OslcMediaType.APPLICATION_NTRIPLES => RdfFormat.NTriples,
-        OslcMediaType.APPLICATION_JSON_LD => RdfFormat.JsonLd,
+        var t when t.Equals(OslcMediaType.APPLICATION_RDF_XML, StringComparison.OrdinalIgnoreCase) => RdfFormat.RdfXml,
+        var t when t.Equals(OslcMediaType.TEXT_TURTLE, StringComparison.OrdinalIgnoreCase) => RdfFormat.Turtle,
+        var t when t.Equals(OslcMediaType.APPLICATION_NTRIPLES, StringComparison.OrdinalIgnoreCase) => RdfFormat.NTriples,
+        var t when t.Equals(OslcMediaType.APPLICATION_JSON_LD, StringComparison.OrdinalIgnoreCase) => RdfFormat.JsonLd,
        _ => throw new ArgumentOutOfRangeException(nameof(requestedType),
            "Unknown RDF format"),
    },
🧹 Nitpick comments (5)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/Program.cs (1)

1-1: Remove unused import.

The System.Net namespace is imported but not used in this file.

-using System.Net;
-
var builder = DistributedApplication.CreateBuilder(args);
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs (2)

7-7: Address TODO comment about test collection organization.

Consider implementing the suggested refimpl-aspire collection for better test organization.

Would you like me to help implement the collection-based fixture approach or create an issue to track this improvement?


39-42: Consider making timeout values configurable.

Hard-coded timeout values (300s for startup, 5s delay) might not be suitable for all environments and could cause test flakiness.

Consider extracting these to configuration:

+private const int StartupTimeoutSeconds = 300;
+private const int StartupDelaySeconds = 5;

-await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(300)).ConfigureAwait(true);
+await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(StartupTimeoutSeconds)).ConfigureAwait(true);

// Wait a bit for the application to fully start
-await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
+await Task.Delay(TimeSpan.FromSeconds(StartupDelaySeconds)).ConfigureAwait(true);
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (2)

31-37: Fix inconsistent indentation.

The constructor has inconsistent indentation that deviates from C# coding standards.

-    public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture) 
-        {
+    public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
+    {
        _aspireFixture = aspireFixture;
        
        // Create HTTP client for direct API testing
        _httpClient = new HttpClient();
    }

61-61: Replace Console.WriteLine with proper test output mechanism.

Using Console.WriteLine in tests is not recommended. Consider using xUnit's ITestOutputHelper for test output.

Add ITestOutputHelper to the constructor and use it for output:

+private readonly ITestOutputHelper _output;

-public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
+public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture, ITestOutputHelper output)
{
    _aspireFixture = aspireFixture;
+    _output = output;

-Console.WriteLine($"Actual XML content: {content}");
+_output.WriteLine($"Actual XML content: {content}");
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 635d63e and 7ac86d6.

📒 Files selected for processing (12)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs (1 hunks)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ServiceProviderController.cs (1 hunks)
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/OSLC4NetExamples.Server.Tests.AspireHost.csproj (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/Program.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/Properties/launchSettings.json (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/appsettings.Development.json (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/appsettings.json (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/OSLC4NetExamples.Server.Tests.csproj (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (1 hunks)
✅ Files skipped from review due to trivial changes (5)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/appsettings.Development.json
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/Properties/launchSettings.json
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/appsettings.json
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/OSLC4NetExamples.Server.Tests.AspireHost.csproj
🚧 Files skipped from review as they are similar to previous changes (3)
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/ServiceProviderController.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/OSLC4NetExamples.Server.Tests.csproj
  • OSLC4Net_SDK/Examples/OSLC4NetExamples.Server.NetCoreApi/Controllers/CatalogController.cs
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests.AspireHost/Program.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs
  • OSLC4Net_SDK/OSLC4Net.Server.Providers/OslcRdfOutputFormatter.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test (windows-latest, 8.x, net8.0)
  • GitHub Check: test (windows-latest, 9.x, net9.0)
  • GitHub Check: test (windows-latest, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 8.x, net8.0)
  • GitHub Check: test (ubuntu-24.04, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04, 10.x, net10.0)
  • GitHub Check: test (ubuntu-24.04-arm, 9.x, net9.0)
  • GitHub Check: test (ubuntu-24.04-arm, 10.x, net10.0)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (1)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (1)

40-257: Well-structured integration tests.

The test methods follow good practices:

  • Proper async/await usage
  • Clear AAA (Arrange, Act, Assert) pattern
  • Comprehensive content negotiation testing
  • Proper resource disposal

@berezovskyi
Copy link
Member Author

We have smoke/contract tests which don't show up in coverage - new code is covered.

@berezovskyi berezovskyi merged commit dc18fbb into main Jul 6, 2025
13 of 15 checks passed
@berezovskyi berezovskyi deleted the b67-TextOutputFormatter branch July 6, 2025 19:33
Copy link
Contributor

@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: 4

🧹 Nitpick comments (6)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs (3)

7-7: Consider the suggested collection approach in the TODO comment.

The TODO comment suggests considering a "refimpl-aspire collection" approach. This should be addressed before finalizing the implementation.

Would you like me to help evaluate the pros and cons of the suggested collection approach versus the current assembly fixture approach?


34-35: Extract magic timeout value to a constant.

The 300-second timeout is a magic number that should be extracted to a named constant for better maintainability.

+    private const int ApplicationStartTimeoutSeconds = 300;
+
     protected async Task<DistributedApplication> SetupAspireAsync()
     {
         var builder = await DistributedApplicationTestingBuilder
-            .CreateAsync<OSLC4NetExamples_Server_Tests_AspireHost>().ConfigureAwait(true);
+            .CreateAsync<OSLC4NetExamples_Server_Tests_AspireHost>().ConfigureAwait(false);
 
         var app = await builder.BuildAsync();
 
-        await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(300)).ConfigureAwait(true);
+        await app.StartAsync().WaitAsync(TimeSpan.FromSeconds(ApplicationStartTimeoutSeconds)).ConfigureAwait(false);

50-50: Consider safer null handling for the property initialization.

The null-forgiving operator (= null!) masks potential null reference issues. Consider using proper null checking or making the property nullable until properly initialized.

-    public string NetCoreApiBaseUri { get; private set; } = null!;
+    public string NetCoreApiBaseUri { get; private set; } = string.Empty;

Or better yet, make it nullable and add proper validation:

-    public string NetCoreApiBaseUri { get; private set; } = null!;
+    public string? NetCoreApiBaseUri { get; private set; }
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (3)

101-116: Extract JSON test data to a constant or test data file.

The hardcoded JSON string makes the test harder to maintain and read. Consider extracting it to a constant or loading from a test data file.

+    private const string TestServiceProviderJson = """
+        [
+          {
+            "@id": "https://localhost:44387/oslc/catalog",
+            "@type": [
+              "http://open-services.net/ns/core#ServiceProvider"
+            ],
+            "http://purl.org/dc/terms/description": [
+              {
+                "@value": "test me",
+                "@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"
+              }
+            ]
+          }
+        ]
+        """;
+
     [Fact]
     public async Task TestCatalogPutEndpoint()
     {
         // Arrange
         var catalogUrl = $"{_aspireFixture.NetCoreApiBaseUri}oslc/catalog";
 
-        var serviceProviderJson = """
-        [
-          {
-            "@id": "https://localhost:44387/oslc/catalog",
-            "@type": [
-              "http://open-services.net/ns/core#ServiceProvider"
-            ],
-            "http://purl.org/dc/terms/description": [
-              {
-                "@value": "test me",
-                "@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"
-              }
-            ]
-          }
-        ]
-        """;
-
-        using var requestContent = new StringContent(serviceProviderJson, Encoding.UTF8, "application/ld+json");
+        using var requestContent = new StringContent(TestServiceProviderJson, Encoding.UTF8, "application/ld+json");

194-200: Extract media types to constants for better maintainability.

The media type strings are repeated throughout the tests and could be extracted to constants for better maintainability and to prevent typos.

+    private static class MediaTypes
+    {
+        public const string RdfXml = "application/rdf+xml";
+        public const string Turtle = "text/turtle";
+        public const string JsonLd = "application/ld+json";
+        public const string NTriples = "application/n-triples";
+    }
+
     [Fact]
     public async Task TestCatalogContentNegotiation()
     {
         // Arrange
         var catalogUrl = $"{_aspireFixture.NetCoreApiBaseUri}oslc/catalog";
 
         var mediaTypes = new[]
         {
-            "application/rdf+xml",
-            "text/turtle",
-            "application/ld+json",
-            "application/n-triples"
+            MediaTypes.RdfXml,
+            MediaTypes.Turtle,
+            MediaTypes.JsonLd,
+            MediaTypes.NTriples
         };

74-76: Extract common HTTP client configuration to a helper method.

The pattern of clearing Accept headers and setting new ones is repeated throughout the tests. Consider extracting this to a helper method to reduce code duplication.

+    private void SetAcceptHeader(string mediaType)
+    {
+        _httpClient.DefaultRequestHeaders.Accept.Clear();
+        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
+    }
+
     [Fact]
     public async Task TestCatalogGetEndpoint()
     {
         // Arrange
         var catalogUrl = $"{_aspireFixture.NetCoreApiBaseUri}oslc/catalog";
-        _httpClient.DefaultRequestHeaders.Accept.Clear();
-        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/ld+json"));
+        SetAcceptHeader("application/ld+json");

Apply similar changes to other test methods that follow this pattern.

Also applies to: 119-121, 145-147, 171-173, 205-207

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7572383 and 03b1230.

📒 Files selected for processing (4)
  • .github/workflows/main.yml (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs (1 hunks)
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/main.yml
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.cs`: Review the C# code against the Microsoft Framework design guidelines ...

**/*.cs: Review the C# code against the Microsoft Framework design guidelines as well as 10 guidelines
from "Building Maintainable Software" by Joost Visser. Point out deviations and improvements.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs
  • OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs
🧬 Code Graph Analysis (1)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs (1)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs (1)
  • RefimplAspireFixture (14-51)
🔇 Additional comments (2)
OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/AspireAppCollection.cs (1)

20-26: LGTM! Well-implemented xUnit collection fixture pattern.

The implementation correctly follows xUnit's collection fixture pattern for sharing test context across multiple test classes. The explanatory comment is helpful and the naming is clear.

OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs (1)

238-251: Excellent XML namespace validation implementation.

The XML namespace validation with XmlNamespaceManager is well-implemented and provides thorough validation of the OSLC XML structure. This demonstrates good understanding of XML processing and OSLC specifications.


public async ValueTask InitializeAsync()
{
DistributedApplication ??= await SetupAspireAsync().ConfigureAwait(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid ConfigureAwait(true) in test code.

Using ConfigureAwait(true) in test code is generally not recommended as it can cause deadlocks and is typically unnecessary. Consider using ConfigureAwait(false) or omitting it entirely.

-        DistributedApplication ??= await SetupAspireAsync().ConfigureAwait(true);
+        DistributedApplication ??= await SetupAspireAsync().ConfigureAwait(false);
📝 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
DistributedApplication ??= await SetupAspireAsync().ConfigureAwait(true);
DistributedApplication ??= await SetupAspireAsync().ConfigureAwait(false);
🤖 Prompt for AI Agents
In OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs at
line 27, replace ConfigureAwait(true) with ConfigureAwait(false) or remove
ConfigureAwait entirely to avoid potential deadlocks and unnecessary context
capturing in test code.

Comment on lines +41 to +42
// Wait a bit for the application to fully start
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider making the startup delay configurable.

The 5-second delay appears arbitrary and could be environment-dependent. Consider making it configurable or implementing a proper readiness check.

+    private const int ApplicationStartupDelaySeconds = 5;
+
     // Wait a bit for the application to fully start
-    await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
+    await Task.Delay(TimeSpan.FromSeconds(ApplicationStartupDelaySeconds)).ConfigureAwait(false);

Alternatively, consider implementing a proper health check endpoint to verify the application is ready instead of using an arbitrary delay.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/RefimplAspireFixture.cs
around lines 41 to 42, the fixed 5-second delay for application startup is
arbitrary and may not suit all environments. Replace this hardcoded delay by
making the wait time configurable via a parameter or configuration setting, or
better yet, implement a health check mechanism that repeatedly checks the
application's readiness endpoint until it confirms the app is ready before
proceeding.

Comment on lines +31 to +37
public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
{
_aspireFixture = aspireFixture;

// Create HTTP client for direct API testing
_httpClient = new HttpClient();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error handling for HttpClient initialization.

The constructor initializes the HttpClient without error handling. If the fixture is null or not properly initialized, this could cause issues during test execution.

 public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
 {
-    _aspireFixture = aspireFixture;
+    _aspireFixture = aspireFixture ?? throw new ArgumentNullException(nameof(aspireFixture));
 
     // Create HTTP client for direct API testing
     _httpClient = new HttpClient();
 }
📝 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
public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
{
_aspireFixture = aspireFixture;
// Create HTTP client for direct API testing
_httpClient = new HttpClient();
}
public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
{
_aspireFixture = aspireFixture ?? throw new ArgumentNullException(nameof(aspireFixture));
// Create HTTP client for direct API testing
_httpClient = new HttpClient();
}
🤖 Prompt for AI Agents
In OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs around
lines 31 to 37, the constructor initializes HttpClient without checking if the
aspireFixture parameter is null. Add a null check for aspireFixture at the start
of the constructor and throw an ArgumentNullException if it is null to prevent
issues during test execution. This ensures the fixture is properly initialized
before creating the HttpClient.


// Verify it contains expected OSLC elements
// Note: Log the actual content to see what we're getting
Console.WriteLine($"Actual XML content: {content}");
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace Console.WriteLine with proper test logging.

Using Console.WriteLine in production test code is not recommended. Consider using the xUnit ITestOutputHelper for better test output management.

 public class TestOslcNetCoreApi : IDisposable
 {
     private readonly RefimplAspireFixture _aspireFixture;
     private readonly HttpClient _httpClient;
+    private readonly ITestOutputHelper _output;
 
-    public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture)
+    public TestOslcNetCoreApi(RefimplAspireFixture aspireFixture, ITestOutputHelper output)
     {
         _aspireFixture = aspireFixture ?? throw new ArgumentNullException(nameof(aspireFixture));
+        _output = output ?? throw new ArgumentNullException(nameof(output));
 
         // Create HTTP client for direct API testing
         _httpClient = new HttpClient();
     }

Then replace:

-        Console.WriteLine($"Actual XML content: {content}");
+        _output.WriteLine($"Actual XML content: {content}");
🤖 Prompt for AI Agents
In OSLC4Net_SDK/Tests/OSLC4NetExamples.Server.Tests/TestOslcNetCoreApi.cs at
line 61, replace the Console.WriteLine statement with xUnit's ITestOutputHelper
logging. Inject ITestOutputHelper into the test class constructor, store it in a
private field, and use its WriteLine method to output the XML content instead of
Console.WriteLine.

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.

Port our MediaTypeFormatter to be a TextOutputFormatter

1 participant