Skip to content

Conversation

iamngoni
Copy link

@iamngoni iamngoni commented Oct 3, 2025

What does this PR do?

fix: 🐛 Convert integer properties to int, fix model parsing

Fixes #1171 - Type 'String' is not a subtype of type 'int'
Fixes #1150 - Type 'Null' is not a subtype of type 'int'

Test Plan

[x] Ran both Dart & Flutter tests (both passed)

Related PRs and Issues

#1171
#1150

Have you read the Contributing Guidelines on issues?

Yes

Summary by CodeRabbit

  • Bug Fixes
    • Improved deserialization robustness for numeric fields: now accepts both native numbers and numeric strings with proper null handling.
    • Ensured consistent string conversion while respecting nullability.
    • Reduced runtime errors from mismatched types during parsing.
    • Preserved existing behavior for arrays and other non-primitive types.
    • Enhanced compatibility with inconsistent API responses, minimizing data loss and crashes during model parsing.

Copy link
Contributor

coderabbitai bot commented Oct 3, 2025

Walkthrough

The Dart model template at templates/dart/lib/src/models/model.dart.twig updates fromMap parsing for non-array fields. Integers now use guarded parsing: accept int directly or parse from String, with null handling. Numbers (doubles) now accept double directly or parse from String, with null handling. Strings now use explicit toString() with existing nullability behavior. Other types remain direct map access. Arrays continue using List.from with existing null fallback. No changes to constructors or toMap signatures.

Possibly related PRs

Suggested reviewers

  • ChiragAgg5k
  • abnegate

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly identifies the primary change—fixing integer property conversion and model parsing—and aligns with the pull request objectives and code modifications.
Linked Issues Check ✅ Passed The enhanced fromMap logic for integer properties now guards against string and null values, directly addressing the string-to-int error in issue #1171 and the null-to-int error in issue #1150 by parsing appropriately or returning null when allowed.
Out of Scope Changes Check ✅ Passed All modifications are confined to the model parsing template and focus on type-specific parsing enhancements, which directly support the stated objective of fixing model parsing without introducing unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link
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

🧹 Nitpick comments (2)
templates/dart/lib/src/models/model.dart.twig (2)

48-49: Improve number parsing with error handling and int-to-double conversion.

The double parsing has similar issues to the integer case:

  1. double.parse() can throw: invalid strings or null in required fields cause FormatException
  2. Inefficient int handling: int values (e.g., 42) fail the is double check and go through toString() + parse() instead of direct .toDouble() conversion

Consider this improved version:

-                    {%- elseif property.type == "number" -%}
-                        (map['{{property.name | escapeDollarSign }}'] is double ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}double.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? double.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
+                    {%- elseif property.type == "number" -%}
+                        (map['{{property.name | escapeDollarSign }}'] is double 
+                            ? map['{{property.name | escapeDollarSign }}'] 
+                            : map['{{property.name | escapeDollarSign }}'] is int
+                                ? (map['{{property.name | escapeDollarSign }}'] as int).toDouble()
+                                : {% if property.required %}(double.tryParse(map['{{property.name | escapeDollarSign }}']?.toString() ?? '') ?? 0.0){% else %}(map['{{property.name | escapeDollarSign }}'] != null ? double.tryParse(map['{{property.name | escapeDollarSign }}'].toString()) : null){% endif %})

This approach:

  • Uses tryParse() to avoid exceptions
  • Explicitly handles int-to-double conversion efficiently
  • Provides a default value (0.0) for required fields when parsing fails

52-53: Consider type validation for non-primitive types.

The fallback case (booleans, objects, etc.) uses direct map access without type checking. While this is generally safe for well-formed JSON, consider whether additional type validation would help catch unexpected data:

// Example for boolean validation (if needed)
{% if property.type == "boolean" %}
    (map['{{property.name | escapeDollarSign }}'] is bool 
        ? map['{{property.name | escapeDollarSign }}'] 
        : {% if property.required %}false{% else %}null{% endif %})
{% else %}
    map['{{property.name | escapeDollarSign }}']
{% endif %}

This is optional and depends on how strictly you want to validate server responses.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1ca749 and 6df4bc6.

📒 Files selected for processing (1)
  • templates/dart/lib/src/models/model.dart.twig (1 hunks)
🔇 Additional comments (2)
templates/dart/lib/src/models/model.dart.twig (2)

46-54: Verify type conversions for reported scenarios and finalize error-handling strategy.

  • Add or confirm tests covering:
    1. Issue #1171: Row.fromMap with sequence field as a String
    2. Issue #1150: listDocuments null/integer fields
  • Decide on a consistent parse-failure policy: tryParse with defaults, throw descriptive exceptions, or enable debug-mode logging.

50-51: Add null safety for required string properties.

For required string properties, if the map value is null or the key is missing, calling .toString() on null will throw at runtime.

Consider adding a null check with a fallback:

-                    {%- elseif property.type == "string" -%}
-                        map['{{property.name | escapeDollarSign }}']{% if not property.required %}?{% endif %}.toString()
+                    {%- elseif property.type == "string" -%}
+                        {% if property.required %}(map['{{property.name | escapeDollarSign }}'] ?? '').toString(){% else %}map['{{property.name | escapeDollarSign }}']?.toString(){% endif %}

This ensures required fields have a default empty string if the server omits the field, preventing runtime crashes.

Likely an incorrect or invalid review comment.

Comment on lines +46 to +47
{%- if property.type == "integer" -%}
(map['{{property.name | escapeDollarSign }}'] is int ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? int.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
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 | 🟠 Major

Add error handling for int.parse() failures.

The integer parsing logic handles the String-to-int conversion (issue #1171) and null handling (issue #1150), but int.parse() can throw FormatException in several cases:

  1. Invalid string format: non-numeric strings like "abc" or "1.5" (decimals)
  2. Double/float values: if the server returns 1.5 (a double), toString() produces "1.5", which int.parse() rejects
  3. Required properties with null: if a required field is null, int.parse("null") throws

Consider these options:

Option 1 (recommended): Use int.tryParse() with fallback for required fields:

-                    {%- if property.type == "integer" -%}
-                        (map['{{property.name | escapeDollarSign }}'] is int ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? int.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
+                    {%- if property.type == "integer" -%}
+                        (map['{{property.name | escapeDollarSign }}'] is int 
+                            ? map['{{property.name | escapeDollarSign }}'] 
+                            : {% if property.required %}(int.tryParse(map['{{property.name | escapeDollarSign }}']?.toString() ?? '') ?? 0){% else %}(map['{{property.name | escapeDollarSign }}'] != null ? int.tryParse(map['{{property.name | escapeDollarSign }}'].toString()) : null){% endif %})

Option 2: Handle doubles explicitly by truncating/rounding:

-                    {%- if property.type == "integer" -%}
-                        (map['{{property.name | escapeDollarSign }}'] is int ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? int.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
+                    {%- if property.type == "integer" -%}
+                        (map['{{property.name | escapeDollarSign }}'] is int 
+                            ? map['{{property.name | escapeDollarSign }}'] 
+                            : map['{{property.name | escapeDollarSign }}'] is double
+                                ? (map['{{property.name | escapeDollarSign }}'] as double).toInt()
+                                : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}(map['{{property.name | escapeDollarSign }}'] != null ? int.tryParse(map['{{property.name | escapeDollarSign }}'].toString()) : null){% endif %})
📝 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
{%- if property.type == "integer" -%}
(map['{{property.name | escapeDollarSign }}'] is int ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? int.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
{%- if property.type == "integer" -%}
(map['{{property.name | escapeDollarSign }}'] is int
? map['{{property.name | escapeDollarSign }}']
: {% if property.required %}(int.tryParse(map['{{property.name | escapeDollarSign }}']?.toString() ?? '') ?? 0){% else %}(map['{{property.name | escapeDollarSign }}'] != null ? int.tryParse(map['{{property.name | escapeDollarSign }}'].toString()) : null){% endif %})
Suggested change
{%- if property.type == "integer" -%}
(map['{{property.name | escapeDollarSign }}'] is int ? map['{{property.name | escapeDollarSign }}'] : {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}map['{{property.name | escapeDollarSign }}'] != null ? int.parse(map['{{property.name | escapeDollarSign }}'].toString()) : null{% endif %})
{%- if property.type == "integer" -%}
(map['{{property.name | escapeDollarSign }}'] is int
? map['{{property.name | escapeDollarSign }}']
: map['{{property.name | escapeDollarSign }}'] is double
? (map['{{property.name | escapeDollarSign }}'] as double).toInt()
: {% if property.required %}int.parse(map['{{property.name | escapeDollarSign }}'].toString()){% else %}(map['{{property.name | escapeDollarSign }}'] != null ? int.tryParse(map['{{property.name | escapeDollarSign }}'].toString()) : null){% endif %})

@ChiragAgg5k ChiragAgg5k self-assigned this Oct 4, 2025
@ChiragAgg5k
Copy link
Member

@iamngoni the issue "Type 'String' is not a subtype of type 'int'" has been fixed server side and does not require sdk change, please create new pr for the other issue

@ChiragAgg5k ChiragAgg5k closed this Oct 4, 2025
@iamngoni
Copy link
Author

iamngoni commented Oct 4, 2025

@ChiragAgg5k thank you 🙏. I've created a new PR for the other issue.

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

Labels

None yet

Projects

None yet

2 participants