Skip to content

Conversation

@abnegate
Copy link
Member

@abnegate abnegate commented Dec 11, 2025

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added request model support across SDK languages for structured request handling
    • New API endpoints for player creation (single and bulk operations)
  • Chores

    • Updated platform PHP requirements to versions 8.3 and 8.4
    • Enhanced type safety with explicit return type annotations

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

This pull request introduces comprehensive support for request models across the SDK generator ecosystem. Changes include: updating PHP platform requirements to 8.3-8.4; adding a new Player model, validator, and HTTP endpoints in the mock server; enhancing the Spec interface with type hints and a getRequestModels() method; extending all SDK language generators (16+ languages) to resolve model types and generate RequestModel files from templates; introducing request model templates for each language; and updating ~30+ test files to validate the new functionality with new Player creation test cases and expected responses. The feature enables SDKs to handle request-specific model definitions distinctly from response models.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • src/Spec/Spec.php and src/Spec/Swagger2.php: Multiple method signature changes adding return type hints and the new getRequestModels() method; getDefinitions() logic refactored to filter and structure request vs. response models differently—requires verification that existing behavior is preserved and filtering logic is correct.

  • src/SDK/Language/*.php files (16 language generators): Each file adds similar but language-specific type resolution logic in getTypeName for nested models and new file generation entries. Cross-language consistency and correctness of type mappings warrant careful review, particularly the array/model nesting logic.

  • mock-server endpoints and response typing: The new Response.php constant typing (public const string ...) and setModel return type changes may affect subclasses or existing callers—verify no breakage in derived classes.

  • Template files across languages (kotlin, dart, dotnet, go, node, php, python, ruby, swift): Each template implements toMap/ToMap/to_map serialization logic with varying handling of nested schemas, arrays, and enums—verify correctness of property mapping logic, enum value extraction, and null-safety patterns.

  • tests/resources/spec.json: The new player definition and endpoints must align with expected mock-server implementation; verify x-request-model flag usage and required field consistency.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Request model support' directly relates to the primary objective of the pull request, which adds comprehensive request model generation across multiple SDK language targets.
Docstring Coverage ✅ Passed Docstring coverage is 85.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-request-model

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

Caution

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

⚠️ Outside diff range comments (2)
src/SDK/Language/Ruby.php (1)

269-269: Pre-existing typo: 'nill' should be 'nil'.

This is a pre-existing issue not introduced by this PR, but worth noting: Ruby's null value is nil, not nill.

-                    $output .= 'nill';
+                    $output .= 'nil';
src/Spec/Spec.php (1)

166-188: Stale docblock parameter.

Line 168 references @param array $parameter but the method signature has $type as the third parameter, not $parameter.

     /**
      * Set Attribute
      *
      * Method for setting a specific field attribute
      *
      * @param string $key
      * @param mixed $value
-     * @param array $parameter
+     * @param string $type
      * @return mixed
      */
     public function setAttribute(string $key, mixed $value, $type = self::SET_TYPE_ASSIGN): mixed
🧹 Nitpick comments (17)
templates/php/src/Services/Service.php.twig (1)

9-23: Model imports for service methods look good; consider separating enum/model dedupe

The new parameter.model handling correctly adds Models\... imports only once per model and mirrors the existing enum pattern, so generated services will have the right model classes in scope.

Since added is shared between enums and models, if you ever have an enum and a model with the same base name, one of the use statements could be skipped. If that’s a realistic case, consider tracking them separately (e.g., addedEnums vs addedModels) to avoid accidental collisions.

src/SDK/Language/CLI.php (1)

347-366: Improved CLI type resolution for model-backed params

The new branches in getTypeName() for array.model and model nicely ensure model-backed parameters resolve to a concrete PascalCase type (with [] for arrays) instead of falling through to generic mappings—this should make generated CLI types much more accurate.

Minor nit: the docblock still refers to the second parameter as $nestedTypes, but the signature uses $spec. If you touch this again, aligning those names would avoid confusion for readers and tooling.

src/SDK/Language/ReactNative.php (1)

131-150: React Native model types now resolved explicitly

The added checks for array.model and model in getTypeName() correctly map model-backed parameters to Models.<PascalCase> (and [] for arrays), which should give much stronger typings for request models and other modeled inputs without disturbing the existing responseModel-based object handling.

Similar to CLI, the docblock still names the second argument $nestedTypes while the method uses $method; if you revisit this, updating the annotation would keep docs and implementation in sync.

templates/php/src/Models/RequestModel.php.twig (2)

74-74: Simplify the complex ternary logic for better readability.

Line 74 contains deeply nested ternary operators handling sub_schema, arrays, and enums in a single expression. This makes the template difficult to read and maintain.

Consider breaking this into a Twig macro or helper function that handles the conversion logic more explicitly:

'{{ property.name }}' => {{ property | convertToArrayValue }},

Or split the logic across multiple lines using Twig's set/if blocks before the array definition.


84-86: Add error handling for JSON encoding.

The json_encode() call can return false on failure (e.g., for non-UTF-8 strings or circular references), but the method signature promises a string return type.

Consider adding error handling:

 public function toJson(): string
 {
-    return json_encode($this->toArray());
+    $json = json_encode($this->toArray());
+    if ($json === false) {
+        throw new \RuntimeException('Failed to encode model to JSON: ' . json_last_error_msg());
+    }
+    return $json;
 }
tests/languages/ruby/tests.rb (1)

93-96: Optional: Add trailing comma for consistency.

RuboCop suggests adding a trailing comma after the last array item for consistency with Ruby style conventions.

Apply this diff:

 response = general.create_players(players: [
     {id: 'player1', name: 'John Doe', score: 100},
-    {id: 'player2', name: 'Jane Doe', score: 200}
+    {id: 'player2', name: 'Jane Doe', score: 200},
 ])
src/SDK/Language/Swift.php (2)

310-314: Request-model file mapping for Swift looks consistent; double-check path convention

