Skip to content

Add region and asOfTime support to market data objects#1

Merged
mareeben merged 9 commits intomasterfrom
feature/marketDataObject
May 13, 2025
Merged

Add region and asOfTime support to market data objects#1
mareeben merged 9 commits intomasterfrom
feature/marketDataObject

Conversation

@mareeben
Copy link
Contributor

@mareeben mareeben commented May 12, 2025

Introduced new properties region and asOfTime across domain models, schemas, and repository methods for better regional and timestamp granularity in market data handling. Refactored FxSpotPriceObject to inherit from a new BaseMarketDataObject, consolidating shared logic. Updated schema versioning with a centralized version management system.

➕ What does this PR do?

🔨 Changes

  • Feature / enhancement
  • Bug fix
  • Chore / refactor

✅ Checklist

  • Tests added/updated
  • Docs updated (or NA)
  • No secrets in diff
  • Linked to issue #____
  • Screenshots / GIF added (UI changes)

🗒 Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Introduced support for region and time-specific fields in FX spot price data, enhancing granularity and context.
    • Added new data models for market data, including base and FX spot price types, with improved versioning and identification.
    • Implemented custom JSON converters for date and time-only fields to improve data serialization and compatibility.
    • Added centralized region constants for consistent region identification across the application.
  • Improvements

    • Enhanced schema and interface definitions to include region, as-of time, and integer-based version tracking.
    • Refined retry logic and region-aware operations for saving and retrieving market data.
    • Simplified version management with integer versioning and validation of region parameters.
  • Bug Fixes

    • Improved handling and validation of version and region fields in market data operations.
  • Chores

    • Updated project dependencies and reorganized code for better maintainability and extensibility.
    • Added mapping layers between domain models and data transfer objects for consistent data handling.

Introduced new properties `region` and `asOfTime` across domain models, schemas, and repository methods for better regional and timestamp granularity in market data handling. Refactored `FxSpotPriceObject` to inherit from a new `BaseMarketDataObject`, consolidating shared logic. Updated schema versioning with a centralized version management system.
Copy link

@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

🔭 Outside diff range comments (2)
src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (1)

38-49: ⚠️ Potential issue

Two query gotchas: DateOnly serialisation & lexicographical ORDER BY on version

  1. Parameter @asOfDate will be serialised to JSON using the default DateOnly converter (none), resulting in an empty string. Combined with the earlier note, this filter will never match until custom converters are added.

  2. version is stored as a string and ordered lexicographically, so "10" comes before "2".
    Convert to int in the query:

ORDER BY CAST(c.version AS int) DESC

(Cosmos SQL supports CAST to int).

Together these issues can lead to incorrect “latest” retrieval.

src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs (1)

8-15: 🛠️ Refactor suggestion

Consider using decimal for monetary values

Price is declared as double, which can introduce floating-point rounding errors (e.g., 0.1 + 0.2 != 0.3).
For FX spot prices 4–8 decimal places of precision are common and deterministic arithmetic is required.

-[JsonProperty("price")]
-public required double Price { get; set; }
+[JsonProperty("price")]
+public required decimal Price { get; set; }

If large numbers or sub-pip precision are expected, configure the JSON serializer with FloatParseHandling.Decimal.

🧹 Nitpick comments (7)
Phoenix.MarketData.Functions/SaveDocumentToDb.cs (1)

33-33: Consider using constants for region and standardizing time format

The region value "NY" is hardcoded as a string. Consider using constants or enums for regions to avoid typos and ensure consistency across the codebase. Also, for AsOfTime, consider standardizing the seconds parameter format by removing the leading zero (using 5 instead of 05).

-            Region = "NY",
+            Region = RegionConstants.NewYork, // Use a constant

-            AsOfTime = new TimeOnly(15, 30, 05),
+            AsOfTime = new TimeOnly(15, 30, 5),

Also applies to: 37-37

src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (1)

7-12: Good implementation of centralized schema versioning

The centralization of schema versions is a good practice. Consider enhancing this class with:

  1. Methods for version comparison (IsNewerThan, IsCompatibleWith)
  2. Additional constants for future versions (V1, V2)
  3. Documentation on versioning strategy and upgrade paths
public static class SchemaVersions
{
    public const string V0 = "0.0.0";
    // Ready for future versions
    // public const string V1 = "1.0.0";
    
    public static readonly HashSet<string> Supported = [V0];
    
    /// <summary>
    /// Checks if a schema version is supported
    /// </summary>
    public static bool IsSupported(string version) => Supported.Contains(version);
    
    /// <summary>
    /// Compares two versions and returns true if version1 is newer than version2
    /// </summary>
    public static bool IsNewerThan(string version1, string version2)
    {
        // Implementation for semantic version comparison
        return false; // Placeholder
    }
}
src/Phoenix.MarketData.Domain/Schemas/FxSpotPriceSchema.json (1)

59-63: Consider documenting time zone implications for asOfTime

The asOfTime property is well-defined with a pattern for HH:mm or HH:mm:ss, but there's no mention of the time zone context. Consider documenting whether this time should be interpreted in UTC, local time, or the time zone of the specified region.

    "asOfTime": {
      "type": "string",
      "pattern": "^[0-9]{2}:[0-9]{2}(:[0-9]{2})?$",
-      "description": "The specific time (HH:mm or HH:mm:ss) associated with the asOfDate, representing the moment the data was recorded. Example: '15:30' or '15:30:45'."
+      "description": "The specific time (HH:mm or HH:mm:ss) associated with the asOfDate, representing the moment the data was recorded. Example: '15:30' or '15:30:45'. Times are interpreted in the timezone of the specified region."
    },
src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (2)

9-11: Confirm that the sample ID format matches BaseMarketDataObject.CalculateId()

The sample value now includes the new region component, which is great.
However, the example still prefixes the string with [dataType].[assetclass] (using a dot) whereas BaseMarketDataObject.CalculateId() joins every segment with a double-underscore (__).
If consumers copy this comment literally they may create malformed IDs when querying.

Consider either:

  1. Removing the dot in the comment, or
  2. Adding a second example that reflects the exact output of CalculateId().

40-46: Region should be a constrained value (enum or constant list)

Region is currently an unconstrained string, which makes downstream aggregation difficult ("EU" vs "eu" vs "EUROPE").
If only a finite set of regions is supported, expose them through:

public static class Regions
{
    public const string Global = "global";
    public const string Europe = "eu";
    // …
}

or an enum with a custom converter. This prevents typo-driven data fragmentation.

src/Phoenix.MarketData.Domain/Models/BaseMarketDataObject.cs (2)

26-37: Consider adding a guard clause to prevent unnecessary Id recalculation.

When setting the Version property, the Id is always recalculated regardless of whether the value actually changed.

public string? Version
{
    get => _version;
    set
    {
+       if (_version == value)
+           return;
            
        _version = value;

        // Invalidate the ID if the Version changes
        _id = CalculateId();
    }
}

11-79: Consider adding XML documentation for public properties.

While the class has good XML documentation, adding documentation for individual properties would improve code readability and maintainability, especially for properties like Region and AsOfTime that have specific business meanings.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43f95ed and e3fe129.

📒 Files selected for processing (8)
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs (2 hunks)
  • src/Phoenix.MarketData.Domain/Models/BaseMarketDataObject.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (3 hunks)
  • src/Phoenix.MarketData.Domain/Schemas/FxSpotPriceSchema.json (3 hunks)
  • src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
Phoenix.MarketData.Functions/SaveDocumentToDb.cs (1)
src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (1)
  • SchemaVersions (7-12)
src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (1)
src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1)
  • Task (14-25)
src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs (1)
src/Phoenix.MarketData.Domain/Models/BaseMarketDataObject.cs (1)
  • BaseMarketDataObject (10-79)
🔇 Additional comments (9)
Phoenix.MarketData.Functions/SaveDocumentToDb.cs (1)

6-6: Schema versioning updated to use centralized version management

Good improvement to use the SchemaVersions.V0 constant instead of a hardcoded string. This aligns with the centralized version management approach.

Also applies to: 29-29

src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1)

14-16:

Details

✅ Verification successful

Ensure all callers of GetNextVersionAsync have been updated

This method signature change adds a required parameter without a default value. Ensure all callers have been updated to provide the region parameter to prevent runtime errors.


🏁 Script executed:

#!/bin/bash
# Find all callers of GetNextVersionAsync that might not have been updated

echo "Searching for calls to GetNextVersionAsync that might not include the region parameter..."
rg "GetNextVersionAsync\s*<.*>\s*\(\s*.*,\s*.*,\s*.*,\s*\w+\s*,\s*\w+\s*\)" -g "*.cs" --multiline --ignore-case

Length of output: 302


Let’s broaden the search to catch every usage of GetNextVersionAsync, including calls without explicit type arguments:


🏁 Script executed:

#!/bin/bash
# Search for all references to GetNextVersionAsync in C# files
echo "Searching for all occurrences of GetNextVersionAsync in .cs files..."
rg "GetNextVersionAsync" -g "*.cs" -n -C2

Length of output: 1399


All callers updated – no further action required

Verified that the only usage of GetNextVersionAsync in MarketDataRepository.cs now supplies the new region argument (marketData.Region). No other calls exist, so no runtime issues should occur from this signature change.

src/Phoenix.MarketData.Domain/Schemas/FxSpotPriceSchema.json (1)

44-47: Good implementation of region and asOfTime properties

The addition of region and asOfTime properties with proper descriptions and validation is well-implemented. Making region optional maintains backward compatibility with existing data.

Also applies to: 59-63

src/Phoenix.MarketData.Domain/Models/BaseMarketDataObject.cs (6)

1-10: Good implementation of a base class for market data objects.

The abstract class design with XML documentation effectively centralizes core market data functionality and implements the IMarketDataObject interface. This is a clean approach that will reduce code duplication across concrete market data classes.


12-14: Good use of backing fields for lazy initialization.

The backing fields for Id, CreatedTimestamp, and Version provide proper control over these properties. The nullable types are appropriate for this context.


16-21: Effective implementation of Id property with lazy initialization.

The Id property is well-implemented with lazy calculation and a private setter that prevents external modification while allowing deserialization.


48-49: New Region property aligns with PR objectives.

The required Region property properly implements the PR objective of enhancing regional data support.


67-68: New AsOfTime property aligns with PR objectives.

The nullable TimeOnly property for AsOfTime appropriately implements the PR objective of adding timestamp accuracy to market data objects.


70-78:

Details

❓ Verification inconclusive

Verify that all implementations use the same ID format.

The CalculateId method now incorporates the Region property into the ID format. Ensure this format is consistently used across all repositories and services that may construct these IDs independently.


🏁 Script executed:

#!/bin/bash
# Search for other ID calculation implementations in the codebase
rg "string\.Join\(\s*\"__\"\s*,\s*new\s*\[\]" --type cs

Length of output: 159


No additional ID generators found in the codebase
Only the CalculateId implementation in

  • src/Phoenix.MarketData.Domain/Models/BaseMarketDataObject.cs

remains, so there are no other in‐repo overrides to update.
Please verify that any external repositories or services which independently build these IDs have been updated to include the new Region segment. Consider adding or updating documentation (or a shared utility) to ensure consistency across all consumers.

Replaced `FxSpotPriceObject` and `FxVolSurfaceObject` with `FxSpotPriceData` using a new DTO-based structure for better separation of concerns. Introduced mappings for serialization and a retry mechanism in `MarketDataRepository` to handle version conflicts. Standardized region definitions and updated schemas to clarify UTC timestamps.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (5)
src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (1)

1-33: Consider adding comprehensive version management capabilities

The class provides basic version support but lacks functionality that will likely be needed as the system evolves.

Consider enhancing this class with additional functionality:

  1. Add validation for semantic version format
  2. Implement methods to get the latest supported version
  3. Add capability to check version compatibility (e.g., backward compatibility)
  4. Include methods to convert between versions if needed

Example for getting the latest version:

/// <summary>
/// Gets the latest supported schema version
/// </summary>
public static string GetLatestVersion()
{
    if (Supported.Count == 0)
        return string.Empty;
        
    return Supported.OrderByDescending(v => Version.Parse(v)).First();
}
src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (4)

15-19: Consider more robust Id calculation logic.

The lazy Id calculation is a good approach, but it might be safer to include checks to ensure all required properties are initialized before calculating.

public string Id
{
-   get => _id ??= CalculateId(); // If not set, calculate it
+   get 
+   {
+       if (_id == null)
+       {
+           // Check if required properties are initialized before calculating
+           if (string.IsNullOrEmpty(DataType) || string.IsNullOrEmpty(AssetClass) || 
+               string.IsNullOrEmpty(AssetId) || string.IsNullOrEmpty(Region) || 
+               string.IsNullOrEmpty(DocumentType))
+           {
+               throw new InvalidOperationException("Cannot calculate Id: required properties are not initialized.");
+           }
+           _id = CalculateId();
+       }
+       return _id;
+   }
    private set => _id = value;  // Can only be set during deserialization
}

44-44: Consider validating Region property.

The Region property is marked as required, but there's no validation against a known set of valid regions.

-public required string Region { get; set; }
+private string _region;
+public required string Region 
+{ 
+    get => _region; 
+    set 
+    {
+        // Consider validation against known regions
+        if (string.IsNullOrWhiteSpace(value))
+            throw new ArgumentException("Region cannot be null or empty", nameof(value));
+        
+        _region = value;
+        
+        // Invalidate ID if Region changes
+        if (_id != null)
+            _id = null;
+    }
+}

50-54: Add clarification about UTC default for CreateTimestamp.

The CreateTimestamp property defaulting to UTC now might not be clear to users. Consider adding a comment or documentation.

public DateTimeOffset CreateTimestamp
{
-   get => _createTimeStamp ??= DateTimeOffset.UtcNow;
+   // Defaults to UTC time when accessed if not explicitly set
+   get => _createTimeStamp ??= DateTimeOffset.UtcNow;
    set => _createTimeStamp = value;
}

60-68: Optimize Id calculation for potential null or empty Region.

The Id calculation should be resilient against uninitialized properties, particularly the new Region field.

private string CalculateId()
{
+   // Validate that required properties are initialized
+   if (string.IsNullOrEmpty(DataType) || string.IsNullOrEmpty(AssetClass) || 
+       string.IsNullOrEmpty(AssetId) || string.IsNullOrEmpty(Region) || 
+       string.IsNullOrEmpty(DocumentType))
+   {
+       throw new InvalidOperationException("Cannot calculate Id: required properties are not initialized.");
+   }
+
    var id = string.Join("__", new[] {
        DataType, AssetClass, AssetId, Region, AsOfDate.ToString("yyyyMMdd"), DocumentType});
    if (Version != null)
        id += $"__{Version}";

    return id;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3fe129 and 0e2c69c.

📒 Files selected for processing (16)
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs (2 hunks)
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs (0 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceObject.cs (0 hunks)
  • src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (3 hunks)
  • src/Phoenix.MarketData.Domain/Regions.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Schemas/FxSpotPriceSchema.json (3 hunks)
  • src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (3 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDto.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceObjectDto.cs (1 hunks)
💤 Files with no reviewable changes (2)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceObject.cs
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs
✅ Files skipped from review due to trivial changes (5)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceData.cs
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs
  • src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceObjectDto.cs
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDto.cs
  • src/Phoenix.MarketData.Domain/Regions.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Phoenix.MarketData.Domain/Schemas/FxSpotPriceSchema.json
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs
  • src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs
  • src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (4)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDto.cs (1)
  • FxSpotPriceDto (6-10)
src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1)
  • FxSpotPriceData (7-11)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1)
  • BaseMarketDataMapper (6-41)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1)
  • CopyBasePropertiesFrom (50-64)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (2)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1)
  • BaseMarketDataDto (10-65)
src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1)
  • BaseMarketData (9-69)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1)
  • BaseMarketDataDto (8-25)
🔇 Additional comments (10)
src/Phoenix.MarketData.Domain/Schemas/SchemaVersions.cs (3)

9-11: Good start with semantic versioning

The implementation follows semantic versioning principles with clear version constants. The commented placeholder for V1 shows foresight for future versioning.


18-18: Collection initialization style is modern and clean

Using the collection initialization syntax with square brackets (introduced in C# 12) is concise and readable.


23-23: Efficient implementation of version checking

The IsSupported method implementation is clean and efficient using the HashSet for O(1) lookups.

src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (2)

1-3: Appropriate imports for the mapper class.

The imports are correctly including both the domain models and serialization namespaces needed for the mapping operations.


6-7: Good use of static class for mapper functionality.

Using a static class is appropriate for this stateless mapping functionality, following common mapper pattern practices in C#.

src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (1)

5-6: Class design follows consistent mapping pattern.

The static class design is consistent with BaseMarketDataMapper, providing a clean approach for mapping operations.

src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1)

6-9: Appropriate XML documentation for the base DTO class.

The documentation clearly explains the purpose and functionality of the base DTO class.

src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (3)

5-10: Well-documented abstract base class.

The abstract class definition and XML documentation clearly explain the purpose and role of this base class in the domain model hierarchy.


11-14: Good use of backing fields for special property behavior.

The backing fields support the lazy initialization and special behavior of the Id, CreateTimestamp, and Version properties.


23-36: Version property correctly invalidates Id.

The Version property setter correctly invalidates the Id when the version changes, maintaining consistency.

Comment on lines 1 to 3
using Phoenix.MarketData.Domain.Models;

namespace Phoenix.MarketData.Infrastructure.Mapping;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing import for FxSpotPriceDto.

The import for Phoenix.MarketData.Infrastructure.Serialization namespace is missing, which contains the FxSpotPriceDto class.

using Phoenix.MarketData.Domain.Models;
+using Phoenix.MarketData.Infrastructure.Serialization;

namespace Phoenix.MarketData.Infrastructure.Mapping;

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

Comment on lines 10 to 48
public class BaseMarketDataDto
{
[JsonProperty("id")] // Ensures this property is deserialized from JSON
public string Id { get; set; }

[JsonProperty("schemaVersion")]
public string SchemaVersion { get; set; }

[JsonProperty("version")]
public string? Version { get; set; }

[JsonProperty("assetId")]
public string AssetId { get; set; }

[JsonProperty("assetClass")]
public string AssetClass { get; set; }

[JsonProperty("dataType")]
public string DataType { get; set; }

[JsonProperty("region")]
public string Region { get; set; }

[JsonProperty("tags")]
public List<string> Tags { get; set; }

[JsonProperty("documentType")]
public string DocumentType { get; set; }

[JsonProperty("createTimestamp")]
public DateTimeOffset CreateTimestamp { get; set; }

[JsonProperty("asOfDate")]
[JsonConverter(typeof(DateOnlyConverter))]
public DateOnly AsOfDate { get; set; }

[JsonProperty("asOfTime")]
[JsonConverter(typeof(TimeOnlyConverter))]
public TimeOnly? AsOfTime { get; set; }
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Initialize collections and consider required properties.

The class definition properly includes all necessary market data properties with appropriate JSON attributes. However, the Tags property doesn't have an initializer which could lead to NullReferenceExceptions, and many properties could benefit from the required modifier.

public class BaseMarketDataDto 
{
    [JsonProperty("id")] // Ensures this property is deserialized from JSON
    public string Id { get; set; }

    [JsonProperty("schemaVersion")]
-   public string SchemaVersion { get; set; }
+   public required string SchemaVersion { get; set; }

    [JsonProperty("version")]
    public string? Version { get; set; }

    [JsonProperty("assetId")]
-   public string AssetId { get; set; }
+   public required string AssetId { get; set; }

    [JsonProperty("assetClass")]
-   public string AssetClass { get; set; }
+   public required string AssetClass { get; set; }

    [JsonProperty("dataType")]
-   public string DataType { get; set; }
+   public required string DataType { get; set; }

    [JsonProperty("region")]
-   public string Region { get; set; }
+   public required string Region { get; set; }

    [JsonProperty("tags")]
-   public List<string> Tags { get; set; }
+   public List<string> Tags { get; set; } = new();

    [JsonProperty("documentType")]
-   public string DocumentType { get; set; }
+   public required string DocumentType { get; set; }

    // Other properties...
📝 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 class BaseMarketDataDto
{
[JsonProperty("id")] // Ensures this property is deserialized from JSON
public string Id { get; set; }
[JsonProperty("schemaVersion")]
public string SchemaVersion { get; set; }
[JsonProperty("version")]
public string? Version { get; set; }
[JsonProperty("assetId")]
public string AssetId { get; set; }
[JsonProperty("assetClass")]
public string AssetClass { get; set; }
[JsonProperty("dataType")]
public string DataType { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("tags")]
public List<string> Tags { get; set; }
[JsonProperty("documentType")]
public string DocumentType { get; set; }
[JsonProperty("createTimestamp")]
public DateTimeOffset CreateTimestamp { get; set; }
[JsonProperty("asOfDate")]
[JsonConverter(typeof(DateOnlyConverter))]
public DateOnly AsOfDate { get; set; }
[JsonProperty("asOfTime")]
[JsonConverter(typeof(TimeOnlyConverter))]
public TimeOnly? AsOfTime { get; set; }
public class BaseMarketDataDto
{
[JsonProperty("id")] // Ensures this property is deserialized from JSON
public string Id { get; set; }
[JsonProperty("schemaVersion")]
public required string SchemaVersion { get; set; }
[JsonProperty("version")]
public string? Version { get; set; }
[JsonProperty("assetId")]
public required string AssetId { get; set; }
[JsonProperty("assetClass")]
public required string AssetClass { get; set; }
[JsonProperty("dataType")]
public required string DataType { get; set; }
[JsonProperty("region")]
public required string Region { get; set; }
[JsonProperty("tags")]
public List<string> Tags { get; set; } = new();
[JsonProperty("documentType")]
public required string DocumentType { get; set; }
[JsonProperty("createTimestamp")]
public DateTimeOffset CreateTimestamp { get; set; }
[JsonProperty("asOfDate")]
[JsonConverter(typeof(DateOnlyConverter))]
public DateOnly AsOfDate { get; set; }
[JsonProperty("asOfTime")]
[JsonConverter(typeof(TimeOnlyConverter))]
public TimeOnly? AsOfTime { get; set; }
}

Added standardized null checks across mappers and DTO methods for robustness. Introduced property validation for lists and enhanced semantic version comparison logic. Moved schema files to the appropriate infrastructure layer for better organization.
@phoenixvc phoenixvc deleted a comment from coderabbitai bot May 12, 2025
Copy link

@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 (1)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (1)

1-2: ⚠️ Potential issue

Missing import for FxSpotPriceDto.

The code is missing an import for the FxSpotPriceDto class from the Phoenix.MarketData.Infrastructure.Serialization namespace. Without this import, the compiler will not be able to resolve references to this type.

using Phoenix.MarketData.Domain.Models;
+using Phoenix.MarketData.Infrastructure.Serialization;
🧹 Nitpick comments (2)
src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (2)

9-11: Consider using semantic versioning constants

The versioning approach seems to align with semantic versioning (SemVer), but this isn't explicitly stated. Making this explicit would improve code clarity and set expectations for future version additions.

You could enhance the documentation and add a more descriptive constant naming:

-    public const string V0 = "0.0.0";
-    // Ready for future versions
-    // public const string V1 = "1.0.0";
+    /// <summary>
+    /// Initial schema version (SemVer: Major.Minor.Patch)
+    /// </summary>
+    public const string V0 = "0.0.0";
+    
+    // Ready for future versions
+    // public const string V1 = "1.0.0";

18-18: Consider adding a method to retrieve the latest supported version

The class maintains a set of supported versions but doesn't provide a way to easily determine the latest one, which could be useful in many scenarios.

Add a method to retrieve the latest supported version:

public static readonly HashSet<string> Supported = [V0];

+/// <summary>
+/// Returns the latest supported schema version.
+/// </summary>
+/// <returns>The latest supported schema version.</returns>
+public static string GetLatestVersion()
+{
+    return Supported.Count > 0 
+        ? Supported.OrderBy(v => Version.Parse(v)).Last() 
+        : string.Empty;
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e2c69c and 7a44b47.

📒 Files selected for processing (9)
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Phoenix.MarketData.Domain.csproj (0 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Phoenix.MarketData.Infrastructure.csproj (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Schemas/FxSpotPriceSchema.json (3 hunks)
  • src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1 hunks)
💤 Files with no reviewable changes (1)
  • src/Phoenix.MarketData.Domain/Phoenix.MarketData.Domain.csproj
✅ Files skipped from review due to trivial changes (2)
  • src/Phoenix.MarketData.Infrastructure/Phoenix.MarketData.Infrastructure.csproj
  • src/Phoenix.MarketData.Infrastructure/Schemas/FxSpotPriceSchema.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (4)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDto.cs (1)
  • FxSpotPriceDto (6-10)
src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1)
  • FxSpotPriceData (7-11)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (2)
  • BaseMarketDataMapper (6-46)
  • ApplyToDomain (29-45)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataObjectDto.cs (1)
  • CopyBasePropertiesFrom (50-65)
🔇 Additional comments (4)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceMapper.cs (3)

7-21: Well-implemented ToDto method with proper null checking.

Good implementation with proper null validation using ArgumentNullException.ThrowIfNull(). The method correctly maps the Price property and leverages the base mapper to handle common properties.


23-31: Well-implemented bidirectional mapping support.

The ApplyToDomain method properly implements the missing bidirectional mapping functionality. It includes appropriate null validation for both parameters and correctly maps specific and base properties.


5-32:

Details

✅ Verification successful

Verify integration with repository layer.

This mapper class is a key part of the infrastructure for the new market data model with region and asOfTime support. Ensure it's properly integrated with the repository layer that consumes it.


🏁 Script executed:

#!/bin/bash
# Search for repository classes that use this mapper
rg -A 5 "FxSpotPriceMapper" --type csharp

Length of output: 1468


Integration Verified: FxSpotPriceMapper is wired into the repository layer

We confirmed that FxSpotPriceMapper.ToDto is invoked in MarketDataRepository.cs when projecting FxSpotPriceData to FxSpotPriceDto. Base properties (including region and asOfTime) flow through BaseMarketDataMapper. No further integration gaps were found.

• Location of usage:

  • src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (switch mapping to FxSpotPriceMapper.ToDto)
src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (1)

1-38: Overall implementation is clean and well-structured

The SchemaVersions class provides a clear, centralized approach to schema version management with good documentation and a clean API. The use of a static class with constants and utility methods is appropriate for this use case.

Replaced string-based versioning with integer-based for consistency, updated serialization classes, and introduced custom JSON converters for DateOnly and TimeOnly types. Removed redundant DTO, renamed and reorganized mapping and serialization classes, aligning with new conventions.
@coderabbitai
Copy link

coderabbitai bot commented May 13, 2025

Walkthrough

This update refactors the market data domain and infrastructure to introduce new base and FX spot price data models, region and time granularity support, and schema version management. It adds robust mapping and serialization logic, updates repository and versioning interfaces, and removes obsolete FX object classes. JSON serialization is globally configured for custom date and time handling.

Changes

File(s) Change Summary
Phoenix.MarketData.Functions/Program.cs Sets global default JSON serialization settings to use custom converters for TimeOnly and DateOnly.
Phoenix.MarketData.Functions/SaveDocumentToDb.cs Updates to use new FxSpotPriceData model, sets SchemaVersion to constant, adds Region and AsOfTime, changes Price to decimal, and updates usings.
src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs Adds new abstract class BaseMarketData implementing IMarketDataObject with core properties and ID logic.
src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs Adds new class FxSpotPriceData inheriting from BaseMarketData with a required decimal Price property.
src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs, src/Phoenix.MarketData.Domain/Models/FxVolSurfaceObject.cs Removes obsolete FX spot price and volatility surface object classes and related nested types.
src/Phoenix.MarketData.Domain/Models/FxVolSurfaceData.cs Adds empty file for future FX volatility surface data model.
src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs Updates interface: adds Region and AsOfTime, changes Version to nullable int, corrects timestamp property, updates ID format.
src/Phoenix.MarketData.Domain/Regions.cs Adds static class Regions with region code constants.
src/Phoenix.MarketData.Domain/AssetClass.cs Adds static class AssetClass with predefined asset class string constants.
src/Phoenix.MarketData.Domain/Phoenix.MarketData.Domain.csproj Removes Newtonsoft.Json package reference.
src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs Refactors repository: adds retry logic, region support, updates method signatures, and introduces domain-to-DTO mapping.
src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs Updates version management to include region, changes version type to int, simplifies logic.
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs Adds static mapper for converting between BaseMarketData and DTO.
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs Adds static mapper for converting between FxSpotPriceData and DTO.
src/Phoenix.MarketData.Infrastructure/Phoenix.MarketData.Infrastructure.csproj Adds Newtonsoft.Json package reference.
src/Phoenix.MarketData.Infrastructure/Schemas/FxSpotPriceSchema.json Updates schema: replaces timestamp with asOfDate, adds optional region and asOfTime properties.
src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs Adds static class for schema version management with comparison and support checks.
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs Adds DTO class for base market data with JSON attributes and a property copy method.
src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs Adds DTO class for FX spot price data inheriting from BaseMarketDataDto.
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs Adds custom JSON converter for DateOnly type.
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs Adds custom JSON converter for TimeOnly type.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Repository as MarketDataRepository
    participant VersionMgr as VersionManager
    participant Mapper
    participant CosmosDB

    Caller->>Repository: SaveAsync(marketData, saveNextVersion)
    alt saveNextVersion = true
        Repository->>VersionMgr: GetNextVersionAsync(..., region, ...)
        VersionMgr->>Repository: GetLatestAsync(..., region, ...)
        Repository-->>VersionMgr: latestVersion
        VersionMgr-->>Repository: nextVersion (int)
        Repository->>marketData: set Version
    end
    Repository->>Mapper: MapToDto(marketData)
    Mapper-->>Repository: DTO
    Repository->>CosmosDB: Insert DTO
    alt Conflict (409)
        Repository->>Repository: Retry up to 3 times with backoff
    end
    CosmosDB-->>Repository: Success/Failure
    Repository-->>Caller: Task Complete / Exception
Loading

Poem

In fields of code where data flows,
A rabbit hops where schema grows.
New regions bloom, and time ticks true,
With mappers fresh and models new.
Versioned streams and JSON bright—
The market data leaps to light!
🐇✨

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 458904a and e243c83.

📒 Files selected for processing (3)
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs (2 hunks)
  • src/Phoenix.MarketData.Domain/AssetClass.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Regions.cs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/Phoenix.MarketData.Domain/AssetClass.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Phoenix.MarketData.Domain/Regions.cs
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs
✨ Finishing Touches
  • 📝 Generate Docstrings

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

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

Copy link

@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

🧹 Nitpick comments (8)
src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1)

1-11: Domain Model for FX Spot Price
The FxSpotPriceData class correctly inherits from BaseMarketData and introduces a required Price property. Consider adding an XML doc comment for the Price property to maintain consistency with the class summary and improve documentation.

src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (1)

1-9: DTO for FX Spot Price Serialization
The DTO extends BaseMarketDataDto and properly decorates the Price property for JSON serialization. For consistency with the domain layer, consider adding XML comments to the class and the Price property.

src/Phoenix.MarketData.Domain/Regions.cs (1)

3-9: Well-structured constants class for region support

The Regions class provides a clean implementation of region constants that will help standardize region references throughout the codebase. This aligns well with the PR objective to add region support to market data objects.

Consider adding XML documentation to the class and each constant to provide more context about their usage and meaning, especially for less obvious abbreviations like "ldn" and "jhb".

src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)

22-26: Consider adding culture specification to WriteJson for consistency

For consistency with the DateOnlyJsonConverter implementation shown in the context snippets, consider specifying the culture when formatting the TimeOnly value.

-        writer.WriteValue(value.ToString(Format));
+        writer.WriteValue(value.ToString(Format, System.Globalization.CultureInfo.InvariantCulture));
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (1)

8-22: Consider optimizing the ToDto method to avoid intermediate object creation

The current implementation creates an unnecessary intermediate BaseMarketDataDto object that's only used for property copying, which is inefficient.

     public static FxSpotPriceDataDto ToDto(FxSpotPriceData domain)
     {
         ArgumentNullException.ThrowIfNull(domain);
         
         var dto = new FxSpotPriceDataDto
         {
             Price = domain.Price,
-        };
-
-        // Apply base properties
-        var baseDto = BaseMarketDataMapper.ToDto(domain);
-        dto.CopyBasePropertiesFrom(baseDto);
+            // Apply base properties directly
+            Id = domain.Id,
+            SchemaVersion = domain.SchemaVersion,
+            Version = domain.Version,
+            AssetId = domain.AssetId,
+            AssetClass = domain.AssetClass,
+            DataType = domain.DataType,
+            Region = domain.Region,
+            DocumentType = domain.DocumentType,
+            CreateTimestamp = domain.CreateTimestamp,
+            AsOfDate = domain.AsOfDate,
+            AsOfTime = domain.AsOfTime,
+            Tags = domain.Tags
+        };

         return dto;
     }

Alternatively, if you want to maintain consistency with the current pattern, consider adding a method to BaseMarketDataMapper that populates an existing DTO instead of creating a new one.

src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (1)

32-48: Improve exception handling in IsNewerThan method.

The current implementation catches FormatException only to throw a new one with a more descriptive message. This creates an unnecessary exception chaining and loses the original exception details.

Consider including the original exception as the inner exception when rethrowing:

catch (FormatException ex)
{
-    throw new FormatException("Could not parse version string when trying to compare versions.");
+    throw new FormatException("Could not parse version string when trying to compare versions.", ex);
}
src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1)

11-13: Inconsistent casing in private backing field name.

The naming convention for private fields appears inconsistent. The _createTimeStamp field uses camel case with an uppercase 'S' while other fields use consistent camel casing.

-    private DateTimeOffset? _createTimeStamp; // Backing field for CreatedTimestamp
+    private DateTimeOffset? _createTimestamp; // Backing field for CreateTimestamp
src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (1)

90-101: Consider implementation timeline for commented mapper placeholders.

The MapToDto method effectively uses pattern matching to handle different market data types, but contains commented placeholders for future mappers.

            return domain switch
            {
                FxSpotPriceData fxSpotData => FxSpotPriceDataMapper.ToDto(fxSpotData),
-               //Add other mappers...
-               //ForwardPriceData f => ForwardPriceMapper.ToDto(f),
-               //YieldCurveData y => YieldCurveMapper.ToDto(y),
-               //VolatilitySurfaceData v => VolatilitySurfaceMapper.ToDto(v),
+               // TODO: Implement additional mappers in future PRs - TICKET-123
                _ => throw new NotSupportedException($"Unsupported market data type: {typeof(T).Name}")
            };

If these additional mappers are planned for future work rather than this PR, consider replacing the commented code with a single TODO comment that references a ticket number for better tracking.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43f95ed and 1243cb9.

📒 Files selected for processing (21)
  • Phoenix.MarketData.Functions/Program.cs (2 hunks)
  • Phoenix.MarketData.Functions/SaveDocumentToDb.cs (2 hunks)
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs (0 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceData.cs (1 hunks)
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceObject.cs (0 hunks)
  • src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (4 hunks)
  • src/Phoenix.MarketData.Domain/Phoenix.MarketData.Domain.csproj (0 hunks)
  • src/Phoenix.MarketData.Domain/Regions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (3 hunks)
  • src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Phoenix.MarketData.Infrastructure.csproj (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Schemas/FxSpotPriceSchema.json (3 hunks)
  • src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1 hunks)
💤 Files with no reviewable changes (3)
  • src/Phoenix.MarketData.Domain/Phoenix.MarketData.Domain.csproj
  • src/Phoenix.MarketData.Domain/Models/FxVolSurfaceObject.cs
  • src/Phoenix.MarketData.Domain/Models/FxSpotPriceObject.cs
🧰 Additional context used
🧬 Code Graph Analysis (9)
Phoenix.MarketData.Functions/Program.cs (2)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)
  • TimeOnlyJsonConverter (18-34)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (1)
  • DateOnlyJsonConverter (16-30)
src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (2)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (1)
  • FxSpotPriceDataDto (8-22)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (1)
  • BaseMarketDataDto (10-66)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (1)
  • WriteJson (20-23)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1)
src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1)
  • BaseMarketData (9-74)
src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (4)
src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (1)
  • FxSpotPriceDataDto (5-9)
src/Phoenix.MarketData.Domain/Models/FxSpotPriceData.cs (1)
  • FxSpotPriceData (7-11)
src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1)
  • BaseMarketDataMapper (6-46)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (1)
  • CopyBasePropertiesFrom (50-65)
src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (2)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (1)
  • DateOnly (25-29)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)
  • TimeOnly (28-33)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (1)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)
  • WriteJson (22-26)
src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1)
src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (2)
  • Task (28-63)
  • Task (66-88)
src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (2)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/DateOnlyJsonConverter.cs (2)
  • DateOnlyJsonConverter (16-30)
  • DateOnly (25-29)
src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (2)
  • TimeOnlyJsonConverter (18-34)
  • TimeOnly (28-33)
🔇 Additional comments (16)
src/Phoenix.MarketData.Infrastructure/Phoenix.MarketData.Infrastructure.csproj (1)

11-11: Centralize JSON Dependency in Infrastructure
Adding the Newtonsoft.Json package here aligns with the goal of consolidating serialization logic. Ensure this version (13.0.3) is compatible with all downstream projects and check for any known vulnerabilities (e.g., via a security advisory scan).

Phoenix.MarketData.Functions/Program.cs (1)

9-11: Global JSON Converter Registration
Registering TimeOnlyJsonConverter and DateOnlyJsonConverter via JsonConvert.DefaultSettings ensures consistent serialization of TimeOnly and DateOnly types across the Functions project. Verify this executes before any JSON operations and consider extending the settings (e.g., NullValueHandling, Culture) if future requirements arise.

Also applies to: 35-41

Phoenix.MarketData.Functions/SaveDocumentToDb.cs (2)

5-8: LGTM: Added necessary imports for new domain types

The added imports correctly reference the new domain namespace and schema version constants needed for the updated implementation.


28-40: Correctly implemented market data object with new region and time properties

The code successfully transitions from using the old FxSpotPriceObject to the new FxSpotPriceData class with proper initialization of all required properties, including the newly added Region and AsOfTime. The use of SchemaVersions.V0 aligns with the centralized version management approach mentioned in the PR objectives.

src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs (1)

5-18: Good documentation with comprehensive XML comments

The XML documentation clearly explains the purpose, behavior, and usage of the converter, which will be valuable for other developers working with the code.

src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (1)

24-32: LGTM: ApplyToDomain implementation is clean and effective

The method correctly applies the specific property (Price) before delegating to the base mapper for common properties. The null checks are properly implemented using ArgumentNullException.ThrowIfNull.

src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1)

1-47: Well-designed mapper implementation for BaseMarketData

The implementation provides clean mapping between the domain model and DTO, including proper null validation and defensive copying of collections. Good use of ArgumentNullException.ThrowIfNull() for parameter validation and proper handling of the Tags collection.

src/Phoenix.MarketData.Infrastructure/Schemas/FxSpotPriceSchema.json (3)

6-6: Update of required fields aligns with domain model changes

The schema correctly updates the required fields to use asOfDate instead of timestamp, matching the domain model changes that replace timestamp with more granular date representation.


44-47: Region property added correctly

Good addition of the region property with appropriate description explaining its purpose for geographic market segmentation.


59-63: Time representation implemented with clear format

The asOfTime property is well-defined with an appropriate regex pattern that validates both HH:mm and HH:mm:ss formats. The description clearly explains the UTC time interpretation.

src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs (1)

14-27: Version management updated correctly for region support

The method has been properly updated to include region support with appropriate validation. The return type change from Task<string> to Task<int> aligns with the domain model update where Version is now an int? instead of a string.

src/Phoenix.MarketData.Domain/Models/Interfaces/IMarketDataObject.cs (1)

76-81:

Details

✅ Verification successful

Ensure TimeOnly serialization is properly configured across the application.

The new TimeOnly? property requires proper JSON serialization. I see the converters are defined, but confirming they're registered in the DI container is essential.


🏁 Script executed:

#!/bin/bash
# Check if JsonConverters are registered in the application

echo "Checking for JsonConverter registration..."
rg -p "AddJsonOptions|JsonConverter|configure.*json" --type csharp

Length of output: 1193


🏁 Script executed:

#!/bin/bash
# Verify JSON converter registration in Program.cs
echo "Searching for AddJsonOptions usage in Program.cs..."
rg -n "AddJsonOptions" src/Phoenix.MarketData.Functions/Program.cs

echo "Inspecting converter additions in Program.cs..."
rg -n "Converters.Add" src/Phoenix.MarketData.Functions/Program.cs

Length of output: 519


🏁 Script executed:

#!/bin/bash
# Locate all Program.cs files in the repo
echo "Locating Program.cs files..."
find . -type f -name Program.cs

# Search for JSON converter registration patterns across all C# files
echo "Searching for converter registrations..."
rg -n "Converters\.Add" --type cs || true
rg -n "AddJsonOptions" --type cs || true
rg -n "Configure<.*Json" --type cs || true
rg -n "SerializerOptions" --type cs || true

Length of output: 403


🏁 Script executed:

#!/bin/bash
# Search for TimeOnlyJsonConverter usage in Functions Program.cs
echo "Locating converter usage..."
rg -n "TimeOnlyJsonConverter" -C 3 Phoenix.MarketData.Functions/Program.cs || true

# Print the top of Program.cs to inspect JSON configuration
echo "Printing Program.cs head..."
sed -n '1,80p' Phoenix.MarketData.Functions/Program.cs

Length of output: 2567


JSON Converter Registration Confirmed

I’ve verified that the TimeOnlyJsonConverter is properly registered:

  • Global Newtonsoft setup in Phoenix.MarketData.Functions/Program.cs
    JsonConvert.DefaultSettings includes

    Converters = [
        new TimeOnlyJsonConverter(), 
        new DateOnlyJsonConverter()
    ]
  • Property-level attributes in Infrastructure/Serialization/BaseMarketDataDto.cs
    Properties decorated with

    [JsonConverter(typeof(TimeOnlyJsonConverter))]
    [JsonConverter(typeof(DateOnlyJsonConverter))]

No additional DI registration is required for Newtonsoft-based serialization.

src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (1)

50-65: LGTM! The CopyBasePropertiesFrom method has proper null checking.

The method includes appropriate null argument validation and defensive copying of the Tags collection, which are good practices for preventing bugs.

src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs (3)

1-5: Appropriate imports added for new functionality.

The additions of System.Net and Phoenix.MarketData.Infrastructure.Mapping namespaces are necessary to support the new error handling and mapping functionality.


30-63: Good implementation of retry logic addressing previous race condition concerns.

The retry mechanism with backoff delay properly addresses the race condition mentioned in the past review comment. This implementation:

  1. Attempts to save with up to 3 retries
  2. Catches 409 Conflict exceptions specifically when attempting to save next version
  3. Uses a simple backoff strategy with increasing delay
  4. Throws an explicit exception if all retries fail

The addition of mapping through MapToDto also supports the domain model refactoring mentioned in the PR objectives.


66-76: Region parameter correctly added to query.

The region parameter has been appropriately added to the method signature and SQL query, aligning with the PR objective of adding region support to market data objects.

@@ -0,0 +1 @@
 No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove or Implement Placeholder File
The file is currently empty (only contains a BOM) and does not declare any class. If FxVolSurfaceData is intended for future use, please add at least a class stub or documentation; otherwise, remove this file to avoid confusion.

Comment on lines +1 to +31
using System.Globalization;
using Newtonsoft.Json;

namespace Phoenix.MarketData.Infrastructure.Serialization.JsonConverters
{
/// <summary>
/// A custom JSON converter for serializing and deserializing <see cref="DateOnly"/> values.
/// </summary>
/// <remarks>
/// This converter handles <see cref="DateOnly"/> types by formatting them as strings
/// in the "yyyy-MM-dd" format during serialization and deserialization.
/// </remarks>
/// <example>
/// Use this converter with <see cref="JsonConverterAttribute"/> to convert <see cref="DateOnly"/> properties in JSON.
/// </example>
public class DateOnlyJsonConverter : JsonConverter<DateOnly>
{
private const string Format = "yyyy-MM-dd";

public override void WriteJson(JsonWriter writer, DateOnly value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString(Format, CultureInfo.InvariantCulture));
}

public override DateOnly ReadJson(JsonReader reader, Type objectType, DateOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var str = reader.Value?.ToString();
return DateOnly.ParseExact(str!, Format, CultureInfo.InvariantCulture);
}
}
} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve null handling in DateOnly JSON converter

The converter is well-implemented overall with good documentation and use of invariant culture. However, there's a potential issue in the ReadJson method.

The forced unwrap on line 28 (str!) will throw an exception if str is null. Consider adding proper null handling:

public override DateOnly ReadJson(JsonReader reader, Type objectType, DateOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
{
    var str = reader.Value?.ToString();
-   return DateOnly.ParseExact(str!, Format, CultureInfo.InvariantCulture);
+   if (string.IsNullOrEmpty(str))
+   {
+       throw new JsonSerializationException("Cannot convert null or empty string to DateOnly.");
+   }
+   
+   return DateOnly.ParseExact(str, Format, CultureInfo.InvariantCulture);
}

This change provides more descriptive error messages and avoids potential null reference exceptions.

Comment on lines +21 to +25
/// <summary>
/// Represents the version of a market data object. This property is used to track
/// incremental updates or changes to the data over time.
/// </summary>
int? Version { get; set; }
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Breaking change: Version property type changed from string to int.

Changing the Version property type from string? to int? is a breaking change that will require updates to all implementations and consumers of this interface.


🏁 Script executed:

#!/bin/bash
# Identify code that might be affected by the Version property type change

echo "Searching for Version property usage in implementations..."
rg -p "Version\s*{\s*get" --type csharp

echo "Searching for Version property assignments..."
rg -p "\.Version\s*=" --type csharp

echo "Searching for Version property access..."
rg -p "\.Version\b" --type csharp | grep -v "\.Version\s*="

Length of output: 1377


Breaking Change: Version property type changed from string? to int?

The change of the IMarketDataObject.Version from string?int? is breaking and requires updates to all implementations and consumers:

• src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs
– Line 16: Version = domain.Version;
– Line 35: domain.Version = dto.Version;

• src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs
– Line 55: public string? Version { get; set; } (needs to be int? or conversion logic)

• src/Phoenix.MarketData.Infrastructure/Cosmos/VersionManager.cs
– Line 23: if (latest == null || latest.Version == null)
– Line 26: return latest.Version.Value + 1;

• src/Phoenix.MarketData.Infrastructure/Cosmos/MarketDataRepository.cs
– Line 36: marketData.Version = await _versionManager.GetNextVersionAsync<T>(…);

Action items:

  • Update DTO’s Version property type or add appropriate intstring conversions.
  • Adjust mapping in BaseMarketDataMapper to parse/format the version.
  • Review repository and version-management logic to ensure all numeric operations compile and behave as expected.

/// in ISO 8601 format, e.g., "2023-10-19T15:23:00Z".
/// </summary>
DateTimeOffset CreatedTimestamp { get; }
DateTimeOffset CreateTimestamp { get; }
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Breaking change: Property renamed from CreatedTimestamp to CreateTimestamp.

The property has been renamed from CreatedTimestamp to CreateTimestamp (removing the 'd'). This is a breaking change that will affect all implementations and consumers of this interface.


🏁 Script executed:

#!/bin/bash
# Identify code that might be affected by the timestamp property rename

echo "Searching for CreatedTimestamp property usage..."
rg -p "CreatedTimestamp" --type csharp

Length of output: 287


Breaking Change: Update Implementation for CreateTimestamp

The interface property was renamed from CreatedTimestamp to CreateTimestamp. This requires updating any implementation or references to the old name.

• File: src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (line 12)

  • The backing-field comment still refers to CreatedTimestamp and should be updated to CreateTimestamp.

Suggested diff:

-    private DateTimeOffset? _createTimeStamp; // Backing field for CreatedTimestamp
+    private DateTimeOffset? _createTimeStamp; // Backing field for CreateTimestamp

Please verify there are no other lingering references (e.g., in tests or other consumers) to the old property name.

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

Comment on lines +46 to +48
[JsonProperty("asOfTime")]
[JsonConverter(typeof(TimeOnlyJsonConverter))]
public TimeOnly? AsOfTime { get; set; }
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

JSON converter implementation for TimeOnly appears incomplete.

The TimeOnlyJsonConverter being applied here uses TimeOnly.Parse() which doesn't enforce a specific format, potentially leading to inconsistent serialization/deserialization.

Looking at the converter implementation in the provided code snippet, I notice it uses TimeOnly.Parse(str!) without specifying a format. This is inconsistent with the DateOnlyJsonConverter which uses a specific format string.


🏁 Script executed:

#!/bin/bash
# Check how the TimeOnly converter is used elsewhere in the codebase
echo "Checking TimeOnly converter usage and implementation..."
rg -p "TimeOnly" --type csharp -A 5 -B 5

Length of output: 3951


Enforce strict time format in TimeOnlyJsonConverter ReadJson

The converter’s WriteJson uses the "HH:mm:ss" constant, but ReadJson still calls TimeOnly.Parse(str!), which accepts multiple formats. This can lead to unexpected parsing behavior. Please update ReadJson to use TimeOnly.ParseExact with the same format (and specify CultureInfo.InvariantCulture).

  • File: src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs
    Location: ReadJson method (around line 32)

Suggested diff:

--- a/src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs
+++ b/src/Phoenix.MarketData.Infrastructure/Serialization/JsonConverters/TimeOnlyJsonConverter.cs
@@ -30,7 +30,9 @@ public class TimeOnlyJsonConverter : JsonConverter<TimeOnly>
         var str = reader.Value?.ToString();
-        return TimeOnly.Parse(str!);
+        // Enforce the same "HH:mm:ss" format on deserialization
+        return TimeOnly.ParseExact(str!, Format, CultureInfo.InvariantCulture);
 }
  • Add using System.Globalization; at the top of the file.

Comment on lines +23 to +36
public int? Version
{
get => _version;
set
{
if (_version == value)
return;

_version = value;

// Invalidate the ID if the Version changes
_id = CalculateId();
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ID recalculation happens only when Version changes, not other ID components.

The Version property setter invalidates and recalculates the ID, but changes to other ID components (AssetId, Region, etc.) don't trigger recalculation.

Consider implementing similar logic for other properties that contribute to the ID, or use a more general solution that tracks changes to all relevant properties:

public required string AssetId { get; set; }
+    {
+        get => _assetId;
+        set
+        {
+            if (_assetId == value)
+                return;
+                
+            _assetId = value;
+            _id = null; // Invalidate ID when component changes
+        }
+    }

Alternatively, consider using C# property change notification or implementing a generalized solution that invalidates the ID whenever any of its components change.

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

mareeben and others added 3 commits May 13, 2025 12:44
…ters/TimeOnlyJsonConverter.cs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Replaced property-by-property mapping with constructor-based initialization for DTOs, improving clarity and reducing boilerplate. Added validation for ID calculations and enhanced error handling for schema version parsing. Adjusted the JSON deserialization logic for TimeOnly to handle culture-specific formats.
Added checks for null, empty, and invalid string formats during deserialization. Introduced TryParse method for safer conversion and clearer
Copy link

@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

🧹 Nitpick comments (4)
src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (2)

5-9: Consider making the Price property immutable.

The Price property is currently mutable with both a getter and setter. Since DTOs are typically used for data transfer rather than data manipulation, consider making it immutable to prevent unintended modifications after construction.

-    public decimal Price { get; set; }
+    public decimal Price { get; }

10-15: Long parameter list in constructor might benefit from a builder pattern.

The constructor has a large number of parameters which can make it difficult to use correctly. Consider implementing a builder pattern or using record types if you're using C# 9.0+ to improve readability and maintainability.

For example, with C# records:

public record FxSpotPriceDataDto : BaseMarketDataDto
{
    [JsonProperty("price")]
    public decimal Price { get; init; }
    
    public FxSpotPriceDataDto(BaseMarketDataDto baseData, decimal price) 
        : base(baseData)
    {
        Price = price;
    }
}

This assumes that BaseMarketDataDto would be updated to support copying its properties.

src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (2)

54-67: Exception handling implemented, but logging is missing

The GetLatestVersion method now includes proper exception handling for malformed version strings, addressing a previous review suggestion. However, the comment on line 65 mentions "Log the error" but no actual logging is implemented.

Consider implementing actual logging:

catch (FormatException ex)
{
    // Log the error and return a fallback version or rethrow with better context
+   _logger.LogError(ex, "Invalid version format found in supported versions.");
    throw new FormatException("Invalid version format found in supported versions.", ex);
}

Or remove the logging comment if logging isn't needed:

catch (FormatException ex)
{
-   // Log the error and return a fallback version or rethrow with better context
    throw new FormatException("Invalid version format found in supported versions.", ex);
}

7-68: Consider adding version validation in static constructor

While the class handles exceptions during runtime operations, it would be beneficial to validate the format of version strings when the class is initialized to catch configuration errors early.

Consider adding a static constructor to validate all predefined versions:

public static class SchemaVersions
{
    /// <summary>
    /// Initial schema version (SemVer: Major.Minor.Patch)
    /// </summary>
    public const string V0 = "0.0.0";

    // Ready for future versions
    // public const string V1 = "1.0.0";

    /// <summary>
    /// Represents the collection of schema versions supported within the Phoenix MarketData domain.
    /// This set of versions is used to validate and check compatibility for schema versioning in
    /// operations and data handling.
    /// </summary>
    public static readonly HashSet<string> Supported = [V0];
    
+   static SchemaVersions()
+   {
+       // Validate all supported versions at startup
+       foreach (var version in Supported)
+       {
+           try
+           {
+               _ = Version.Parse(version);
+           }
+           catch (FormatException ex)
+           {
+               throw new FormatException($"Invalid version format '{version}' in SchemaVersions.", ex);
+           }
+       }
+   }
    
    // Rest of the class...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d39f75b and 1d5157f.

📒 Files selected for processing (6)
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs (1 hunks)
  • src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Phoenix.MarketData.Infrastructure/Mapping/BaseMarketDataMapper.cs
  • src/Phoenix.MarketData.Infrastructure/Mapping/FxSpotPriceDataMapper.cs
  • src/Phoenix.MarketData.Infrastructure/Serialization/BaseMarketDataDto.cs
  • src/Phoenix.MarketData.Domain/Models/BaseMarketData.cs
🔇 Additional comments (4)
src/Phoenix.MarketData.Infrastructure/Serialization/FxSpotPriceDataDto.cs (1)

1-4: Appropriate imports and namespace.

The inclusion of Newtonsoft.Json is appropriate for the JSON serialization needs of this DTO.

src/Phoenix.MarketData.Infrastructure/Schemas/SchemaVersions.cs (3)

1-1: Correct namespace implementation

The namespace now correctly matches the file location in the Infrastructure project, addressing the inconsistency noted in a previous review.


32-48: Exception handling properly implemented

The IsNewerThan method now includes proper exception handling for malformed version strings, addressing the suggestion from a previous review.


22-22:

Details

✅ Verification successful

Verify C# version compatibility

The code uses collection expressions [V0] which is a C# 12.0 feature. Ensure your build pipeline and all development environments support this language version.


🏁 Script executed:

#!/bin/bash
# Check project file for language version setting
echo "Checking .csproj files for language version:"
find src -name "*.csproj" -exec grep -l "<LangVersion>" {} \; -exec grep -A 1 "<LangVersion>" {} \;

# Check global.json if it exists
echo "Checking global.json for SDK version:"
if [ -f global.json ]; then
    cat global.json
fi

Length of output: 439


C# 12 support confirmed via .NET 9.0 SDK

Project’s global.json pins the SDK at 9.0.0, which includes C# 12, and with no <LangVersion> override the compiler defaults to LatestMajor. The collection expression syntax ([V0]) is therefore supported—no changes required.

mareeben added 2 commits May 13, 2025 13:57
Marked the `MarketDataRepository` field as `readonly` for immutability and consistency. Updated the ID calculation logic to return an empty string instead of throwing an exception when required properties are missing.
Introduced `AssetClass` and added descriptive comments for `Regions` to streamline market data classification. Updated references in `SaveDocumentToDb` to use the new `AssetClass.Fx` constant for better type safety and consistency.
@mareeben mareeben merged commit 3891c9a into master May 13, 2025
1 check passed
@mareeben mareeben deleted the feature/marketDataObject branch May 13, 2025 12:02
@coderabbitai coderabbitai bot mentioned this pull request May 14, 2025
8 tasks
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.

1 participant

Comments