The new requestModel scope entry mirrors the existing definition scope (same ...{{ spec.title | caseUcfirst}}Models/... and swift/Sources/Models/*.twig usage), so it should integrate cleanly into generation.

One thing to verify: other Swift files (e.g., Error.swift, UploadProgress.swift) use /Sources/{{ spec.title | caseUcfirst}}/Models/... with a slash before Models, while both the definition and new requestModel entries use {{ spec.title | caseUcfirst}}Models without the slash. If the intention is Sources/AppwriteModels/..., this is fine; if the desired layout is Sources/Appwrite/Models/..., you may want to adjust both entries together for consistency.


335-341: Model and array-of-model type resolution in getTypeName looks correct; consider minor robustness tweak

The new branches correctly map:

  • array.model[<SpecTitle>Models.<ModelName>]
  • model<SpecTitle>Models.<ModelName> or [<SpecTitle>Models.<ModelName>] when type === TYPE_ARRAY.

To make this a bit more defensive in case $parameter['array'] or $parameter['type'] is missing for some inputs, you could mirror the later TYPE_ARRAY branch and use null-coalescing/local temporaries, e.g.:

$array = $parameter['array'] ?? null;
if (!empty($array['model'])) {
    return '[' . ($spec['title'] ?? '') . 'Models.' . $this->toPascalCase($array['model']) . ']';
}
$model = $parameter['model'] ?? null;
if (!empty($model)) {
    $modelType = ($spec['title'] ?? '') . 'Models.' . $this->toPascalCase($model);
    return (($parameter['type'] ?? null) === self::TYPE_ARRAY) ? '[' . $modelType . ']' : $modelType;
}

This keeps behavior the same for well-formed parameters while reducing the chance of notices if a caller passes an unexpected shape.

src/SDK/SDK.php (1)

707-717: LGTM! Request model generation implemented correctly.

The new requestModel case follows the established pattern used for definition generation. The implementation properly iterates through request models, respects exclusion rules, and renders templates.

Consider whether the setExclude() method (line 765) and exclude() method (line 788) should be extended to support request model exclusions in the future. The current implementation only handles services, methods, and definitions, which may be sufficient for now but could be expanded if selective request model exclusion becomes necessary.

mock-server/src/Utopia/Model/Player.php (1)

16-23: Consider defensive handling for missing array keys.

The fromArray method directly accesses array keys without checking for their existence. If a malformed payload is passed, this will throw an Undefined array key error rather than a more descriptive exception.

 public static function fromArray(array $value): static
 {
+    if (!isset($value['id'], $value['name'], $value['score'])) {
+        throw new \InvalidArgumentException('Missing required keys: id, name, score');
+    }
     return new static(
         id: $value['id'],
         name: $value['name'],
         score: $value['score'],
     );
 }
templates/swift/Sources/Models/RequestModel.swift.twig (1)

36-43: Consider extracting complex mapping logic for readability.

The inline logic on line 39 handles multiple cases (sub_schema arrays, sub_schema objects, enums, plain values) in a single dense line. While functional, this could be challenging to maintain.

Consider breaking out the value mapping into a Twig macro or splitting across multiple lines for clarity:

{%~ for property in requestModel.properties %}
{% set propName = property.name | escapeSwiftKeyword | removeDollarSign %}
{% set optionalChain = property.required ? '' : '?' %}
{% if property.sub_schema %}
{% if property.type == 'array' %}
            "{{ property.name }}": {{ propName }}{{ optionalChain }}.map { $0.toMap() },
{% else %}
            "{{ property.name }}": {{ propName }}{{ optionalChain }}.toMap(),
{% endif %}
{% elseif property.enum %}
            "{{ property.name }}": {{ propName }}{{ optionalChain }}.rawValue,
{% else %}
            "{{ property.name }}": {{ propName }},
{% endif %}
{%~ endfor %}
templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig (1)

1-30: Template duplication with templates/kotlin/.../RequestModel.kt.twig.

This template is identical to templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig. While this may be intentional to maintain separate Android and Kotlin SDK templates, consider whether a shared template could reduce maintenance burden if the logic remains identical.

The template itself is correctly structured for generating Kotlin request model classes.

templates/python/package/models/request_model.py.twig (1)

33-36: Consider moving json import to file level.

The json module import inside to_json() works but is unconventional. Moving it to the file-level imports would be more Pythonic and avoid repeated import overhead if called multiple times.

-from typing import Optional, List, Dict, Any
+from typing import Optional, List, Dict, Any
+import json
 ...
     def to_json(self) -> str:
         """Convert the model to a JSON string."""
-        import json
         return json.dumps(self.to_map())
templates/ruby/lib/container/models/request_model.rb.twig (1)

29-40: Inconsistent use of removeDollarSign filter.

On line 32, removeDollarSign is applied to properties with sub_schema but not to regular properties in the else branch. If the property name can contain a $ character, this inconsistency could cause issues.

Consider applying the filter consistently:

-                    "{{ property.name }}": {% if property.sub_schema %}{% if property.type == 'array' %}@{{ property.name | caseSnake | escapeKeyword | removeDollarSign }}.map { |it| it.to_map }{% else %}@{{property.name | caseSnake | escapeKeyword | removeDollarSign }}.to_map{% endif %}{% else %}@{{property.name | caseSnake | escapeKeyword }}{% endif %}{% if not loop.last %},{% endif %}
+                    "{{ property.name }}": {% if property.sub_schema %}{% if property.type == 'array' %}@{{ property.name | caseSnake | escapeKeyword | removeDollarSign }}.map { |it| it.to_map }{% else %}@{{property.name | caseSnake | escapeKeyword | removeDollarSign }}.to_map{% endif %}{% else %}@{{property.name | caseSnake | escapeKeyword | removeDollarSign }}{% endif %}{% if not loop.last %},{% endif %}
mock-server/src/Utopia/Response.php (2)

32-36: Unused constructor parameter.

The $response parameter is declared but never used in the constructor body. If this is intentional (to satisfy the parent signature without using it), consider adding a comment or suppression annotation to clarify intent.

     public function __construct(SwooleResponse $response)
     {
+        // Intentionally not calling parent constructor or using $response
     }

257-260: Consider using strict null comparison.

Using != for null comparison works but !== is preferred for clarity and to avoid potential type coercion issues.

     public static function hasFilter(): bool
     {
-        return self::$filter != null;
+        return self::$filter !== null;
     }
src/Spec/Swagger2.php (1)

516-559: Significant code duplication with getDefinitions().

getRequestModels() shares ~90% of its logic with getDefinitions(). Consider extracting the common property enrichment logic into a private helper method to reduce duplication and ensure consistent behavior.

Also, the same description type issue exists on line 527:

             $model = [
                 'name' => $key,
                 'properties' => $schema['properties'] ?? [],
-                'description' => $schema['description'] ?? [],
+                'description' => $schema['description'] ?? '',
                 'required' => $schema['required'] ?? [],
                 'additionalProperties' => $schema['additionalProperties'] ?? []
             ];
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac31311 and 55106f4.

⛔ Files ignored due to path filters (1)
  • mock-server/composer.lock is excluded by !**/*.lock
📒 Files selected for processing (89)
  • composer.json (1 hunks)
  • example.php (1 hunks)
  • mock-server/app/http.php (3 hunks)
  • mock-server/composer.json (1 hunks)
  • mock-server/src/Utopia/Model.php (2 hunks)
  • mock-server/src/Utopia/Model/Player.php (1 hunks)
  • mock-server/src/Utopia/Response.php (6 hunks)
  • mock-server/src/Utopia/Validator/Player.php (1 hunks)
  • src/SDK/Language/Android.php (1 hunks)
  • src/SDK/Language/Apple.php (1 hunks)
  • src/SDK/Language/CLI.php (1 hunks)
  • src/SDK/Language/Dart.php (2 hunks)
  • src/SDK/Language/Deno.php (1 hunks)
  • src/SDK/Language/DotNet.php (2 hunks)
  • src/SDK/Language/Flutter.php (1 hunks)
  • src/SDK/Language/Go.php (2 hunks)
  • src/SDK/Language/JS.php (1 hunks)
  • src/SDK/Language/Kotlin.php (2 hunks)
  • src/SDK/Language/Node.php (2 hunks)
  • src/SDK/Language/PHP.php (2 hunks)
  • src/SDK/Language/Python.php (2 hunks)
  • src/SDK/Language/ReactNative.php (1 hunks)
  • src/SDK/Language/Ruby.php (2 hunks)
  • src/SDK/Language/Swift.php (2 hunks)
  • src/SDK/Language/Web.php (1 hunks)
  • src/SDK/SDK.php (2 hunks)
  • src/Spec/Spec.php (5 hunks)
  • src/Spec/Swagger2.php (8 hunks)
  • templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig (1 hunks)
  • templates/dart/lib/models.dart.twig (1 hunks)
  • templates/dart/lib/src/models/request_model.dart.twig (1 hunks)
  • templates/dotnet/Package/Models/RequestModel.cs.twig (1 hunks)
  • templates/go/models/request_model.go.twig (1 hunks)
  • templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig (1 hunks)
  • templates/node/src/models/request_model.ts.twig (1 hunks)
  • templates/php/src/Models/RequestModel.php.twig (1 hunks)
  • templates/php/src/Services/Service.php.twig (1 hunks)
  • templates/python/package/models/request_model.py.twig (1 hunks)
  • templates/ruby/lib/container/models/request_model.rb.twig (1 hunks)
  • templates/swift/Sources/Models/RequestModel.swift.twig (1 hunks)
  • tests/Android14Java11Test.php (1 hunks)
  • tests/Android14Java17Test.php (1 hunks)
  • tests/Android14Java8Test.php (1 hunks)
  • tests/Android5Java17Test.php (1 hunks)
  • tests/AppleSwift56Test.php (1 hunks)
  • tests/Base.php (2 hunks)
  • tests/DartBetaTest.php (1 hunks)
  • tests/DartStableTest.php (1 hunks)
  • tests/Deno1193Test.php (1 hunks)
  • tests/Deno1303Test.php (1 hunks)
  • tests/DotNet60Test.php (1 hunks)
  • tests/DotNet80Test.php (1 hunks)
  • tests/DotNet90Test.php (1 hunks)
  • tests/FlutterBetaTest.php (1 hunks)
  • tests/FlutterStableTest.php (1 hunks)
  • tests/KotlinJava11Test.php (1 hunks)
  • tests/KotlinJava17Test.php (1 hunks)
  • tests/KotlinJava8Test.php (1 hunks)
  • tests/Node16Test.php (1 hunks)
  • tests/Node18Test.php (1 hunks)
  • tests/Node20Test.php (1 hunks)
  • tests/PHP80Test.php (1 hunks)
  • tests/PHP83Test.php (1 hunks)
  • tests/Python310Test.php (1 hunks)
  • tests/Python311Test.php (1 hunks)
  • tests/Python312Test.php (1 hunks)
  • tests/Python313Test.php (1 hunks)
  • tests/Python39Test.php (1 hunks)
  • tests/Ruby27Test.php (1 hunks)
  • tests/Ruby30Test.php (1 hunks)
  • tests/Ruby31Test.php (1 hunks)
  • tests/Swift56Test.php (1 hunks)
  • tests/WebChromiumTest.php (1 hunks)
  • tests/WebNodeTest.php (1 hunks)
  • tests/languages/android/Tests.kt (2 hunks)
  • tests/languages/apple/Tests.swift (1 hunks)
  • tests/languages/dart/tests.dart (1 hunks)
  • tests/languages/deno/tests.ts (1 hunks)
  • tests/languages/dotnet/Tests.cs (1 hunks)
  • tests/languages/flutter/tests.dart (1 hunks)
  • tests/languages/kotlin/Tests.kt (2 hunks)
  • tests/languages/node/test.js (1 hunks)
  • tests/languages/php/test.php (2 hunks)
  • tests/languages/python/tests.py (1 hunks)
  • tests/languages/ruby/tests.rb (1 hunks)
  • tests/languages/swift/Tests.swift (1 hunks)
  • tests/languages/web/index.html (1 hunks)
  • tests/languages/web/node.js (1 hunks)
  • tests/resources/spec.json (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (56)
tests/languages/web/node.js (1)
tests/languages/node/test.js (1)
  • general (28-28)
src/SDK/Language/Dart.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/Android5Java17Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/FlutterStableTest.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Python311Test.php (1)
tests/Base.php (1)
  • Base (17-335)
mock-server/app/http.php (2)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
tests/Deno1303Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/languages/swift/Tests.swift (4)
tests/languages/node/test.js (1)
  • general (28-28)
tests/languages/web/node.js (1)
  • general (11-11)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
tests/DartBetaTest.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/KotlinJava11Test.php (1)
tests/Base.php (1)
  • Base (17-335)
mock-server/src/Utopia/Validator/Player.php (4)
mock-server/src/Utopia/Model.php (2)
  • Model (7-166)
  • getType (67-67)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
src/Spec/Spec.php (1)
  • getDescription (53-53)
src/Spec/Swagger2.php (1)
  • getDescription (20-23)
tests/WebChromiumTest.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Ruby27Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Python313Test.php (1)
tests/Base.php (1)
  • Base (17-335)
mock-server/src/Utopia/Model/Player.php (2)
mock-server/src/Utopia/Model.php (1)
  • Model (7-166)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
tests/Node16Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/languages/apple/Tests.swift (4)
tests/languages/node/test.js (1)
  • general (28-28)
tests/languages/web/node.js (1)
  • general (11-11)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
src/SDK/Language/Web.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/KotlinJava8Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/languages/node/test.js (1)
tests/languages/web/node.js (2)
  • response (4-4)
  • general (11-11)
src/SDK/Language/DotNet.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/PHP80Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/ReactNative.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/languages/deno/tests.ts (2)
tests/languages/node/test.js (2)
  • response (18-18)
  • general (28-28)
tests/languages/web/node.js (2)
  • response (4-4)
  • general (11-11)
tests/Python310Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Android14Java11Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Python312Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/Python.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/WebNodeTest.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Swift56Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Android14Java8Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/CLI.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/Android14Java17Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/Ruby30Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/PHP83Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/Deno.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/AppleSwift56Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/DotNet60Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/Kotlin.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/DartStableTest.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/Node.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
src/SDK/Language/JS.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/languages/dotnet/Tests.cs (4)
tests/languages/node/test.js (1)
  • general (28-28)
tests/languages/web/node.js (1)
  • general (11-11)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
src/SDK/Language/PHP.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
src/SDK/Language/Go.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/languages/kotlin/Tests.kt (3)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
tests/languages/android/Tests.kt (1)
  • writeToFile (315-318)
src/SDK/SDK.php (2)
src/Spec/Spec.php (1)
  • getRequestModels (133-133)
src/Spec/Swagger2.php (1)
  • getRequestModels (516-559)
tests/Ruby31Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/FlutterBetaTest.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/languages/php/test.php (2)
mock-server/src/Utopia/Model/Player.php (1)
  • Player (7-24)
mock-server/src/Utopia/Validator/Player.php (1)
  • Player (8-29)
tests/DotNet80Test.php (1)
tests/Base.php (1)
  • Base (17-335)
tests/KotlinJava17Test.php (1)
tests/Base.php (1)
  • Base (17-335)
src/SDK/Language/Swift.php (1)
src/SDK/Language.php (1)
  • toPascalCase (116-119)
tests/DotNet90Test.php (1)
tests/Base.php (1)
  • Base (17-335)
mock-server/src/Utopia/Response.php (1)
mock-server/src/Utopia/Model.php (2)
  • filter (50-53)
  • Model (7-166)
src/Spec/Spec.php (1)
src/Spec/Swagger2.php (19)
  • getTitle (12-15)
  • getDescription (20-23)
  • getNamespace (28-31)
  • getVersion (36-39)
  • getEndpoint (44-49)
  • getEndpointDocs (54-59)
  • getLicenseName (64-67)
  • getLicenseURL (72-75)
  • getContactName (80-83)
  • getContactURL (88-91)
  • getContactEmail (96-99)
  • getServices (104-136)
  • getMethods (300-339)
  • getTargetNamespace (432-435)
  • getGlobalHeaders (440-457)
  • getDefinitions (459-509)
  • getRequestModels (516-559)
  • getRequestEnums (564-587)
  • getResponseEnums (592-629)
🪛 GitHub Actions: Twig Linting
templates/python/package/models/request_model.py.twig

[error] 6-6: H014 6:3 Found extra blank lines. endfor %}. djlint lint reported a formatting issue.

🪛 RuboCop (1.81.7)
tests/languages/ruby/tests.rb

[convention] 95-95: Put a comma after the last item of a multiline array.

(Style/TrailingCommaInArrayLiteral)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: dotnet (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: swift (server)
  • GitHub Check: flutter (client)
  • GitHub Check: android (client)
  • GitHub Check: apple (client)

use Utopia\MockServer\Utopia\Response;
use Utopia\Swoole\Request;
use Utopia\Swoole\Response as UtopiaSwooleResponse;
use Utopia\Validator\JSON;
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n '\bJSON\b' mock-server/app/http.php

Repository: appwrite/sdk-generator

Length of output: 95


Remove unused import: Utopia\Validator\JSON.

The JSON validator is imported on line 21 but is not used anywhere in this file.

🤖 Prompt for AI Agents
In mock-server/app/http.php around line 21, the file imports
Utopia\Validator\JSON which is unused; remove the unused import line (delete the
"use Utopia\Validator\JSON;" statement) to clean up imports and avoid dead code
warnings, then run a quick linter or PHP syntax check to confirm no references
remain.

Comment on lines 18 to 26
"platform": {
"php": "8.2"
"php": "8.4"
},
"config": {
"allow-plugins": {
"php-http/discovery": true,
"tbachert/spi": true
}
}
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find and examine the root composer.json
find . -maxdepth 2 -name "composer.json" -type f

Repository: appwrite/sdk-generator

Length of output: 109


🏁 Script executed:

# Check root composer.json for PHP version constraint
cat -n composer.json | grep -A 5 -B 5 "php"

Repository: appwrite/sdk-generator

Length of output: 1127


🏁 Script executed:

# Check mock-server composer.json dependencies
cat -n mock-server/composer.json

Repository: appwrite/sdk-generator

Length of output: 873


🏁 Script executed:

# Look for CI configuration files
find . -type f \( -name ".github" -o -name ".gitlab-ci.yml" -o -name ".travis.yml" -o -name "phpunit.xml" \) 2>/dev/null | head -20

Repository: appwrite/sdk-generator

Length of output: 79


🏁 Script executed:

# Search for workflow files in .github
find .github -type f -name "*.yml" -o -name "*.yaml" 2>/dev/null | head -20

Repository: appwrite/sdk-generator

Length of output: 275


🏁 Script executed:

cat -n .github/workflows/tests.yml

Repository: appwrite/sdk-generator

Length of output: 3426


🏁 Script executed:

# Search for references to the plugins in the codebase
rg "php-http/discovery|tbachert/spi" --type json

Repository: appwrite/sdk-generator

Length of output: 1052


Mock-server PHP 8.4 platform constraint: ensure CI/runtime alignment

The mock-server platform is set to PHP 8.4, which is stricter than the root constraint ("php": ">=8.3"). The allowed plugins (php-http/discovery and tbachert/spi) are available as transitive dependencies and won't cause Composer warnings.

However, the CI workflow currently runs tests with PHP 8.3 only (line 15 of .github/workflows/tests.yml), not 8.4. Confirm that your mock-server runtime environment actually uses PHP 8.4, or adjust the platform constraint if testing remains on 8.3.

Comment on lines 118 to +121
/**
* @return string
*/
abstract public function getGlobalHeaders();
abstract public function getGlobalHeaders(): array;
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 | 🟡 Minor

Incorrect docblock: @return string should be @return array.

The docblock on line 119 says @return string but the method signature declares array return type.

     /**
-     * @return string
+     * @return array
      */
     abstract public function getGlobalHeaders(): array;
📝 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
/**
* @return string
*/
abstract public function getGlobalHeaders();
abstract public function getGlobalHeaders(): array;
/**
* @return array
*/
abstract public function getGlobalHeaders(): array;
🤖 Prompt for AI Agents
In src/Spec/Spec.php around lines 118 to 121, the docblock incorrectly states
"@return string" while the abstract method signature declares it returns an
array; update the docblock to "@return array" (or remove the docblock return tag
if you prefer relying on the scalar type) so the PHPDoc matches the method
signature.

Comment on lines +467 to +509
if ($schema['x-request-model'] ?? false) {
continue;
}
$model = [
'name' => $key,
'properties' => $schema['properties'] ?? [],
'description' => $schema['description'] ?? [],
'required' => $schema['required'] ?? [],
'additionalProperties' => $schema['additionalProperties'] ?? []
];
if (isset($sch['properties'])) {
foreach ($sch['properties'] as $name => $def) {
$sch['properties'][$name]['name'] = $name;
$sch['properties'][$name]['description'] = $def['description'];
$sch['properties'][$name]['example'] = $def['x-example'];
$sch['properties'][$name]['required'] = in_array($name, $sch['required']);
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'];
$model['properties'][$name]['example'] = $def['x-example'];
$model['properties'][$name]['required'] = in_array($name, $model['required']);
if (isset($def['items']['$ref'])) {
//nested model
$sch['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
$model['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
}

if (isset($def['items']['x-anyOf'])) {
//nested model
$sch['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']);
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']);
}

if (isset($def['items']['x-oneOf'])) {
//nested model
$sch['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']);
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']);
}

if (isset($def['enum'])) {
// enum property
$sch['properties'][$name]['enum'] = $def['enum'];
$sch['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name);
$sch['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? [];
$model['properties'][$name]['enum'] = $def['enum'];
$model['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name);
$model['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? [];
}
}
}
$list[$key] = $model;
}
return $list;
}
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 | 🟡 Minor

Type mismatch: description default should be a string, not an array.

On line 473, 'description' => $schema['description'] ?? [] uses an empty array as the default, but description is expected to be a string.

             $model = [
                 'name' => $key,
                 'properties' => $schema['properties'] ?? [],
-                'description' => $schema['description'] ?? [],
+                'description' => $schema['description'] ?? '',
                 'required' => $schema['required'] ?? [],
                 'additionalProperties' => $schema['additionalProperties'] ?? []
             ];
📝 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 ($schema['x-request-model'] ?? false) {
continue;
}
$model = [
'name' => $key,
'properties' => $schema['properties'] ?? [],
'description' => $schema['description'] ?? [],
'required' => $schema['required'] ?? [],
'additionalProperties' => $schema['additionalProperties'] ?? []
];
if (isset($sch['properties'])) {
foreach ($sch['properties'] as $name => $def) {
$sch['properties'][$name]['name'] = $name;
$sch['properties'][$name]['description'] = $def['description'];
$sch['properties'][$name]['example'] = $def['x-example'];
$sch['properties'][$name]['required'] = in_array($name, $sch['required']);
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'];
$model['properties'][$name]['example'] = $def['x-example'];
$model['properties'][$name]['required'] = in_array($name, $model['required']);
if (isset($def['items']['$ref'])) {
//nested model
$sch['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
$model['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
}
if (isset($def['items']['x-anyOf'])) {
//nested model
$sch['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']);
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']);
}
if (isset($def['items']['x-oneOf'])) {
//nested model
$sch['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']);
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']);
}
if (isset($def['enum'])) {
// enum property
$sch['properties'][$name]['enum'] = $def['enum'];
$sch['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name);
$sch['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? [];
$model['properties'][$name]['enum'] = $def['enum'];
$model['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name);
$model['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? [];
}
}
}
$list[$key] = $model;
}
return $list;
}
if ($schema['x-request-model'] ?? false) {
continue;
}
$model = [
'name' => $key,
'properties' => $schema['properties'] ?? [],
'description' => $schema['description'] ?? '',
'required' => $schema['required'] ?? [],
'additionalProperties' => $schema['additionalProperties'] ?? []
];
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'];
$model['properties'][$name]['example'] = $def['x-example'];
$model['properties'][$name]['required'] = in_array($name, $model['required']);
if (isset($def['items']['$ref'])) {
//nested model
$model['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
}
if (isset($def['items']['x-anyOf'])) {
//nested model
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']);
}
if (isset($def['items']['x-oneOf'])) {
//nested model
$model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']);
}
if (isset($def['enum'])) {
// enum property
$model['properties'][$name]['enum'] = $def['enum'];
$model['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name);
$model['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? [];
}
}
}
$list[$key] = $model;
}
return $list;
}
🤖 Prompt for AI Agents
In src/Spec/Swagger2.php around lines 467 to 509, the model's 'description'
default is set to an empty array which mismatches the expected string type;
change the default to an empty string (''), and likewise ensure individual
property descriptions use a safe string default when assigning
$model['properties'][$name]['description'] (e.g. use $def['description'] ?? '')
so description fields are always strings.

Comment on lines +477 to +482
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'];
$model['properties'][$name]['example'] = $def['x-example'];
$model['properties'][$name]['required'] = in_array($name, $model['required']);
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 | 🟡 Minor

Potential undefined index access on $def['description'] and $def['x-example'].

In getDefinitions(), lines 480-481 access $def['description'] and $def['x-example'] directly without null coalescing, which could cause notices if these keys are missing. The getRequestModels() method (lines 534-535) correctly uses ?? '' and ?? null.

-                    $model['properties'][$name]['description'] = $def['description'];
-                    $model['properties'][$name]['example'] = $def['x-example'];
+                    $model['properties'][$name]['description'] = $def['description'] ?? '';
+                    $model['properties'][$name]['example'] = $def['x-example'] ?? null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'];
$model['properties'][$name]['example'] = $def['x-example'];
$model['properties'][$name]['required'] = in_array($name, $model['required']);
if (isset($model['properties'])) {
foreach ($model['properties'] as $name => $def) {
$model['properties'][$name]['name'] = $name;
$model['properties'][$name]['description'] = $def['description'] ?? '';
$model['properties'][$name]['example'] = $def['x-example'] ?? null;
$model['properties'][$name]['required'] = in_array($name, $model['required']);
🤖 Prompt for AI Agents
In src/Spec/Swagger2.php around lines 477-482, the code accesses
$def['description'] and $def['x-example'] directly which can raise undefined
index notices; change those accesses to use null coalescing (e.g.
$def['description'] ?? '' and $def['x-example'] ?? null) and also guard the
required check by ensuring $model['required'] exists before in_array (e.g. use
isset($model['required']) && in_array(...)) so missing keys get safe defaults
like in getRequestModels().

Comment on lines +34 to +43
public Dictionary<string, object?> ToMap()
{
return new Dictionary<string, object?>
{
{%~ for property in requestModel.properties %}
{ "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ property.name | caseUcfirst | overrideIdentifier }}?.ConvertAll(p => p.ToMap()){% else %}{{ property.name | caseUcfirst | overrideIdentifier }}?.ToMap(){% endif %}{% elseif property.enum %}{{ property.name | caseUcfirst | overrideIdentifier }}{% if not property.required %}?{% endif %}.Value{% else %}{{ property.name | caseUcfirst | overrideIdentifier }}{% endif %} }{% if not loop.last %},{% endif %}

{%~ endfor %}
};
}
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the repository structure
git ls-files | grep -E "(filter|twig|dotnet)" | head -20

Repository: appwrite/sdk-generator

Length of output: 1309


🏁 Script executed:

# Search for propertyType filter definition
rg -n "propertyType" --type-add 'php:*.php' --type php | head -30

Repository: appwrite/sdk-generator

Length of output: 501


🏁 Script executed:

# Look for ConvertAll usages in templates
rg -rn "ConvertAll" --type-add 'twig:*.twig' --type twig

Repository: appwrite/sdk-generator

Length of output: 617


🏁 Script executed:

# Search for how arrays are defined in the dotnet templates
rg -rn "List<" templates/dotnet/ --type-add 'twig:*.twig' --type twig | head -20

Repository: appwrite/sdk-generator

Length of output: 2601


🏁 Script executed:

# Check for any propertyType usage in dotnet templates
rg -rn "propertyType" templates/dotnet/ --type-add 'twig:*.twig' --type twig | head -30

Repository: appwrite/sdk-generator

Length of output: 548


🏁 Script executed:

# Find the DotNet language class definition
find . -name "*.php" | xargs grep -l "class.*Dotnet\|class.*DotNet\|class.*NetSDK" | head -10

Repository: appwrite/sdk-generator

Length of output: 193


🏁 Script executed:

# Search for propertyType filter in DotNet language
rg -n "propertyType" src/SDK/Language/*.php | grep -i dotnet

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Get the full DotNet language class file
find . -path "*/Language/Dotnet.php" -o -path "*/Language/DotNet.php" -o -path "*/Language/Csharp.php"

Repository: appwrite/sdk-generator

Length of output: 95


🏁 Script executed:

# Look at RequestModel.cs.twig content more carefully
cat -n templates/dotnet/Package/Models/RequestModel.cs.twig | head -50

Repository: appwrite/sdk-generator

Length of output: 2613


🏁 Script executed:

# Search for how the "n" filter is defined in DotNet context
rg -B5 -A10 "new TwigFilter.*'n'" src/SDK/Language/ | grep -A10 -B5 "Dotnet\|DotNet\|Csharp"

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Read the DotNet.php language class to find propertyType filter
cat -n src/SDK/Language/DotNet.php | head -100

Repository: appwrite/sdk-generator

Length of output: 2745


🏁 Script executed:

# Search for propertyType filter definition in DotNet.php
rg -n "propertyType" src/SDK/Language/DotNet.php -A 20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Get full length of DotNet.php
wc -l src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 97


🏁 Script executed:

# Search for propertyType in the entire file
rg -n "propertyType" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look for filter definitions in DotNet.php
rg -n "new TwigFilter" src/SDK/Language/DotNet.php | head -20

Repository: appwrite/sdk-generator

Length of output: 302


🏁 Script executed:

# Check the parent Language class for propertyType filter
rg -n "propertyType" src/SDK/Language.php -A 20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Also check if DotNet extends and overrides from Language
rg -n "class DotNet extends" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 97


🏁 Script executed:

# Broader search for propertyType in all PHP files
rg -n "propertyType" src/ --type php

Repository: appwrite/sdk-generator

Length of output: 501


🏁 Script executed:

# Search for how filters are registered/added in general
rg -n "TwigFilter.*property" src/SDK/Language/ --type php -A 5

Repository: appwrite/sdk-generator

Length of output: 4295


🏁 Script executed:

# Check if propertyType exists anywhere in Language.php
wc -l src/SDK/Language.php

Repository: appwrite/sdk-generator

Length of output: 90


🏁 Script executed:

# Read Language.php to look for propertyType
cat src/SDK/Language.php | grep -n "propertyType"

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check test files to see if they work
rg -n "propertyType" tests/ --type php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look at DotNet-specific tests
cat -n tests/DotNet60Test.php | head -50

Repository: appwrite/sdk-generator

Length of output: 1615


🏁 Script executed:

# Check how filters are registered - look for getFilters or addFilters
rg -n "getFilters|addFilters|registerFilters" src/SDK/Language.php -A 5

Repository: appwrite/sdk-generator

Length of output: 169


🏁 Script executed:

# Look at full filter registration in DotNet.php
rg -n "return \[" src/SDK/Language/DotNet.php -B 5 -A 30 | head -100

Repository: appwrite/sdk-generator

Length of output: 2873


🏁 Script executed:

# Check if there's a getPropertyType method in DotNet
rg -n "getPropertyType" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Get the full getTypeName implementation in DotNet.php
rg -n "getTypeName" src/SDK/Language/DotNet.php -A 30

Repository: appwrite/sdk-generator

Length of output: 5289


🏁 Script executed:

# Check if DotNet has a getFilters override
sed -n '480,520p' src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 1628


🏁 Script executed:

# Look for all TwigFilter additions in DotNet.php
rg -n "new TwigFilter" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 302


🏁 Script executed:

# Get the full property_type function from DotNet.php
sed -n '520,560p' src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 1700


🏁 Script executed:

# Check how RequestModel.cs.twig should use property_type
cat -n templates/dotnet/Package/Models/RequestModel.cs.twig | sed -n '15,30p'

Repository: appwrite/sdk-generator

Length of output: 1075


🏁 Script executed:

# Check other DotNet templates to see if they use propertyType or property_type
rg -n "propertyType\|property_type" templates/dotnet/ --type-add 'twig:*.twig' --type twig | head -20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check the sub_schema function more carefully - does it handle sub_schema properly?
sed -n '520,545p' src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 1234


🏁 Script executed:

# Check if propertyType is supposed to be a generic filter in Language.php base class
grep -n "propertyType\|'property'" src/SDK/Language.php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look at other language implementations to see if they have propertyType filter
rg -n "getFilters.*function" src/SDK/Language/ -A 50 | head -100

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Verify that propertyType filter really doesn't exist anywhere
rg -rn "propertyType" src/SDK/Language/ --type php

Repository: appwrite/sdk-generator

Length of output: 456


🏁 Script executed:

# Check when this template was added or modified - look at git log
git log --oneline -20 -- templates/dotnet/Package/Models/RequestModel.cs.twig

Repository: appwrite/sdk-generator

Length of output: 96


🏁 Script executed:

# Check if propertyType and 'n' are the same or different in other languages
rg -n "TwigFilter\('propertyType'|'n'" src/SDK/Language/Swift.php -A 3

Repository: appwrite/sdk-generator

Length of output: 370


🏁 Script executed:

# Check the full git commit to understand what was added
git show --stat 55106f4c | head -40

Repository: appwrite/sdk-generator

Length of output: 2343


🏁 Script executed:

# Check if DotNet has 'n' filter
rg -n "TwigFilter\('n'" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check what getPropertyType does in Swift - this is what DotNet needs to implement
rg -n "getPropertyType" src/SDK/Language/Swift.php -A 30 | head -50

Repository: appwrite/sdk-generator

Length of output: 2096


🏁 Script executed:

# Verify DotNet doesn't have getPropertyType method
rg -n "getPropertyType" src/SDK/Language/DotNet.php

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Double-check: is propertyType filter used in any DotNet template?
rg -rn "propertyType" templates/dotnet/ --type-add 'twig:*.twig' --type twig

Repository: appwrite/sdk-generator

Length of output: 548


🏁 Script executed:

# Look at the actual git diff for the commit to see what changed
git show 55106f4c -- templates/dotnet/Package/Models/RequestModel.cs.twig | head -100

Repository: appwrite/sdk-generator

Length of output: 2726


🏁 Script executed:

# Check if 'n' filter exists in DotNet now
rg -n "TwigFilter\('n'" src/SDK/Language/DotNet.php -A 5

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check what languages have the 'n' filter
rg -rn "TwigFilter\('n'" src/SDK/Language/ -A 3

Repository: appwrite/sdk-generator

Length of output: 48


Add the missing propertyType filter to the DotNet language class.

The DotNet language implementation does not define a propertyType filter, but the RequestModel.cs.twig template uses it on lines 18 and 24. This will cause a Twig compilation error. Other languages (Swift, Kotlin, Go) implement propertyType by delegating to a getPropertyType() method. For DotNet, implement this filter following the same pattern and ensure it returns List<T> (not T[]) for array properties, so that ConvertAll() on line 39 works correctly.

Comment on lines +22 to +27
export function {{ requestModel.name | caseCamel }}ToMap(model: {{ requestModel.name | caseUcfirst }}): Record<string, unknown> {
return {
{%~ for property in requestModel.properties %}
'{{ property.name }}': {% if property.sub_schema %}{% if property.type == 'array' %}model.{{ property.name | escapeKeyword }}{% if not property.required %}?{% endif %}.map(item => {{ property.sub_schema | caseCamel }}ToMap(item)){% else %}model.{{ property.name | escapeKeyword }} ? {{ property.sub_schema | caseCamel }}ToMap(model.{{ property.name | escapeKeyword }}) : undefined{% endif %}{% elseif property.enum %}model.{{ property.name | escapeKeyword }}{% else %}model.{{ property.name | escapeKeyword }}{% endif %},
{%~ endfor %}
};
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full request_model.ts.twig template file
cat -n templates/node/src/models/request_model.ts.twig

Repository: appwrite/sdk-generator

Length of output: 1659


🏁 Script executed:

# Search for sub_schema usage in Node templates
rg -n 'sub_schema' templates/node/ -A 2 -B 2

Repository: appwrite/sdk-generator

Length of output: 947


🏁 Script executed:

# Check if sub_schema is used in request model properties across the codebase
rg -n 'sub_schema' templates/ -l | grep -E '(request|model)' | head -20

Repository: appwrite/sdk-generator

Length of output: 753


🏁 Script executed:

# Search for how other languages/templates handle sub_schema imports
rg -n 'sub_schema' templates/ -A 1 -B 1 | head -100

Repository: appwrite/sdk-generator

Length of output: 16290


🏁 Script executed:

# Check how other templates handle sub_schema imports for request models
# Let's look at Ruby request model
head -50 templates/ruby/lib/container/models/request_model.rb.twig

Repository: appwrite/sdk-generator

Length of output: 2316


🏁 Script executed:

# Check Swift request model for import handling
head -50 templates/swift/Sources/Models/RequestModel.swift.twig

Repository: appwrite/sdk-generator

Length of output: 2259


🏁 Script executed:

# Search for examples or specs that show sub_schema in request models
find . -name "*.json" -o -name "*.yaml" -o -name "*.yml" | xargs grep -l "requestModel" 2>/dev/null | head -5

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look at the general model template to see how it handles sub_schema imports
head -80 templates/node/src/models/model.ts.twig

Repository: appwrite/sdk-generator

Length of output: 164


🏁 Script executed:

# Search for test files or examples showing request models with sub_schema
find . -path ./node_modules -prune -o -type f \( -name "*.test.ts" -o -name "*.spec.ts" -o -name "*_test.ts" \) -print | head -10

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look for any configuration or spec files that define request models
find . -name "*.json" -path "*/spec*" -o -name "*.yaml" -path "*/spec*" | head -10

Repository: appwrite/sdk-generator

Length of output: 93


🏁 Script executed:

# Search for how requestModel properties are defined in the codebase
rg -l "requestModel\.properties" --type ts --type js | head -5

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check if sub_schema is ever set in requestModel context
rg "requestModel" templates/ -A 5 -B 5 | grep -E "(sub_schema|requestModel)" | head -30

Repository: appwrite/sdk-generator

Length of output: 4780


🏁 Script executed:

# Check if any request model properties in spec.json have sub_schema
python3 << 'EOF'
import json

with open('./tests/resources/spec.json', 'r') as f:
    spec = json.load(f)

# Look for requestModels and their properties with sub_schema
if 'requestModels' in spec:
    for model_name, model in spec['requestModels'].items():
        if 'properties' in model:
            for prop_name, prop in model['properties'].items():
                if 'x-schema' in prop or 'sub_schema' in prop:
                    print(f"Found sub_schema in {model_name}.{prop_name}: {prop}")
                elif prop.get('type') == 'object' and 'x-schema' not in prop:
                    print(f"Found object type in {model_name}.{prop_name}: {prop}")
else:
    print("No requestModels found in spec")
    print("Available keys:", list(spec.keys())[:20])
EOF

Repository: appwrite/sdk-generator

Length of output: 152


🏁 Script executed:

# Also check the structure of the spec file
jq 'keys' tests/resources/spec.json

Repository: appwrite/sdk-generator

Length of output: 237


🏁 Script executed:

# Search the spec more efficiently for sub_schema
jq '.definitions | to_entries[] | select(.value.properties != null) | .key as $model | .value.properties | to_entries[] | select(.value | has("x-schema") or .value.type == "object") | {model: $model, property: .key, value: .value}' tests/resources/spec.json 2>/dev/null | head -100

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# More direct approach: search for x-schema in the entire spec
grep -o '"x-schema":"[^"]*"' tests/resources/spec.json | head -20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check a specific definition structure to understand the pattern
jq '.definitions | keys | .[0:5]' tests/resources/spec.json

Repository: appwrite/sdk-generator

Length of output: 110


🏁 Script executed:

# Let's examine the actual definitions structure
jq '.definitions.player | .properties | keys' tests/resources/spec.json 2>/dev/null

Repository: appwrite/sdk-generator

Length of output: 97


🏁 Script executed:

# Check the generator code to understand when sub_schema is set
rg -n "sub_schema" --type ts --type js --type py -B 3 -A 3 | head -80

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look for where properties get their sub_schema field
rg -n "\.sub_schema\s*=" --type ts --type js --type py | head -20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Broad search for sub_schema across entire codebase
rg -n "sub_schema" . --type-list | head -20

Repository: appwrite/sdk-generator

Length of output: 455


🏁 Script executed:

# Check if sub_schema is defined in any filter or function
rg -n "sub_schema" . -A 2 -B 2 | grep -E "(filter|function|def|const|let|var)" | head -30

Repository: appwrite/sdk-generator

Length of output: 6540


🏁 Script executed:

# Look for where property objects are constructed/enriched
rg -n "property\[" . --type ts --type js | head -20

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Search for where properties get enriched with sub_schema
rg -n "properties\[" . --type ts --type js -A 5 | head -40

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Check if there's any code that sets or processes sub_schema
find . -name "*.ts" -o -name "*.js" | xargs grep -l "sub_schema" | grep -v node_modules | grep -v templates

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Look for getPropertyType or similar filters that might set sub_schema
rg -n "getPropertyType|propertyType" . --type ts --type js -A 5 | head -50

Repository: appwrite/sdk-generator

Length of output: 48


🏁 Script executed:

# Search for any documentation or comments about sub_schema
rg -n "sub_schema" . -B 2 -A 2 | grep -E "(#|//|comment|doc|TODO|FIXME)" | head -20

Repository: appwrite/sdk-generator

Length of output: 2568


🏁 Script executed:

# Check if there are any request models in the spec with nested/object properties
jq '.definitions | to_entries[] | select(.value.properties) | {model: .key, properties: (.value.properties | keys)}' tests/resources/spec.json

Repository: appwrite/sdk-generator

Length of output: 317


🏁 Script executed:

# Look at the entire player definition to understand structure
jq '.definitions.player' tests/resources/spec.json

Repository: appwrite/sdk-generator

Length of output: 548


🏁 Script executed:

# Look at the full context where sub_schema is set
sed -n '480,550p' src/Spec/Swagger2.php

Repository: appwrite/sdk-generator

Length of output: 3662


🏁 Script executed:

# Check if request models can have array properties with $ref
rg -n "requestModel|x-request-model" tests/resources/spec.json -B 5 -A 10 | head -50

Repository: appwrite/sdk-generator

Length of output: 534


🏁 Script executed:

# Search for any models with nested array references to understand the pattern
jq '.definitions | to_entries[] | select(.value.properties[] | .items | select(. != null) | select(has("$ref") or has("x-anyOf") or has("x-oneOf"))) | .key' tests/resources/spec.json

Repository: appwrite/sdk-generator

Length of output: 144


🏁 Script executed:

# Check how Ruby/PHP/Swift handle the import for sub_schema in request models
head -20 templates/ruby/lib/container/models/request_model.rb.twig

Repository: appwrite/sdk-generator

Length of output: 862


🏁 Script executed:

head -20 templates/php/src/Models/RequestModel.php.twig

Repository: appwrite/sdk-generator

Length of output: 555


🏁 Script executed:

# Check if there's any template that DOES import sub_schema for request models
rg -n "import.*sub_schema" templates/

Repository: appwrite/sdk-generator

Length of output: 48


Missing imports for nested request model ToMap functions.

When a property has sub_schema, the generated code calls {{ property.sub_schema | caseCamel }}ToMap(item), but no corresponding import statement is generated for nested request models' ToMap functions.

If sub_schema refers to another request model (e.g., Player), the generated file would require an import like:

import { playerToMap } from './player';

This occurs when a request model has an array property with items referencing another model definition. The import section currently only generates enum imports (lines 1-5), leaving calls to nested model ToMap functions without their required imports.

Comment on lines +1 to +7
from typing import Optional, List, Dict, Any
{%~ for property in requestModel.properties %}
{%~ if property.enum %}
from ..enums.{{ property.enumName | caseSnake }} import {{ property.enumName | caseUcfirst }}
{%~ endif %}
{%~ endfor %}

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 | 🟡 Minor

Fix pipeline failure: extra blank lines after import loop.

The pipeline reports a formatting issue (H014) at line 6. The template has extra blank lines between the enum import block and the class definition.

Consider adjusting the template to reduce blank lines:

 from typing import Optional, List, Dict, Any
 {%~ for property in requestModel.properties %}
 {%~ if property.enum %}
 from ..enums.{{ property.enumName | caseSnake }} import {{ property.enumName | caseUcfirst }}
 {%~ endif %}
 {%~ endfor %}
-
-
+
 class {{ requestModel.name | caseUcfirst }}:

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

🧰 Tools
🪛 GitHub Actions: Twig Linting

[error] 6-6: H014 6:3 Found extra blank lines. endfor %}. djlint lint reported a formatting issue.

🤖 Prompt for AI Agents
In templates/python/package/models/request_model.py.twig lines 1-7 there are
extra blank lines emitted after the enum import loop which cause H014; update
the template so the loop does not produce an empty newline when no enums are
present and ensure only a single newline separates the imports from the class
definition (remove the extra blank line(s) emitted by the loop and adjust the
template's whitespace/newline control so it always outputs exactly one blank
line before the class).

@@ -0,0 +1,66 @@
#frozen_string_literal: true
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 | 🟡 Minor

Fix the frozen_string_literal magic comment syntax.

The magic comment is missing a space after #. Ruby requires # frozen_string_literal: true (with space) for it to be recognized.

-#frozen_string_literal: true
+# frozen_string_literal: true
📝 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
#frozen_string_literal: true
# frozen_string_literal: true
🤖 Prompt for AI Agents
In templates/ruby/lib/container/models/request_model.rb.twig around lines 1 to
1, the frozen_string_literal magic comment is written as
"#frozen_string_literal: true" which isn't recognized by Ruby; update it to
include a space after the hash so it reads "# frozen_string_literal: true"
(i.e., insert a single space between '#' and 'frozen_string_literal') to fix the
syntax.

Comment on lines +55 to +57
unless valid_{{ property.name | caseSnake }}.include?({{ property.name | caseSnake | escapeKeyword }})
raise ArgumentError, "Invalid " + {{ property.name | caseSnake | escapeKeyword }} + ". Must be one of: " + valid_{{ property.name | caseSnake }}.join(', ')
end
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 | 🟡 Minor

TypeError risk when concatenating non-string enum value.

If {{ property.name | caseSnake | escapeKeyword }} is a symbol or non-string type, string concatenation with + will raise a TypeError. Use string interpolation instead.

-                unless valid_{{ property.name | caseSnake }}.include?({{ property.name | caseSnake | escapeKeyword }})
-                    raise ArgumentError, "Invalid " + {{ property.name | caseSnake | escapeKeyword }} + ". Must be one of: " + valid_{{ property.name | caseSnake }}.join(', ')
-                end
+                unless valid_{{ property.name | caseSnake }}.include?({{ property.name | caseSnake | escapeKeyword }})
+                    raise ArgumentError, "Invalid #{{{ property.name | caseSnake | escapeKeyword }}}. Must be one of: #{valid_{{ property.name | caseSnake }}.join(', ')}"
+                end
🤖 Prompt for AI Agents
In templates/ruby/lib/container/models/request_model.rb.twig around lines 55 to
57, the code concatenates a possibly non-string enum value using +, which can
raise a TypeError for symbols or other non-String values; change the raise to
use string interpolation or call .to_s on the variable (e.g. raise
ArgumentError, "Invalid #{...}. Must be one of: #{valid_....join(', ')}") so the
enum value is safely converted to a string and the message remains clear.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants