From 55106f4cd65c54c83fcf27328a1b9e3b6ca8cb7f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 10 Dec 2025 22:19:33 +1300 Subject: [PATCH 1/5] Request model support --- composer.json | 2 +- example.php | 2 +- mock-server/app/http.php | 37 +- mock-server/composer.json | 8 +- mock-server/composer.lock | 2240 ++++++++++++++++- mock-server/src/Utopia/Model.php | 3 +- mock-server/src/Utopia/Model/Player.php | 24 + mock-server/src/Utopia/Response.php | 34 +- mock-server/src/Utopia/Validator/Player.php | 29 + src/SDK/Language/Android.php | 5 + src/SDK/Language/Apple.php | 5 + src/SDK/Language/CLI.php | 7 + src/SDK/Language/Dart.php | 12 + src/SDK/Language/Deno.php | 7 + src/SDK/Language/DotNet.php | 12 + src/SDK/Language/Flutter.php | 5 + src/SDK/Language/Go.php | 12 + src/SDK/Language/JS.php | 7 + src/SDK/Language/Kotlin.php | 12 + src/SDK/Language/Node.php | 12 + src/SDK/Language/PHP.php | 12 + src/SDK/Language/Python.php | 10 + src/SDK/Language/ReactNative.php | 7 + src/SDK/Language/Ruby.php | 11 + src/SDK/Language/Swift.php | 12 + src/SDK/Language/Web.php | 7 + src/SDK/SDK.php | 12 + src/Spec/Spec.php | 60 +- src/Spec/Swagger2.php | 125 +- .../io/package/models/RequestModel.kt.twig | 30 + templates/dart/lib/models.dart.twig | 3 + .../lib/src/models/request_model.dart.twig | 26 + .../Package/Models/RequestModel.cs.twig | 50 + templates/go/models/request_model.go.twig | 26 + .../io/appwrite/models/RequestModel.kt.twig | 30 + .../node/src/models/request_model.ts.twig | 28 + .../php/src/Models/RequestModel.php.twig | 88 + templates/php/src/Services/Service.php.twig | 6 + .../package/models/request_model.py.twig | 39 + .../container/models/request_model.rb.twig | 66 + .../Sources/Models/RequestModel.swift.twig | 45 + tests/Android14Java11Test.php | 1 + tests/Android14Java17Test.php | 1 + tests/Android14Java8Test.php | 1 + tests/Android5Java17Test.php | 1 + tests/AppleSwift56Test.php | 1 + tests/Base.php | 9 + tests/DartBetaTest.php | 1 + tests/DartStableTest.php | 1 + tests/Deno1193Test.php | 1 + tests/Deno1303Test.php | 1 + tests/DotNet60Test.php | 1 + tests/DotNet80Test.php | 1 + tests/DotNet90Test.php | 1 + tests/FlutterBetaTest.php | 1 + tests/FlutterStableTest.php | 1 + tests/KotlinJava11Test.php | 1 + tests/KotlinJava17Test.php | 1 + tests/KotlinJava8Test.php | 1 + tests/Node16Test.php | 1 + tests/Node18Test.php | 1 + tests/Node20Test.php | 1 + tests/PHP80Test.php | 1 + tests/PHP83Test.php | 1 + tests/Python310Test.php | 1 + tests/Python311Test.php | 1 + tests/Python312Test.php | 1 + tests/Python313Test.php | 1 + tests/Python39Test.php | 1 + tests/Ruby27Test.php | 1 + tests/Ruby30Test.php | 1 + tests/Ruby31Test.php | 1 + tests/Swift56Test.php | 1 + tests/WebChromiumTest.php | 1 + tests/WebNodeTest.php | 1 + tests/languages/android/Tests.kt | 11 + tests/languages/apple/Tests.swift | 10 + tests/languages/dart/tests.dart | 10 + tests/languages/deno/tests.ts | 12 + tests/languages/dotnet/Tests.cs | 10 + tests/languages/flutter/tests.dart | 10 + tests/languages/kotlin/Tests.kt | 11 + tests/languages/node/test.js | 10 + tests/languages/php/test.php | 14 + tests/languages/python/tests.py | 10 + tests/languages/ruby/tests.rb | 10 + tests/languages/swift/Tests.swift | 10 + tests/languages/web/index.html | 10 + tests/languages/web/node.js | 10 + tests/resources/spec.json | 180 ++ 90 files changed, 3338 insertions(+), 200 deletions(-) create mode 100644 mock-server/src/Utopia/Model/Player.php create mode 100644 mock-server/src/Utopia/Validator/Player.php create mode 100644 templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig create mode 100644 templates/dart/lib/src/models/request_model.dart.twig create mode 100644 templates/dotnet/Package/Models/RequestModel.cs.twig create mode 100644 templates/go/models/request_model.go.twig create mode 100644 templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig create mode 100644 templates/node/src/models/request_model.ts.twig create mode 100644 templates/php/src/Models/RequestModel.php.twig create mode 100644 templates/python/package/models/request_model.py.twig create mode 100644 templates/ruby/lib/container/models/request_model.rb.twig create mode 100644 templates/swift/Sources/Models/RequestModel.swift.twig diff --git a/composer.json b/composer.json index 868b52cad6..55909df922 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,6 @@ "squizlabs/php_codesniffer": "3.*" }, "platform": { - "php": "8.2" + "php": "8.3" } } diff --git a/example.php b/example.php index fe52a6a195..1950a36573 100644 --- a/example.php +++ b/example.php @@ -51,7 +51,7 @@ function configureSDK($sdk, $overrides = []) { 'gitRepoName' => 'reponame', 'twitter' => 'appwrite', 'discord' => ['564160730845151244', 'https://appwrite.io/discord'], - 'defaultHeaders' => ['X-Appwrite-Response-Format' => '1.6.0'], + 'defaultHeaders' => ['X-Appwrite-Response-Format' => '1.8.0'], 'readme' => '**README**', ]; diff --git a/mock-server/app/http.php b/mock-server/app/http.php index d6e612f281..2f21459f0f 100644 --- a/mock-server/app/http.php +++ b/mock-server/app/http.php @@ -18,6 +18,7 @@ use Utopia\MockServer\Utopia\Response; use Utopia\Swoole\Request; use Utopia\Swoole\Response as UtopiaSwooleResponse; +use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\Integer; use Utopia\Validator\ArrayList; @@ -26,8 +27,9 @@ use Utopia\Validator\WhiteList; use Swoole\Process; use Swoole\Http\Server; +use Utopia\MockServer\Utopia\Model\Player; +use Utopia\MockServer\Utopia\Validator\Player as PlayerValidator; -// Appwrite Init Consts const APP_AUTH_TYPE_SESSION = 'Session'; const APP_AUTH_TYPE_JWT = 'JWT'; const APP_AUTH_TYPE_KEY = 'Key'; @@ -521,6 +523,39 @@ ->action(function (string $mockType) { }); +App::post('/v1/mock/tests/general/models') + ->desc('Create Player') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'createPlayer') + ->label('sdk.description', 'Create a new player using a request model.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) + ->label('sdk.mock', true) + ->param('player', [], new PlayerValidator(), 'Player object.', model: Player::class) + ->action(function (Player $player) { + }); + +App::post('/v1/mock/tests/general/models/array') + ->desc('Create Players') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'createPlayers') + ->label('sdk.description', 'Create multiple players using an array of request models.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) + ->label('sdk.mock', true) + ->param('players', [], new ArrayList(new PlayerValidator(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of player objects.', model: Player::class) + ->action(function (array $players) { + /** @var Player[] $players */ + }); + App::get('/v1/mock/tests/general/400-error') ->desc('400 Error') ->groups(['mock']) diff --git a/mock-server/composer.json b/mock-server/composer.json index 66ee3931c1..c9252629ca 100644 --- a/mock-server/composer.json +++ b/mock-server/composer.json @@ -16,6 +16,12 @@ "swoole/ide-helper": "5.1.2" }, "platform": { - "php": "8.2" + "php": "8.4" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "tbachert/spi": true + } } } diff --git a/mock-server/composer.lock b/mock-server/composer.lock index 9acf97cf3f..d6957f70c3 100644 --- a/mock-server/composer.lock +++ b/mock-server/composer.lock @@ -6,30 +6,212 @@ ], "content-hash": "e8e3df78a113bec48bb61da0227ea50f", "packages": [ + { + "name": "brick/math", + "version": "0.14.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.14.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2025-11-24T14:40:29+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "google/protobuf", + "version": "v4.33.2", + "source": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-php.git", + "reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318", + "reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318", + "shasum": "" + }, + "require": { + "php": ">=8.1.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.0.0 <8.5.27" + }, + "suggest": { + "ext-bcmath": "Need to support JSON deserialization" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Protobuf\\": "src/Google/Protobuf", + "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "proto library for PHP", + "homepage": "https://developers.google.com/protocol-buffers/", + "keywords": [ + "proto" + ], + "support": { + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.2" + }, + "time": "2025-12-05T22:12:22+00:00" + }, { "name": "jean85/pretty-package-versions", - "version": "2.0.5", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", "shasum": "" }, "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", + "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" }, "type": "library", "extra": { @@ -48,125 +230,1755 @@ ], "authors": [ { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", + "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.11.0", + "jean85/pretty-package-versions": "^1.2 || ^2.0.1", + "php": "^7.1 || ^8.0", + "symfony/polyfill-php80": "^1.19" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "squizlabs/php_codesniffer": "^3.6", + "symfony/phpunit-bridge": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + }, + "time": "2021-10-20T22:22:37+00:00" + }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "nyholm/psr7-server", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7-server.git", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc", + "reference": "4335801d851f554ca43fa6e7d2602141538854dc", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "nyholm/nsa": "^1.1", + "nyholm/psr7": "^1.3", + "phpunit/phpunit": "^7.0 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nyholm\\Psr7Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "Helper classes to handle PSR-7 server requests", + "homepage": "http://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7-server/issues", + "source": "https://github.com/Nyholm/psr7-server/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2023-11-08T09:30:43+00:00" + }, + { + "name": "open-telemetry/api", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/api.git", + "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", + "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", + "shasum": "" + }, + "require": { + "open-telemetry/context": "^1.4", + "php": "^8.1", + "psr/log": "^1.1|^2.0|^3.0", + "symfony/polyfill-php82": "^1.26" + }, + "conflict": { + "open-telemetry/sdk": "<=1.0.8" + }, + "type": "library", + "extra": { + "spi": { + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" + ] + }, + "branch-alias": { + "dev-main": "1.8.x-dev" + } + }, + "autoload": { + "files": [ + "Trace/functions.php" + ], + "psr-4": { + "OpenTelemetry\\API\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "API for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "api", + "apm", + "logging", + "opentelemetry", + "otel", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/languages/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-10-19T10:49:48+00:00" + }, + { + "name": "open-telemetry/context", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/context.git", + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf", + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/polyfill-php82": "^1.26" + }, + "suggest": { + "ext-ffi": "To allow context switching in Fibers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "fiber/initialize_fiber_handler.php" + ], + "psr-4": { + "OpenTelemetry\\Context\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "Context implementation for OpenTelemetry PHP.", + "keywords": [ + "Context", + "opentelemetry", + "otel" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-09-19T00:05:49+00:00" + }, + { + "name": "open-telemetry/exporter-otlp", + "version": "1.3.3", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/exporter-otlp.git", + "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/07b02bc71838463f6edcc78d3485c04b48fb263d", + "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d", + "shasum": "" + }, + "require": { + "open-telemetry/api": "^1.0", + "open-telemetry/gen-otlp-protobuf": "^1.1", + "open-telemetry/sdk": "^1.0", + "php": "^8.1", + "php-http/discovery": "^1.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "files": [ + "_register.php" + ], + "psr-4": { + "OpenTelemetry\\Contrib\\Otlp\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "OTLP exporter for OpenTelemetry.", + "keywords": [ + "Metrics", + "exporter", + "gRPC", + "http", + "opentelemetry", + "otel", + "otlp", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/languages/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-11-13T08:04:37+00:00" + }, + { + "name": "open-telemetry/gen-otlp-protobuf", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", + "reference": "673af5b06545b513466081884b47ef15a536edde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/673af5b06545b513466081884b47ef15a536edde", + "reference": "673af5b06545b513466081884b47ef15a536edde", + "shasum": "" + }, + "require": { + "google/protobuf": "^3.22 || ^4.0", + "php": "^8.0" + }, + "suggest": { + "ext-protobuf": "For better performance, when dealing with the protobuf format" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opentelemetry\\Proto\\": "Opentelemetry/Proto/", + "GPBMetadata\\Opentelemetry\\": "GPBMetadata/Opentelemetry/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "PHP protobuf files for communication with OpenTelemetry OTLP collectors/servers.", + "keywords": [ + "Metrics", + "apm", + "gRPC", + "logging", + "opentelemetry", + "otel", + "otlp", + "protobuf", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-09-17T23:10:12+00:00" + }, + { + "name": "open-telemetry/sdk", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/sdk.git", + "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", + "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nyholm/psr7-server": "^1.1", + "open-telemetry/api": "^1.7", + "open-telemetry/context": "^1.4", + "open-telemetry/sem-conv": "^1.0", + "php": "^8.1", + "php-http/discovery": "^1.14", + "psr/http-client": "^1.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", + "psr/http-message": "^1.0.1|^2.0", + "psr/log": "^1.1|^2.0|^3.0", + "ramsey/uuid": "^3.0 || ^4.0", + "symfony/polyfill-mbstring": "^1.23", + "symfony/polyfill-php82": "^1.26", + "tbachert/spi": "^1.0.5" + }, + "suggest": { + "ext-gmp": "To support unlimited number of synchronous metric readers", + "ext-mbstring": "To increase performance of string operations", + "open-telemetry/sdk-configuration": "File-based OpenTelemetry SDK configuration" + }, + "type": "library", + "extra": { + "spi": { + "OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [ + "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig", + "OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig" + ], + "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [ + "OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver" + ], + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [ + "OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager" + ] + }, + "branch-alias": { + "dev-main": "1.9.x-dev" + } + }, + "autoload": { + "files": [ + "Common/Util/functions.php", + "Logs/Exporter/_register.php", + "Metrics/MetricExporter/_register.php", + "Propagation/_register.php", + "Trace/SpanExporter/_register.php", + "Common/Dev/Compatibility/_load.php", + "_autoload.php" + ], + "psr-4": { + "OpenTelemetry\\SDK\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "SDK for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "apm", + "logging", + "opentelemetry", + "otel", + "sdk", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/languages/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-11-25T10:59:15+00:00" + }, + { + "name": "open-telemetry/sem-conv", + "version": "1.37.0", + "source": { + "type": "git", + "url": "https://github.com/opentelemetry-php/sem-conv.git", + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/8da7ec497c881e39afa6657d72586e27efbd29a1", + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenTelemetry\\SemConv\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "description": "Semantic conventions for OpenTelemetry PHP.", + "keywords": [ + "Metrics", + "apm", + "logging", + "opentelemetry", + "otel", + "semantic conventions", + "semconv", + "tracing" + ], + "support": { + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", + "docs": "https://opentelemetry.io/docs/php", + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php" + }, + "time": "2025-09-03T12:08:10+00:00" + }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.1" + }, + "time": "2025-09-04T20:59:21+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/http-client", + "version": "v7.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "26cc224ea7103dda90e9694d9e139a389092d007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/26cc224ea7103dda90e9694d9e139a389092d007", + "reference": "26cc224ea7103dda90e9694d9e139a389092d007", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "amphp/amp": "<2.5", + "amphp/socket": "<1.1", + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-04T21:12:57+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "75d7043853a42837e68111812f4d964b01e5101c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", + "reference": "75d7043853a42837e68111812f4d964b01e5101c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-29T11:18:49+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php82", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "5d2ed36f7734637dacc025f179698031951b1692" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692", + "reference": "5d2ed36f7734637dacc025f179698031951b1692", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A library to get pretty versions strings of installed dependencies", + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "composer", - "package", - "release", - "versions" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.33.0" }, - "time": "2021-10-08T21:21:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "mongodb/mongodb", - "version": "1.10.0", + "name": "symfony/polyfill-php83", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/b0bbd657f84219212487d01a8ffe93a789e1e488", - "reference": "b0bbd657f84219212487d01a8ffe93a789e1e488", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.11.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "php": "^7.1 || ^8.0", - "symfony/polyfill-php80": "^1.19" - }, - "require-dev": { - "doctrine/coding-standard": "^9.0", - "squizlabs/php_codesniffer": "^3.6", - "symfony/phpunit-bridge": "^5.2" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { "files": [ - "src/functions.php" + "bootstrap.php" ], "psr-4": { - "MongoDB\\": "src/" - } + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "database", - "driver", - "mongodb", - "persistence" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/1.10.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, - "time": "2021-10-20T22:22:37+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "name": "symfony/service-contracts", + "version": "v3.6.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Contracts\\Service\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -174,10 +1986,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -187,16 +1995,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -207,25 +2017,81 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-07-15T11:30:57+00:00" + }, + { + "name": "tbachert/spi", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/Nevay/spi.git", + "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nevay/spi/zipball/e7078767866d0a9e0f91d3f9d42a832df5e39002", + "reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "composer/semver": "^1.0 || ^2.0 || ^3.0", + "php": "^8.1" + }, + "require-dev": { + "composer/composer": "^2.0", + "infection/infection": "^0.27.9", + "phpunit/phpunit": "^10.5", + "psalm/phar": "^5.18" + }, + "type": "composer-plugin", + "extra": { + "class": "Nevay\\SPI\\Composer\\Plugin", + "branch-alias": { + "dev-main": "1.0.x-dev" + }, + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Nevay\\SPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Service provider loading facility", + "keywords": [ + "service provider" + ], + "support": { + "issues": "https://github.com/Nevay/spi/issues", + "source": "https://github.com/Nevay/spi/tree/v1.0.5" + }, + "time": "2025-06-29T15:42:06+00:00" }, { "name": "utopia-php/cache", - "version": "0.9.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e" + "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e", - "reference": "4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/552b4c554bb14d0c529631ce304cdf4a2b9d06a6", + "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6", "shasum": "" }, "require": { @@ -260,9 +2126,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.9.0" + "source": "https://github.com/utopia-php/cache/tree/0.9.1" }, - "time": "2024-01-07T18:11:23+00:00" + "time": "2024-03-19T17:07:20+00:00" }, { "name": "utopia-php/cli", @@ -313,18 +2179,64 @@ }, "time": "2023-08-05T13:13:08+00:00" }, + { + "name": "utopia-php/compression", + "version": "0.1.3", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/compression.git", + "reference": "66f093557ba66d98245e562036182016c7dcfe8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/66f093557ba66d98245e562036182016c7dcfe8a", + "reference": "66f093557ba66d98245e562036182016c7dcfe8a", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Compression\\": "src/Compression" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Compression library to handle file compression", + "keywords": [ + "compression", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/compression/issues", + "source": "https://github.com/utopia-php/compression/tree/0.1.3" + }, + "time": "2025-01-15T15:15:51+00:00" + }, { "name": "utopia-php/database", - "version": "0.48.2", + "version": "0.48.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4" + "reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4", - "reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4", + "url": "https://api.github.com/repos/utopia-php/database/zipball/02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0", + "reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0", "shasum": "" }, "require": { @@ -332,7 +2244,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -365,32 +2277,35 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.48.2" + "source": "https://github.com/utopia-php/database/tree/0.48.4" }, - "time": "2024-02-02T14:10:14+00:00" + "time": "2024-02-23T03:22:55+00:00" }, { "name": "utopia-php/framework", - "version": "0.33.2", + "version": "0.33.34", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3" + "reference": "76def92594c32504ec80eaacdb60ff8fad73c856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", + "url": "https://api.github.com/repos/utopia-php/http/zipball/76def92594c32504ec80eaacdb60ff8fad73c856", + "reference": "76def92594c32504ec80eaacdb60ff8fad73c856", "shasum": "" }, "require": { - "php": ">=8.0" + "php": ">=8.3", + "utopia-php/compression": "0.1.*", + "utopia-php/telemetry": "0.1.*", + "utopia-php/validators": "0.1.*" }, "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "laravel/pint": "1.*", + "phpbench/phpbench": "1.*", + "phpstan/phpstan": "1.*", + "phpunit/phpunit": "9.*" }, "type": "library", "autoload": { @@ -410,9 +2325,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.2" + "source": "https://github.com/utopia-php/http/tree/0.33.34" }, - "time": "2024-01-31T10:35:59+00:00" + "time": "2025-12-08T07:55:31+00:00" }, { "name": "utopia-php/mongo", @@ -476,16 +2391,16 @@ }, { "name": "utopia-php/swoole", - "version": "0.8.2", + "version": "0.8.4", "source": { "type": "git", "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" + "reference": "150c30700e738c52348cce9ed0e0f0ff96872081" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/150c30700e738c52348cce9ed0e0f0ff96872081", + "reference": "150c30700e738c52348cce9ed0e0f0ff96872081", "shasum": "" }, "require": { @@ -521,9 +2436,104 @@ ], "support": { "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" + "source": "https://github.com/utopia-php/swoole/tree/0.8.4" + }, + "time": "2025-09-07T09:39:46+00:00" + }, + { + "name": "utopia-php/telemetry", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/telemetry.git", + "reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/437f0021777f0e575dfb9e8a1a081b3aed75e33f", + "reference": "437f0021777f0e575dfb9e8a1a081b3aed75e33f", + "shasum": "" + }, + "require": { + "ext-opentelemetry": "*", + "ext-protobuf": "*", + "nyholm/psr7": "^1.8", + "open-telemetry/exporter-otlp": "^1.1", + "open-telemetry/sdk": "^1.1", + "php": ">=8.0", + "symfony/http-client": "^7.1" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Telemetry\\": "src/Telemetry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "keywords": [ + "framework", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/telemetry/issues", + "source": "https://github.com/utopia-php/telemetry/tree/0.1.1" + }, + "time": "2025-03-17T11:57:52+00:00" + }, + { + "name": "utopia-php/validators", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/validators.git", + "reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/validators/zipball/5c57d5b6cf964f8981807c1d3ea8df620c869080", + "reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.*", + "phpstan/phpstan": "1.*", + "phpunit/phpunit": "11.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A lightweight collection of reusable validators for Utopia projects", + "keywords": [ + "php", + "utopia", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/utopia-php/validators/issues", + "source": "https://github.com/utopia-php/validators/tree/0.1.0" }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2025-11-18T11:05:46+00:00" } ], "packages-dev": [ @@ -562,10 +2572,10 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/mock-server/src/Utopia/Model.php b/mock-server/src/Utopia/Model.php index d725ffb96b..cafdb2205e 100644 --- a/mock-server/src/Utopia/Model.php +++ b/mock-server/src/Utopia/Model.php @@ -44,6 +44,7 @@ abstract class Model /** * Filter Document Structure * + * @param Document $document * @return Document */ public function filter(Document $document): Document @@ -85,7 +86,7 @@ public function getRules(): array */ protected function addRule(string $key, array $options): self { - $this->rules[$key] = array_merge([ + $this->rules[$key] = \array_merge([ 'required' => true, 'array' => false, 'description' => '', diff --git a/mock-server/src/Utopia/Model/Player.php b/mock-server/src/Utopia/Model/Player.php new file mode 100644 index 0000000000..2e29250834 --- /dev/null +++ b/mock-server/src/Utopia/Model/Player.php @@ -0,0 +1,24 @@ +models[$instance->getType()] = $instance; @@ -70,7 +64,7 @@ public function setModel(Model $instance) * * @param string $key * @return Model - * @throws Exception + * @throws \Exception */ public function getModel(string $key): Model { @@ -99,7 +93,7 @@ public function getModels(): array * @param string $model * * return void - * @throws Exception + * @throws \Exception */ public function dynamic(Document $document, string $model): void { @@ -240,7 +234,7 @@ public function getPayload(): array * * @return void */ - public static function setFilter(?Filter $filter) + public static function setFilter(?Filter $filter): void { self::$filter = $filter; } diff --git a/mock-server/src/Utopia/Validator/Player.php b/mock-server/src/Utopia/Validator/Player.php new file mode 100644 index 0000000000..adebe83aa0 --- /dev/null +++ b/mock-server/src/Utopia/Validator/Player.php @@ -0,0 +1,29 @@ + 'library/src/main/java/{{ sdk.namespace | caseSlash }}/models/{{ definition.name | caseUcfirst }}.kt', 'template' => '/android/library/src/main/java/io/package/models/Model.kt.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => 'library/src/main/java/{{ sdk.namespace | caseSlash }}/models/{{ requestModel.name | caseUcfirst }}.kt', + 'template' => '/android/library/src/main/java/io/package/models/RequestModel.kt.twig', + ], [ 'scope' => 'enum', 'destination' => 'library/src/main/java/{{ sdk.namespace | caseSlash }}/enums/{{ enum.name | caseUcfirst }}.kt', diff --git a/src/SDK/Language/Apple.php b/src/SDK/Language/Apple.php index 4313f9d34a..f4b64f291d 100644 --- a/src/SDK/Language/Apple.php +++ b/src/SDK/Language/Apple.php @@ -195,6 +195,11 @@ public function getFiles(): array 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Models/{{ definition.name | caseUcfirst }}.swift', 'template' => '/swift/Sources/Models/Model.swift.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Models/{{ requestModel.name | caseUcfirst }}.swift', + 'template' => '/swift/Sources/Models/RequestModel.swift.twig', + ], [ 'scope' => 'enum', 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Enums/{{ enum.name | caseUcfirst }}.swift', diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index db72e8778b..4ec169d2a9 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -357,6 +357,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/Language/Dart.php b/src/SDK/Language/Dart.php index 6ad8efbaa2..6a9bfbf87b 100644 --- a/src/SDK/Language/Dart.php +++ b/src/SDK/Language/Dart.php @@ -148,6 +148,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return 'enums.' . \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'ListtoPascalCase($parameter['array']['model']) . '>'; + } + if (!empty($parameter['model'])) { + $modelType = 'models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? 'List<' . $modelType . '>' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; @@ -424,6 +431,11 @@ public function getFiles(): array 'destination' => '/lib/src/models/{{definition.name | caseSnake }}.dart', 'template' => 'dart/lib/src/models/model.dart.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/lib/src/models/{{requestModel.name | caseSnake }}.dart', + 'template' => 'dart/lib/src/models/request_model.dart.twig', + ], [ 'scope' => 'method', 'destination' => 'docs/examples/{{service.name | caseLower}}/{{method.name | caseKebab}}.md', diff --git a/src/SDK/Language/Deno.php b/src/SDK/Language/Deno.php index c3145c60de..0d13cbb57c 100644 --- a/src/SDK/Language/Deno.php +++ b/src/SDK/Language/Deno.php @@ -163,6 +163,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { $parameter['array'] = $parameter['items']; } diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index d2d89ce5d3..8241d2a997 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -192,6 +192,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return 'Appwrite.Enums.' . \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'ListtoPascalCase($parameter['array']['model']) . '>'; + } + if (!empty($parameter['model'])) { + $modelType = 'Appwrite.Models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? 'List<' . $modelType . '>' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; @@ -464,6 +471,11 @@ public function getFiles(): array 'destination' => '{{ spec.title | caseUcfirst }}/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}.cs', 'template' => 'dotnet/Package/Models/Model.cs.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '{{ spec.title | caseUcfirst }}/Models/{{ requestModel.name | caseUcfirst | overrideIdentifier }}.cs', + 'template' => 'dotnet/Package/Models/RequestModel.cs.twig', + ], [ 'scope' => 'enum', 'destination' => '{{ spec.title | caseUcfirst }}/Enums/{{ enum.name | caseUcfirst | overrideIdentifier }}.cs', diff --git a/src/SDK/Language/Flutter.php b/src/SDK/Language/Flutter.php index ab0c5ec3a5..aa819f2800 100644 --- a/src/SDK/Language/Flutter.php +++ b/src/SDK/Language/Flutter.php @@ -95,6 +95,11 @@ public function getFiles(): array 'destination' => '/lib/src/models/{{definition.name | caseSnake }}.dart', 'template' => 'dart/lib/src/models/model.dart.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/lib/src/models/{{requestModel.name | caseSnake }}.dart', + 'template' => 'dart/lib/src/models/request_model.dart.twig', + ], [ 'scope' => 'default', 'destination' => 'lib/src/input_file.dart', diff --git a/src/SDK/Language/Go.php b/src/SDK/Language/Go.php index f32a118f80..aefcb459c8 100644 --- a/src/SDK/Language/Go.php +++ b/src/SDK/Language/Go.php @@ -148,6 +148,11 @@ public function getFiles(): array 'destination' => 'models/{{ definition.name | caseSnake }}.go', 'template' => 'go/models/model.go.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => 'models/{{ requestModel.name | caseSnake }}.go', + 'template' => 'go/models/request_model.go.twig', + ], ]; } @@ -161,6 +166,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (str_contains($parameter['description'] ?? '', 'Collection attributes') || str_contains($parameter['description'] ?? '', 'List of attributes')) { return '[]map[string]any'; } + if (!empty($parameter['array']['model'])) { + return '[]' . $this->toPascalCase($parameter['array']['model']); + } + if (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? '[]' . $modelType : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/Language/JS.php b/src/SDK/Language/JS.php index 7b5d678450..4b3500fe5d 100644 --- a/src/SDK/Language/JS.php +++ b/src/SDK/Language/JS.php @@ -131,6 +131,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/Language/Kotlin.php b/src/SDK/Language/Kotlin.php index 0f0abe6093..01b5e5415f 100644 --- a/src/SDK/Language/Kotlin.php +++ b/src/SDK/Language/Kotlin.php @@ -127,6 +127,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return 'io.appwrite.enums.' . \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'ListtoPascalCase($parameter['array']['model']) . '>'; + } + if (!empty($parameter['model'])) { + $modelType = 'io.appwrite.models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? 'List<' . $modelType . '>' : $modelType; + } if (isset($parameter['items'])) { $parameter['array'] = $parameter['items']; } @@ -631,6 +638,11 @@ public function getFiles(): array 'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/models/{{ definition.name | caseUcfirst }}.kt', 'template' => '/kotlin/src/main/kotlin/io/appwrite/models/Model.kt.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/models/{{ requestModel.name | caseUcfirst }}.kt', + 'template' => '/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig', + ], [ 'scope' => 'enum', 'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/enums/{{ enum.name | caseUcfirst }}.kt', diff --git a/src/SDK/Language/Node.php b/src/SDK/Language/Node.php index 31d86b8e03..376e5b5cb1 100644 --- a/src/SDK/Language/Node.php +++ b/src/SDK/Language/Node.php @@ -40,6 +40,13 @@ public function getTypeName(array $parameter, array $method = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'Models.' . $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = 'Models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; @@ -274,6 +281,11 @@ public function getFiles(): array 'destination' => 'src/enums/{{ enum.name | caseKebab }}.ts', 'template' => 'web/src/enums/enum.ts.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => 'src/models/{{ requestModel.name | caseKebab }}.ts', + 'template' => 'node/src/models/request_model.ts.twig', + ], ]; } } diff --git a/src/SDK/Language/PHP.php b/src/SDK/Language/PHP.php index 47e28932ca..38ecc20c9b 100644 --- a/src/SDK/Language/PHP.php +++ b/src/SDK/Language/PHP.php @@ -272,6 +272,11 @@ public function getFiles(): array 'destination' => '/src/{{ spec.title | caseUcfirst}}/Enums/{{ enum.name | caseUcfirst }}.php', 'template' => 'php/src/Enums/Enum.php.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/src/{{ spec.title | caseUcfirst}}/Models/{{ requestModel.name | caseUcfirst }}.php', + 'template' => 'php/src/Models/RequestModel.php.twig', + ], ]; } @@ -288,6 +293,13 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'array'; + } + if (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? 'array' : $modelType; + } return match ($parameter['type']) { self::TYPE_STRING => 'string', self::TYPE_BOOLEAN => 'bool', diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index dd2107c07c..969930d1cd 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -245,6 +245,11 @@ public function getFiles(): array 'destination' => '{{ spec.title | caseSnake}}/enums/__init__.py', 'template' => 'python/package/enums/__init__.py.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '{{ spec.title | caseSnake}}/models/{{ requestModel.name | caseSnake }}.py', + 'template' => 'python/package/models/request_model.py.twig', + ], ]; } @@ -261,6 +266,11 @@ public function getTypeName(array $parameter, array $spec = []): string $typeName = \ucfirst($parameter['enumName']); } elseif (!empty($parameter['enumValues'])) { $typeName = \ucfirst($parameter['name']); + } elseif (!empty($parameter['array']['model'])) { + $typeName = 'List[' . $this->toPascalCase($parameter['array']['model']) . ']'; + } elseif (!empty($parameter['model'])) { + $modelType = $this->toPascalCase($parameter['model']); + $typeName = $parameter['type'] === self::TYPE_ARRAY ? 'List[' . $modelType . ']' : $modelType; } else { switch ($parameter['type'] ?? '') { case self::TYPE_FILE: diff --git a/src/SDK/Language/ReactNative.php b/src/SDK/Language/ReactNative.php index 81748709cb..06c893d107 100644 --- a/src/SDK/Language/ReactNative.php +++ b/src/SDK/Language/ReactNative.php @@ -141,6 +141,13 @@ public function getTypeName(array $parameter, array $method = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'Models.' . $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = 'Models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/Language/Ruby.php b/src/SDK/Language/Ruby.php index 011d831b09..59484d04ab 100644 --- a/src/SDK/Language/Ruby.php +++ b/src/SDK/Language/Ruby.php @@ -202,6 +202,11 @@ public function getFiles(): array 'destination' => '/lib/{{ spec.title | caseDash }}/models/{{ definition.name | caseSnake }}.rb', 'template' => 'ruby/lib/container/models/model.rb.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/lib/{{ spec.title | caseDash }}/models/{{ requestModel.name | caseSnake }}.rb', + 'template' => 'ruby/lib/container/models/request_model.rb.twig', + ], [ 'scope' => 'enum', 'destination' => 'lib/{{ spec.title | caseSnake}}/enums/{{ enum.name | caseSnake }}.rb', @@ -223,6 +228,12 @@ public function getTypeName(array $parameter, array $spec = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'Array'; + } + if (!empty($parameter['model'])) { + return $parameter['type'] === self::TYPE_ARRAY ? 'Array' : $this->toPascalCase($parameter['model']); + } return match ($parameter['type']) { self::TYPE_INTEGER => 'Integer', self::TYPE_NUMBER => 'Float', diff --git a/src/SDK/Language/Swift.php b/src/SDK/Language/Swift.php index 6e36e42514..a5fa4921f1 100644 --- a/src/SDK/Language/Swift.php +++ b/src/SDK/Language/Swift.php @@ -307,6 +307,11 @@ public function getFiles(): array 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Models/{{ definition.name | caseUcfirst }}.swift', 'template' => '/swift/Sources/Models/Model.swift.twig', ], + [ + 'scope' => 'requestModel', + 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Models/{{ requestModel.name | caseUcfirst }}.swift', + 'template' => '/swift/Sources/Models/RequestModel.swift.twig', + ], [ 'scope' => 'enum', 'destination' => '/Sources/{{ spec.title | caseUcfirst}}Enums/{{ enum.name | caseUcfirst }}.swift', @@ -327,6 +332,13 @@ public function getTypeName(array $parameter, array $spec = [], bool $isProperty if (!empty($parameter['enumValues'])) { return ($spec['title'] ?? '') . 'Enums.' . \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return '[' . ($spec['title'] ?? '') . 'Models.' . $this->toPascalCase($parameter['array']['model']) . ']'; + } + if (!empty($parameter['model'])) { + $modelType = ($spec['title'] ?? '') . 'Models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? '[' . $modelType . ']' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/Language/Web.php b/src/SDK/Language/Web.php index 2490f833f9..7a65374d42 100644 --- a/src/SDK/Language/Web.php +++ b/src/SDK/Language/Web.php @@ -212,6 +212,13 @@ public function getTypeName(array $parameter, array $method = []): string if (!empty($parameter['enumValues'])) { return \ucfirst($parameter['name']); } + if (!empty($parameter['array']['model'])) { + return 'Models.' . $this->toPascalCase($parameter['array']['model']) . '[]'; + } + if (!empty($parameter['model'])) { + $modelType = 'Models.' . $this->toPascalCase($parameter['model']); + return $parameter['type'] === self::TYPE_ARRAY ? $modelType . '[]' : $modelType; + } if (isset($parameter['items'])) { // Map definition nested type to parameter nested type $parameter['array'] = $parameter['items']; diff --git a/src/SDK/SDK.php b/src/SDK/SDK.php index 7bea56bab3..a6040056e9 100644 --- a/src/SDK/SDK.php +++ b/src/SDK/SDK.php @@ -640,6 +640,7 @@ public function generate(string $target): void 'responseEnums' => $this->spec->getResponseEnums(), 'allEnums' => $this->spec->getAllEnums(), 'definitions' => $this->spec->getDefinitions(), + 'requestModels' => $this->spec->getRequestModels(), 'global' => [ 'headers' => $this->spec->getGlobalHeaders(), 'defaultHeaders' => $this->defaultHeaders, @@ -703,6 +704,17 @@ public function generate(string $target): void $this->render($template, $destination, $block, $params, $minify); } break; + case 'requestModel': + foreach ($this->spec->getRequestModels() as $key => $requestModel) { + $params['requestModel'] = $requestModel; + + if ($this->exclude($file, $params)) { + continue; + } + + $this->render($template, $destination, $block, $params, $minify); + } + break; case 'method': foreach ($this->getFilteredServices() as $key => $service) { $methods = $this->spec->getMethods($key); diff --git a/src/Spec/Spec.php b/src/Spec/Spec.php index 8466b5a688..14f443818e 100644 --- a/src/Spec/Spec.php +++ b/src/Spec/Spec.php @@ -18,9 +18,12 @@ abstract class Spec extends ArrayObject */ public function __construct($input) { - if (filter_var($input, FILTER_VALIDATE_URL)) { - $data = file_get_contents($input, false, stream_context_create([ - "ssl" => ['verify_peer' => false, 'allow_self_signed' => true] + if (\filter_var($input, FILTER_VALIDATE_URL)) { + $data = \file_get_contents($input, false, \stream_context_create([ + 'ssl' => [ + 'verify_peer' => false, + 'allow_self_signed' => true, + ] ])); if (!$data) { @@ -42,85 +45,92 @@ public function __construct($input) /** * @return string */ - abstract public function getTitle(); + abstract public function getTitle(): string; /** * @return string */ - abstract public function getDescription(); + abstract public function getDescription(): string; /** * @return string */ - abstract public function getNamespace(); + abstract public function getNamespace(): string; /** * @return string */ - abstract public function getVersion(); + abstract public function getVersion(): string; /** * @return string */ - abstract public function getEndpoint(); + abstract public function getEndpoint(): string; /** * @return string */ - abstract public function getEndpointDocs(); + abstract public function getEndpointDocs(): string; /** * @return string */ - abstract public function getLicenseName(); + abstract public function getLicenseName(): string; /** * @return string */ - abstract public function getLicenseURL(); + abstract public function getLicenseURL(): string; /** * @return string */ - abstract public function getContactName(); + abstract public function getContactName(): string; /** * @return string */ - abstract public function getContactURL(); + abstract public function getContactURL(): string; /** * @return string */ - abstract public function getContactEmail(); + abstract public function getContactEmail(): string; /** * @return array */ - abstract public function getServices(); + abstract public function getServices(): array; /** * @param string $service * @return array */ - abstract public function getMethods($service); + abstract public function getMethods($service): array; /** * @param array $method * @param string $service * @return string */ - abstract public function getTargetNamespace(array $method, string $service); + abstract public function getTargetNamespace(array $method, string $service): string; /** * @return string */ - abstract public function getGlobalHeaders(); + abstract public function getGlobalHeaders(): array; /** * @return array */ - abstract public function getDefinitions(); + abstract public function getDefinitions(): array; + + /** + * Get request model definitions + * + * @return array + */ + abstract public function getRequestModels(): array; /** * Get Attribute @@ -131,7 +141,7 @@ abstract public function getDefinitions(); * @param mixed $default * @return mixed */ - public function getAttribute($name, $default = null) + public function getAttribute($name, $default = null): mixed { $name = explode('.', $name); @@ -153,12 +163,12 @@ public function getAttribute($name, $default = null) * * Method for setting a specific field attribute * - * @param string $key - * @param mixed $value + * @param string $key + * @param mixed $value * @param array $parameter * @return mixed */ - public function setAttribute($key, $value, $type = self::SET_TYPE_ASSIGN) + public function setAttribute(string $key, mixed $value, $type = self::SET_TYPE_ASSIGN): mixed { switch ($type) { case self::SET_TYPE_ASSIGN: @@ -182,12 +192,12 @@ public function setAttribute($key, $value, $type = self::SET_TYPE_ASSIGN) * * @return array */ - abstract public function getRequestEnums(); + abstract public function getRequestEnums(): array; /** * Get Response Enums * * @return array */ - abstract public function getResponseEnums(); + abstract public function getResponseEnums(): array; } diff --git a/src/Spec/Swagger2.php b/src/Spec/Swagger2.php index f8d6e13f61..677beafa2c 100644 --- a/src/Spec/Swagger2.php +++ b/src/Spec/Swagger2.php @@ -9,7 +9,7 @@ class Swagger2 extends Spec /** * @return string */ - public function getTitle() + public function getTitle(): string { return $this->getAttribute('info.title', ''); } @@ -17,7 +17,7 @@ public function getTitle() /** * @return string */ - public function getDescription() + public function getDescription(): string { return $this->getAttribute('info.description', ''); } @@ -25,7 +25,7 @@ public function getDescription() /** * @return string */ - public function getNamespace() + public function getNamespace(): string { return $this->getAttribute('info.namespace', ''); } @@ -33,7 +33,7 @@ public function getNamespace() /** * @return string */ - public function getVersion() + public function getVersion(): string { return $this->getAttribute('info.version', ''); } @@ -41,7 +41,7 @@ public function getVersion() /** * @return string */ - public function getEndpoint() + public function getEndpoint(): string { return $this->getAttribute('schemes.0', 'https') . '://' . $this->getAttribute('host', 'example.com') . @@ -51,7 +51,7 @@ public function getEndpoint() /** * @return string */ - public function getEndpointDocs() + public function getEndpointDocs(): string { return $this->getAttribute('schemes.0', 'https') . '://' . $this->getAttribute('x-host-docs', 'example.com') . @@ -61,7 +61,7 @@ public function getEndpointDocs() /** * @return string */ - public function getLicenseName() + public function getLicenseName(): string { return $this->getAttribute('info.license.name', ''); } @@ -69,7 +69,7 @@ public function getLicenseName() /** * @return string */ - public function getLicenseURL() + public function getLicenseURL(): string { return $this->getAttribute('info.license.url', ''); } @@ -77,7 +77,7 @@ public function getLicenseURL() /** * @return string */ - public function getContactName() + public function getContactName(): string { return $this->getAttribute('info.contact.name', ''); } @@ -85,7 +85,7 @@ public function getContactName() /** * @return string */ - public function getContactURL() + public function getContactURL(): string { return $this->getAttribute('info.contact.url', ''); } @@ -93,7 +93,7 @@ public function getContactURL() /** * @return string */ - public function getContactEmail() + public function getContactEmail(): string { return $this->getAttribute('info.contact.email', ''); } @@ -101,7 +101,7 @@ public function getContactEmail() /** * @return array */ - public function getServices() + public function getServices(): array { $list = []; @@ -195,7 +195,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth } if ($output['type'] == 'graphql') { - $output['headers']['x-sdk-graphql'] = "true"; + $output['headers']['x-sdk-graphql'] = 'true'; } if (isset($method['consumes']) && is_array($method['consumes'])) { @@ -259,8 +259,10 @@ protected function parseMethod(string $methodName, string $pathName, array $meth $temp['example'] = $value['x-example'] ?? null; $temp['isUploadID'] = $value['x-upload-id'] ?? false; $temp['nullable'] = $value['x-nullable'] ?? false; + $temp['model'] = $value['x-model'] ?? null; $temp['array'] = [ 'type' => $value['items']['type'] ?? '', + 'model' => isset($value['items']['$ref']) ? str_replace('#/definitions/', '', $value['items']['$ref']) : null, ]; if ($value['type'] === 'object' && is_array($value['default'])) { $value['default'] = (empty($value['default'])) ? new stdClass() : $value['default']; @@ -295,7 +297,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth * @param string $service * @return array */ - public function getMethods($service) + public function getMethods($service): array { $list = []; $paths = $this->getAttribute('paths', []); @@ -435,7 +437,7 @@ public function getTargetNamespace(array $method, string $service): string /** * @return array */ - public function getGlobalHeaders() + public function getGlobalHeaders(): array { $list = []; @@ -454,7 +456,7 @@ public function getGlobalHeaders() return $list; } - public function getDefinitions() + public function getDefinitions(): array { $list = []; $definition = $this->getAttribute('definitions', []); @@ -462,43 +464,96 @@ public function getDefinitions() if ($key == 'any') { continue; } - $sch = [ - "name" => $key, - "properties" => $schema['properties'] ?? [], - "description" => $schema['description'] ?? [], - "required" => $schema['required'] ?? [], - "additionalProperties" => $schema['additionalProperties'] ?? [] + 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; + } + + /** + * Get request model definitions + * + * @return array + */ + public function getRequestModels(): array + { + $list = []; + $definition = $this->getAttribute('definitions', []); + foreach ($definition as $key => $schema) { + 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'] ?? null; + $model['properties'][$name]['required'] = in_array($name, $model['required']); + if (isset($def['items']['$ref'])) { + $model['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']); + } + + if (isset($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'])) { + $model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']); + } + + if (isset($def['enum'])) { + $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] = $sch; + $list[$key] = $model; } return $list; } diff --git a/templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig b/templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig new file mode 100644 index 0000000000..4f651f0fb6 --- /dev/null +++ b/templates/android/library/src/main/java/io/package/models/RequestModel.kt.twig @@ -0,0 +1,30 @@ +package {{ sdk.namespace | caseDot }}.models + +import com.google.gson.annotations.SerializedName +{%~ for property in requestModel.properties %} +{%~ if property.enum %} +import {{ sdk.namespace | caseDot }}.enums.{{ property.enumName | caseUcfirst }} +{%~ endif %} +{%~ endfor %} + +/** + * {{ requestModel.description | replace({"\n": "\n * "}) | raw }} + */ +data class {{ requestModel.name | caseUcfirst }}( + {%~ for property in requestModel.properties %} + /** + * {{ property.description | replace({"\n": "\n * "}) | raw }} + */ + @SerializedName("{{ property.name | escapeKeyword | escapeDollarSign}}") + {% if property.required -%} val + {%- else -%} var + {%- endif %} {{ property.name | escapeKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}? = null{% endif %}, + + {%~ endfor %} +) { + fun toMap(): Map = mapOf( + {%~ for property in requestModel.properties %} + "{{ property.name | escapeDollarSign }}" to {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.map { it.toMap() }{% else %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.toMap(){% endif %}{% elseif property.enum %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.value{% else %}{{property.name | escapeKeyword | removeDollarSign}}{% endif %}, + {%~ endfor %} + ) +} diff --git a/templates/dart/lib/models.dart.twig b/templates/dart/lib/models.dart.twig index 0d1e087d0c..6dd086e5db 100644 --- a/templates/dart/lib/models.dart.twig +++ b/templates/dart/lib/models.dart.twig @@ -8,4 +8,7 @@ import 'enums.dart' as enums; part 'src/models/model.dart'; {% for definition in spec.definitions %} part 'src/models/{{definition.name | caseSnake}}.dart'; +{% endfor %} +{% for requestModel in spec.requestModels %} +part 'src/models/{{requestModel.name | caseSnake}}.dart'; {% endfor %} \ No newline at end of file diff --git a/templates/dart/lib/src/models/request_model.dart.twig b/templates/dart/lib/src/models/request_model.dart.twig new file mode 100644 index 0000000000..e8e865df74 --- /dev/null +++ b/templates/dart/lib/src/models/request_model.dart.twig @@ -0,0 +1,26 @@ +{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier }}{% endif %}{% else %}{% if property.type == 'object' and property.additionalProperties %}Map{% else %}{{property | typeName}}{% endif %}{% endif %}{% endmacro %} +part of '../../models.dart'; + +/// {{ requestModel.description }} +class {{ requestModel.name | caseUcfirst | overrideIdentifier }} implements Model { +{% for property in requestModel.properties %} + /// {{ property.description }} + final {% if not property.required %}{{_self.sub_schema(property)}}? {{ property.name | escapeKeyword }}{% else %}{{_self.sub_schema(property)}} {{ property.name | escapeKeyword }}{% endif %}; + +{% endfor %} + {{ requestModel.name | caseUcfirst | overrideIdentifier}}({% if requestModel.properties | length %}{{ '{' }}{% endif %} + +{% for property in requestModel.properties %} + {% if property.required %}required {% endif %}this.{{ property.name | escapeKeyword }}, +{% endfor %} + {% if requestModel.properties | length %}{{ '}' }}{% endif %}); + + @override + Map toMap() { + return { +{% for property in requestModel.properties %} + "{{ property.name | escapeDollarSign }}": {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeKeyword}}{% if not property.required %}?{% endif %}.map((p) => p.toMap()).toList(){% else %}{{property.name | escapeKeyword}}{% if not property.required %}?{% endif %}.toMap(){% endif %}{% elseif property.enum %}{{property.name | escapeKeyword}}{% if not property.required %}?{% endif %}.value{% else %}{{property.name | escapeKeyword }}{% endif %}, +{% endfor %} + }; + } +} diff --git a/templates/dotnet/Package/Models/RequestModel.cs.twig b/templates/dotnet/Package/Models/RequestModel.cs.twig new file mode 100644 index 0000000000..333320442e --- /dev/null +++ b/templates/dotnet/Package/Models/RequestModel.cs.twig @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace {{ spec.title | caseUcfirst }}.Models +{ + /// + /// {{ requestModel.description }} + /// + public class {{ requestModel.name | caseUcfirst | overrideIdentifier }} + { + {%~ for property in requestModel.properties %} + /// + /// {{ property.description }} + /// + [JsonPropertyName("{{ property.name }}")] + public {{ property | propertyType(spec) | raw }}{% if not property.required %}?{% endif %} {{ property.name | caseUcfirst | overrideIdentifier }} { get; set; } + + {%~ endfor %} + + public {{ requestModel.name | caseUcfirst | overrideIdentifier }}( + {%~ for property in requestModel.properties %} + {{ property | propertyType(spec) | raw }}{% if not property.required %}?{% endif %} {{ property.name | caseCamel | overrideIdentifier }}{% if not property.required %} = null{% endif %}{% if not loop.last %},{% endif %} + + {%~ endfor %} + ) + { + {%~ for property in requestModel.properties %} + {{ property.name | caseUcfirst | overrideIdentifier }} = {{ property.name | caseCamel | overrideIdentifier }}; + {%~ endfor %} + } + + public Dictionary ToMap() + { + return new Dictionary + { + {%~ 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 %} + }; + } + + public string ToJson() + { + return JsonSerializer.Serialize(ToMap()); + } + } +} diff --git a/templates/go/models/request_model.go.twig b/templates/go/models/request_model.go.twig new file mode 100644 index 0000000000..261606238f --- /dev/null +++ b/templates/go/models/request_model.go.twig @@ -0,0 +1,26 @@ +package models + +import ( + "encoding/json" +) + +{{ ((requestModel.description | caseUcfirst) ~ " Request Model") | godocComment }} +type {{ requestModel.name | caseUcfirst }} struct { + {%~ for property in requestModel.properties %} + {{ property.description | godocComment(4) }} + {{ property.name | caseUcfirst | escapeKeyword }} {{ property | propertyType(spec) }}{% if not property.required %} `json:"{{ property.name }},omitempty"`{% else %} `json:"{{ property.name }}"`{% endif %} + + {%~ endfor %} +} + +func (model {{ requestModel.name | caseUcfirst }}) ToMap() map[string]interface{} { + return map[string]interface{}{ + {%~ for property in requestModel.properties %} + "{{ property.name }}": model.{{ property.name | caseUcfirst | escapeKeyword }}, + {%~ endfor %} + } +} + +func (model {{ requestModel.name | caseUcfirst }}) ToJson() ([]byte, error) { + return json.Marshal(model.ToMap()) +} diff --git a/templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig b/templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig new file mode 100644 index 0000000000..4f651f0fb6 --- /dev/null +++ b/templates/kotlin/src/main/kotlin/io/appwrite/models/RequestModel.kt.twig @@ -0,0 +1,30 @@ +package {{ sdk.namespace | caseDot }}.models + +import com.google.gson.annotations.SerializedName +{%~ for property in requestModel.properties %} +{%~ if property.enum %} +import {{ sdk.namespace | caseDot }}.enums.{{ property.enumName | caseUcfirst }} +{%~ endif %} +{%~ endfor %} + +/** + * {{ requestModel.description | replace({"\n": "\n * "}) | raw }} + */ +data class {{ requestModel.name | caseUcfirst }}( + {%~ for property in requestModel.properties %} + /** + * {{ property.description | replace({"\n": "\n * "}) | raw }} + */ + @SerializedName("{{ property.name | escapeKeyword | escapeDollarSign}}") + {% if property.required -%} val + {%- else -%} var + {%- endif %} {{ property.name | escapeKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}? = null{% endif %}, + + {%~ endfor %} +) { + fun toMap(): Map = mapOf( + {%~ for property in requestModel.properties %} + "{{ property.name | escapeDollarSign }}" to {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.map { it.toMap() }{% else %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.toMap(){% endif %}{% elseif property.enum %}{{property.name | escapeKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.value{% else %}{{property.name | escapeKeyword | removeDollarSign}}{% endif %}, + {%~ endfor %} + ) +} diff --git a/templates/node/src/models/request_model.ts.twig b/templates/node/src/models/request_model.ts.twig new file mode 100644 index 0000000000..a762432bf8 --- /dev/null +++ b/templates/node/src/models/request_model.ts.twig @@ -0,0 +1,28 @@ +{%~ for property in requestModel.properties %} +{%~ if property.enum %} +import { {{ property.enumName | caseUcfirst }} } from '../enums/{{ property.enumName | caseKebab }}'; +{%~ endif %} +{%~ endfor %} + +/** + * {{ requestModel.description }} + */ +export interface {{ requestModel.name | caseUcfirst }} { + {%~ for property in requestModel.properties %} + /** + * {{ property.description }} + */ + {{ property.name | escapeKeyword }}{% if not property.required %}?{% endif %}: {{ property | getPropertyType(spec) }}; + {%~ endfor %} +} + +/** + * Helper function to convert {{ requestModel.name | caseUcfirst }} to a plain object + */ +export function {{ requestModel.name | caseCamel }}ToMap(model: {{ requestModel.name | caseUcfirst }}): Record { + 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 %} + }; +} diff --git a/templates/php/src/Models/RequestModel.php.twig b/templates/php/src/Models/RequestModel.php.twig new file mode 100644 index 0000000000..93bd9848a5 --- /dev/null +++ b/templates/php/src/Models/RequestModel.php.twig @@ -0,0 +1,88 @@ +{{ property.name | caseCamel | escapeKeyword }} = ${{ property.name | caseCamel | escapeKeyword }}; + {%~ endfor %} + } + + {%~ for property in requestModel.properties %} + /** + * Get {{ property.description | lower }} + * + * @return {{ property | typeName }}{% if not property.required %}|null{% endif %} + + */ + public function get{{ property.name | caseUcfirst }}(): {% if not property.required %}?{% endif %}{{ property | typeName }} + + { + return $this->{{ property.name | caseCamel | escapeKeyword }}; + } + + /** + * Set {{ property.description | lower }} + * + * @param {{ property | typeName }}{% if not property.required %}|null{% endif %} ${{ property.name | caseCamel | escapeKeyword }} + * @return self + */ + public function set{{ property.name | caseUcfirst }}({% if not property.required %}?{% endif %}{{ property | typeName }} ${{ property.name | caseCamel | escapeKeyword }}): self + { + $this->{{ property.name | caseCamel | escapeKeyword }} = ${{ property.name | caseCamel | escapeKeyword }}; + return $this; + } + + {%~ endfor %} + /** + * Convert the model to an array. + * + * @return array + */ + public function toArray(): array + { + return [ + {%~ for property in requestModel.properties %} + '{{ property.name }}' => {% if property.sub_schema %}{% if property.type == 'array' %}array_map(fn($item) => $item->toArray(), $this->{{ property.name | caseCamel | escapeKeyword }} ?? []){% else %}$this->{{ property.name | caseCamel | escapeKeyword }}?->toArray(){% endif %}{% elseif property.enum %}$this->{{ property.name | caseCamel | escapeKeyword }}{% if not property.required %}?{% endif %}->value{% else %}$this->{{ property.name | caseCamel | escapeKeyword }}{% endif %}, + {%~ endfor %} + ]; + } + + /** + * Convert the model to JSON. + * + * @return string + */ + public function toJson(): string + { + return json_encode($this->toArray()); + } +} diff --git a/templates/php/src/Services/Service.php.twig b/templates/php/src/Services/Service.php.twig index b9b9c13109..de6a72fafa 100644 --- a/templates/php/src/Services/Service.php.twig +++ b/templates/php/src/Services/Service.php.twig @@ -15,6 +15,12 @@ use {{ spec.title | caseUcfirst }}\Enums\{{ parameter.enumName | caseUcfirst }}; {% set added = added|merge([parameter.enumName]) %} {% endif %} {% endif %} +{% if parameter.model is not empty and parameter.type != 'array' %} +{% if parameter.model not in added %} +use {{ spec.title | caseUcfirst }}\Models\{{ parameter.model | caseUcfirst }}; +{% set added = added|merge([parameter.model]) %} +{% endif %} +{% endif %} {% endfor %} {% endfor %} diff --git a/templates/python/package/models/request_model.py.twig b/templates/python/package/models/request_model.py.twig new file mode 100644 index 0000000000..9585deed14 --- /dev/null +++ b/templates/python/package/models/request_model.py.twig @@ -0,0 +1,39 @@ +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 }}: + """ + {{ requestModel.description }} + """ + + def __init__( + self, + {%~ for property in requestModel.properties %} + {{ property.name | caseSnake | escapeKeyword }}: {% if not property.required %}Optional[{% endif %}{{ property | typeName }}{% if not property.required %}] = None{% endif %}{% if not loop.last %},{% endif %} + + {%~ endfor %} + ): + {%~ for property in requestModel.properties %} + self.{{ property.name | caseSnake | escapeKeyword }} = {{ property.name | caseSnake | escapeKeyword }} + {%~ endfor %} + + def to_map(self) -> Dict[str, Any]: + """Convert the model to a dictionary.""" + return { + {%~ for property in requestModel.properties %} + '{{ property.name }}': {% if property.sub_schema %}{% if property.type == 'array' %}[item.to_map() for item in self.{{ property.name | caseSnake | escapeKeyword }}] if self.{{ property.name | caseSnake | escapeKeyword }} else []{% else %}self.{{ property.name | caseSnake | escapeKeyword }}.to_map() if self.{{ property.name | caseSnake | escapeKeyword }} else None{% endif %}{% elseif property.enum %}self.{{ property.name | caseSnake | escapeKeyword }}.value if self.{{ property.name | caseSnake | escapeKeyword }} else None{% else %}self.{{ property.name | caseSnake | escapeKeyword }}{% endif %}, + {%~ endfor %} + } + + def to_json(self) -> str: + """Convert the model to a JSON string.""" + import json + return json.dumps(self.to_map()) + + def __repr__(self) -> str: + return f"{{ requestModel.name | caseUcfirst }}({', '.join(f'{k}={v!r}' for k, v in self.to_map().items())})" diff --git a/templates/ruby/lib/container/models/request_model.rb.twig b/templates/ruby/lib/container/models/request_model.rb.twig new file mode 100644 index 0000000000..f6010d67fc --- /dev/null +++ b/templates/ruby/lib/container/models/request_model.rb.twig @@ -0,0 +1,66 @@ +#frozen_string_literal: true + +module {{ spec.title | caseUcfirst }} + module Models + class {{ requestModel.name | caseUcfirst }} +{% for property in requestModel.properties %} + attr_reader :{{ property.name | caseSnake | escapeKeyword }} +{% endfor %} + + def initialize( +{% for property in requestModel.properties %} + {{ property.name | caseSnake | escapeKeyword }}:{% if not property.required %} nil{% endif %}{% if not loop.last %},{% endif %} + +{% endfor %} + ) +{% for property in requestModel.properties %} +{% if property.enum %} +{% if property.required %} + @{{ property.name | caseSnake | escapeKeyword }} = validate_{{ property.name | caseSnake | escapeKeyword }}({{ property.name | caseSnake | escapeKeyword }}) +{% else %} + @{{ property.name | caseSnake | escapeKeyword }} = {{ property.name | caseSnake | escapeKeyword }}.nil? ? {{ property.name | caseSnake | escapeKeyword }} : validate_{{ property.name | caseSnake | escapeKeyword }}({{ property.name | caseSnake | escapeKeyword }}) +{% endif %} +{% else %} + @{{ property.name | caseSnake | escapeKeyword }} = {{ property.name | caseSnake | escapeKeyword }} +{% endif %} +{% endfor %} + end + + def to_map + { +{% for property in requestModel.properties %} + "{{ 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 %} + +{% endfor %} + } + end + + def to_json(*args) + to_map.to_json(*args) + end +{% if requestModel.properties | filter(p => p.enum) | length > 0 %} + + private + +{% endif %} +{% for property in requestModel.properties %} +{% if property.enum %} + def validate_{{ property.name | caseSnake | escapeKeyword }}({{ property.name | caseSnake | escapeKeyword }}) + valid_{{ property.name | caseSnake }} = [ +{% for value in property.enum %} + {{ spec.title | caseUcfirst }}::Enums::{{ property.enumName | caseUcfirst }}::{{ value | caseUpper }}, +{% endfor %} + ] + + 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 + + {{ property.name | caseSnake | escapeKeyword }} + end + +{% endif %} +{% endfor %} + end + end +end diff --git a/templates/swift/Sources/Models/RequestModel.swift.twig b/templates/swift/Sources/Models/RequestModel.swift.twig new file mode 100644 index 0000000000..3381f223fd --- /dev/null +++ b/templates/swift/Sources/Models/RequestModel.swift.twig @@ -0,0 +1,45 @@ +import Foundation +import JSONCodable +{% if requestModel.properties | filter(p => p.enum) | length > 0 %} +import {{spec.title | caseUcfirst}}Enums +{% endif %} + +/// {{ requestModel.description }} +{% if requestModel.properties | length == 0 %} +open class {{ requestModel.name | caseUcfirst }}: Codable {} +{% else %} +open class {{ requestModel.name | caseUcfirst }}: Codable { + + enum CodingKeys: String, CodingKey { + {%~ for property in requestModel.properties %} + case {{ property.name | escapeSwiftKeyword | removeDollarSign }} = "{{ property.name }}" + {%~ endfor %} + } + + {%~ for property in requestModel.properties %} + /// {{ property.description }} + public {% if property.required %}let{% else %}var{% endif %} {{ property.name | escapeSwiftKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}?{% endif %} + + {%~ endfor %} + + public init( + {%~ for property in requestModel.properties %} + {{ property.name | escapeSwiftKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}? = nil{% endif %}{% if not loop.last %},{% endif %} + + {%~ endfor %} + ) { + {%~ for property in requestModel.properties %} + self.{{ property.name | escapeSwiftKeyword | removeDollarSign }} = {{ property.name | escapeSwiftKeyword | removeDollarSign }} + {%~ endfor %} + } + + public func toMap() -> [String: Any?] { + return [ + {%~ for property in requestModel.properties %} + "{{ property.name }}": {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeSwiftKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.map { $0.toMap() }{% else %}{{property.name | escapeSwiftKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.toMap(){% endif %}{% elseif property.enum %}{{property.name | escapeSwiftKeyword | removeDollarSign}}{% if not property.required %}?{% endif %}.rawValue{% else %}{{property.name | escapeSwiftKeyword | removeDollarSign}}{% endif %}{% if not loop.last %},{% endif %} + + {%~ endfor %} + ] + } +} +{% endif %} diff --git a/tests/Android14Java11Test.php b/tests/Android14Java11Test.php index b6e114f404..66d5e7248a 100644 --- a/tests/Android14Java11Test.php +++ b/tests/Android14Java11Test.php @@ -27,6 +27,7 @@ class Android14Java11Test extends Base ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, // ...Base::COOKIE_RESPONSES, diff --git a/tests/Android14Java17Test.php b/tests/Android14Java17Test.php index aede9c7af1..c842146fbd 100644 --- a/tests/Android14Java17Test.php +++ b/tests/Android14Java17Test.php @@ -26,6 +26,7 @@ class Android14Java17Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, // ...Base::COOKIE_RESPONSES, diff --git a/tests/Android14Java8Test.php b/tests/Android14Java8Test.php index 0355e77b04..4cd92630c5 100644 --- a/tests/Android14Java8Test.php +++ b/tests/Android14Java8Test.php @@ -27,6 +27,7 @@ class Android14Java8Test extends Base ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, // ...Base::COOKIE_RESPONSES, diff --git a/tests/Android5Java17Test.php b/tests/Android5Java17Test.php index 9677e0f00d..eeebbe0ca7 100644 --- a/tests/Android5Java17Test.php +++ b/tests/Android5Java17Test.php @@ -26,6 +26,7 @@ class Android5Java17Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, // ...Base::COOKIE_RESPONSES, diff --git a/tests/AppleSwift56Test.php b/tests/AppleSwift56Test.php index b4a6709f14..b46cd3f841 100644 --- a/tests/AppleSwift56Test.php +++ b/tests/AppleSwift56Test.php @@ -25,6 +25,7 @@ class AppleSwift56Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::COOKIE_RESPONSES, diff --git a/tests/Base.php b/tests/Base.php index 4917f2d9ad..9da3b2fc09 100644 --- a/tests/Base.php +++ b/tests/Base.php @@ -57,6 +57,11 @@ abstract class Base extends TestCase 'POST:/v1/mock/tests/general/enum:passed', ]; + protected const MODEL_RESPONSES = [ + 'POST:/v1/mock/tests/general/models:passed', + 'POST:/v1/mock/tests/general/models/array:passed', + ]; + protected const UPLOAD_RESPONSE = [ 'POST:/v1/mock/tests/general/upload:passed', ]; @@ -68,6 +73,10 @@ abstract class Base extends TestCase 'POST:/v1/mock/tests/general/upload:passed', ]; + protected const LARGE_FILE_RESPONSES = [ + 'POST:/v1/mock/tests/general/upload:passed', + ]; + /** * 'Mock 400 error' -> message * '{"message":"Mock 400 error","code":400}' -> response diff --git a/tests/DartBetaTest.php b/tests/DartBetaTest.php index b9571f32ed..2c160b68cf 100644 --- a/tests/DartBetaTest.php +++ b/tests/DartBetaTest.php @@ -25,6 +25,7 @@ class DartBetaTest extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/DartStableTest.php b/tests/DartStableTest.php index 68cf9bef06..2778ee8770 100644 --- a/tests/DartStableTest.php +++ b/tests/DartStableTest.php @@ -25,6 +25,7 @@ class DartStableTest extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Deno1193Test.php b/tests/Deno1193Test.php index cab7e3090b..a09044a5cb 100644 --- a/tests/Deno1193Test.php +++ b/tests/Deno1193Test.php @@ -21,6 +21,7 @@ class Deno1193Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Deno1303Test.php b/tests/Deno1303Test.php index 05cd38a5e1..cb64189839 100644 --- a/tests/Deno1303Test.php +++ b/tests/Deno1303Test.php @@ -21,6 +21,7 @@ class Deno1303Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index 7abecf6b98..ebab72e825 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -25,6 +25,7 @@ class DotNet60Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/DotNet80Test.php b/tests/DotNet80Test.php index bee14eaec7..10b9f39e1a 100644 --- a/tests/DotNet80Test.php +++ b/tests/DotNet80Test.php @@ -25,6 +25,7 @@ class DotNet80Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/DotNet90Test.php b/tests/DotNet90Test.php index b7ddf06420..90cd296f9b 100644 --- a/tests/DotNet90Test.php +++ b/tests/DotNet90Test.php @@ -25,6 +25,7 @@ class DotNet90Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/FlutterBetaTest.php b/tests/FlutterBetaTest.php index 366f3d1320..9942202300 100644 --- a/tests/FlutterBetaTest.php +++ b/tests/FlutterBetaTest.php @@ -25,6 +25,7 @@ class FlutterBetaTest extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::COOKIE_RESPONSES, diff --git a/tests/FlutterStableTest.php b/tests/FlutterStableTest.php index 62521f7800..8454754837 100644 --- a/tests/FlutterStableTest.php +++ b/tests/FlutterStableTest.php @@ -25,6 +25,7 @@ class FlutterStableTest extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::COOKIE_RESPONSES, diff --git a/tests/KotlinJava11Test.php b/tests/KotlinJava11Test.php index fab40bb4a7..5a2e76ef67 100644 --- a/tests/KotlinJava11Test.php +++ b/tests/KotlinJava11Test.php @@ -26,6 +26,7 @@ class KotlinJava11Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/KotlinJava17Test.php b/tests/KotlinJava17Test.php index ac117f47d4..7339c16b32 100644 --- a/tests/KotlinJava17Test.php +++ b/tests/KotlinJava17Test.php @@ -26,6 +26,7 @@ class KotlinJava17Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/KotlinJava8Test.php b/tests/KotlinJava8Test.php index 41fe724e1d..6243b94ef7 100644 --- a/tests/KotlinJava8Test.php +++ b/tests/KotlinJava8Test.php @@ -26,6 +26,7 @@ class KotlinJava8Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Node16Test.php b/tests/Node16Test.php index aded19b033..eea2928632 100644 --- a/tests/Node16Test.php +++ b/tests/Node16Test.php @@ -29,6 +29,7 @@ class Node16Test extends Base ...Base::UPLOAD_RESPONSES, ...Base::UPLOAD_RESPONSES, // Object params ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Node18Test.php b/tests/Node18Test.php index 33a7b1a73f..830575c23e 100644 --- a/tests/Node18Test.php +++ b/tests/Node18Test.php @@ -29,6 +29,7 @@ class Node18Test extends Base ...Base::UPLOAD_RESPONSES, ...Base::UPLOAD_RESPONSES, // Object params ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Node20Test.php b/tests/Node20Test.php index b2395b0135..f69ca34040 100644 --- a/tests/Node20Test.php +++ b/tests/Node20Test.php @@ -29,6 +29,7 @@ class Node20Test extends Base ...Base::UPLOAD_RESPONSES, ...Base::UPLOAD_RESPONSES, // Object params ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/PHP80Test.php b/tests/PHP80Test.php index bf9cefccd3..69b50f94cf 100644 --- a/tests/PHP80Test.php +++ b/tests/PHP80Test.php @@ -21,6 +21,7 @@ class PHP80Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/PHP83Test.php b/tests/PHP83Test.php index 5cba40ca1d..6e32dd952b 100644 --- a/tests/PHP83Test.php +++ b/tests/PHP83Test.php @@ -21,6 +21,7 @@ class PHP83Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Python310Test.php b/tests/Python310Test.php index 486627d633..bd181de569 100644 --- a/tests/Python310Test.php +++ b/tests/Python310Test.php @@ -25,6 +25,7 @@ class Python310Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Python311Test.php b/tests/Python311Test.php index 68e0849603..71fe5e642b 100644 --- a/tests/Python311Test.php +++ b/tests/Python311Test.php @@ -25,6 +25,7 @@ class Python311Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Python312Test.php b/tests/Python312Test.php index a42d090b83..186b9ed432 100644 --- a/tests/Python312Test.php +++ b/tests/Python312Test.php @@ -25,6 +25,7 @@ class Python312Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Python313Test.php b/tests/Python313Test.php index e752278bbe..a3c0e9316e 100644 --- a/tests/Python313Test.php +++ b/tests/Python313Test.php @@ -25,6 +25,7 @@ class Python313Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Python39Test.php b/tests/Python39Test.php index 36f9dfcaef..879e545e21 100644 --- a/tests/Python39Test.php +++ b/tests/Python39Test.php @@ -25,6 +25,7 @@ class Python39Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Ruby27Test.php b/tests/Ruby27Test.php index 7565fe1418..b9e5a8a4ae 100644 --- a/tests/Ruby27Test.php +++ b/tests/Ruby27Test.php @@ -23,6 +23,7 @@ class Ruby27Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Ruby30Test.php b/tests/Ruby30Test.php index d1b8a94313..e02205c5b8 100644 --- a/tests/Ruby30Test.php +++ b/tests/Ruby30Test.php @@ -23,6 +23,7 @@ class Ruby30Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Ruby31Test.php b/tests/Ruby31Test.php index a8de8c44b2..9dc0be9a97 100644 --- a/tests/Ruby31Test.php +++ b/tests/Ruby31Test.php @@ -23,6 +23,7 @@ class Ruby31Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/Swift56Test.php b/tests/Swift56Test.php index 356c1a0543..697a99d0c3 100644 --- a/tests/Swift56Test.php +++ b/tests/Swift56Test.php @@ -25,6 +25,7 @@ class Swift56Test extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::OAUTH_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/WebChromiumTest.php b/tests/WebChromiumTest.php index 8e609e383e..fb9187f200 100644 --- a/tests/WebChromiumTest.php +++ b/tests/WebChromiumTest.php @@ -30,6 +30,7 @@ class WebChromiumTest extends Base ...Base::UPLOAD_RESPONSES, ...Base::UPLOAD_RESPONSES, // Object params ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/WebNodeTest.php b/tests/WebNodeTest.php index dc8e48275e..d51e45cf3a 100644 --- a/tests/WebNodeTest.php +++ b/tests/WebNodeTest.php @@ -30,6 +30,7 @@ class WebNodeTest extends Base ...Base::GENERAL_RESPONSES, ...Base::UPLOAD_RESPONSES, ...Base::ENUM_RESPONSES, + ...Base::MODEL_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/languages/android/Tests.kt b/tests/languages/android/Tests.kt index aa8a083ef3..0e18a17359 100644 --- a/tests/languages/android/Tests.kt +++ b/tests/languages/android/Tests.kt @@ -16,6 +16,7 @@ import io.appwrite.extensions.toJson import io.appwrite.models.Error import io.appwrite.models.InputFile import io.appwrite.models.Mock +import io.appwrite.models.Player import io.appwrite.services.Bar import io.appwrite.services.Foo import io.appwrite.services.General @@ -150,6 +151,16 @@ class ServiceTest { mock = general.enum(MockType.FIRST) writeToFile(mock.result) + // Request model tests + mock = general.createPlayer(Player(id = "player1", name = "John Doe", score = 100)) + writeToFile(mock.result) + + mock = general.createPlayers(listOf( + Player(id = "player1", name = "John Doe", score = 100), + Player(id = "player2", name = "Jane Doe", score = 200) + )) + writeToFile(mock.result) + try { general.error400() } catch (e: AppwriteException) { diff --git a/tests/languages/apple/Tests.swift b/tests/languages/apple/Tests.swift index ae8b55b921..35e956fa2f 100644 --- a/tests/languages/apple/Tests.swift +++ b/tests/languages/apple/Tests.swift @@ -127,6 +127,16 @@ class Tests: XCTestCase { mock = try await general.xenum(mockType: .first) print(mock.result) + // Request model tests + mock = try await general.createPlayer(player: Player(id: "player1", name: "John Doe", score: 100)) + print(mock.result) + + mock = try await general.createPlayers(players: [ + Player(id: "player1", name: "John Doe", score: 100), + Player(id: "player2", name: "Jane Doe", score: 200) + ]) + print(mock.result) + do { try await general.error400() } catch let error as AppwriteError { diff --git a/tests/languages/dart/tests.dart b/tests/languages/dart/tests.dart index 4bdc5018cf..a8b0e25641 100644 --- a/tests/languages/dart/tests.dart +++ b/tests/languages/dart/tests.dart @@ -88,6 +88,16 @@ void main() async { response = await general.xenum(mockType: MockType.first); print(response.result); + // Request model tests + response = await general.createPlayer(player: Player(id: 'player1', name: 'John Doe', score: 100)); + print(response.result); + + response = await general.createPlayers(players: [ + Player(id: 'player1', name: 'John Doe', score: 100), + Player(id: 'player2', name: 'Jane Doe', score: 200) + ]); + print(response.result); + try { await general.error400(); } on AppwriteException catch (e) { diff --git a/tests/languages/deno/tests.ts b/tests/languages/deno/tests.ts index 7ef662d9de..c443851cc4 100644 --- a/tests/languages/deno/tests.ts +++ b/tests/languages/deno/tests.ts @@ -116,6 +116,18 @@ async function start() { // @ts-ignore console.log(response.result); + // Request model tests + response = await general.createPlayer({ id: 'player1', name: 'John Doe', score: 100 }); + // @ts-ignore + console.log(response.result); + + response = await general.createPlayers([ + { id: 'player1', name: 'John Doe', score: 100 }, + { id: 'player2', name: 'Jane Doe', score: 200 } + ]); + // @ts-ignore + console.log(response.result); + try { response = await general.error400(); } catch (error) { diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index 167eb538d7..83b8c5b860 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -84,6 +84,16 @@ public async Task Test1() mock = await general.Enum(MockType.First); TestContext.WriteLine(mock.Result); + // Request model tests + mock = await general.CreatePlayer(new Player("player1", "John Doe", 100)); + TestContext.WriteLine(mock.Result); + + mock = await general.CreatePlayers(new List { + new Player("player1", "John Doe", 100), + new Player("player2", "Jane Doe", 200) + }); + TestContext.WriteLine(mock.Result); + try { await general.Error400(); diff --git a/tests/languages/flutter/tests.dart b/tests/languages/flutter/tests.dart index 939c0ff148..cedd68bc4c 100644 --- a/tests/languages/flutter/tests.dart +++ b/tests/languages/flutter/tests.dart @@ -118,6 +118,16 @@ void main() async { response = await general.xenum(mockType: MockType.first); print(response.result); + // Request model tests + response = await general.createPlayer(player: Player(id: 'player1', name: 'John Doe', score: 100)); + print(response.result); + + response = await general.createPlayers(players: [ + Player(id: 'player1', name: 'John Doe', score: 100), + Player(id: 'player2', name: 'Jane Doe', score: 200) + ]); + print(response.result); + try { await general.error400(); } on AppwriteException catch (e) { diff --git a/tests/languages/kotlin/Tests.kt b/tests/languages/kotlin/Tests.kt index f8b13d735b..d1bc1d83f1 100644 --- a/tests/languages/kotlin/Tests.kt +++ b/tests/languages/kotlin/Tests.kt @@ -14,6 +14,7 @@ import io.appwrite.extensions.toJson import io.appwrite.models.Error import io.appwrite.models.InputFile import io.appwrite.models.Mock +import io.appwrite.models.Player import io.appwrite.services.Bar import io.appwrite.services.Foo import io.appwrite.services.General @@ -117,6 +118,16 @@ class ServiceTest { mock = general.enum(MockType.FIRST) writeToFile(mock.result) + // Request model tests + mock = general.createPlayer(Player(id = "player1", name = "John Doe", score = 100)) + writeToFile(mock.result) + + mock = general.createPlayers(listOf( + Player(id = "player1", name = "John Doe", score = 100), + Player(id = "player2", name = "Jane Doe", score = 200) + )) + writeToFile(mock.result) + try { general.error400() } catch (e: AppwriteException) { diff --git a/tests/languages/node/test.js b/tests/languages/node/test.js index ee0fd703f5..36e2add06b 100644 --- a/tests/languages/node/test.js +++ b/tests/languages/node/test.js @@ -201,6 +201,16 @@ async function start() { response = await general.enum(MockType.First); console.log(response.result); + // Request model tests + response = await general.createPlayer({ id: 'player1', name: 'John Doe', score: 100 }); + console.log(response.result); + + response = await general.createPlayers([ + { id: 'player1', name: 'John Doe', score: 100 }, + { id: 'player2', name: 'Jane Doe', score: 200 } + ]); + console.log(response.result); + try { response = await general.error400(); } catch(error) { diff --git a/tests/languages/php/test.php b/tests/languages/php/test.php index ba0d4ffe38..7cbfe3ae52 100644 --- a/tests/languages/php/test.php +++ b/tests/languages/php/test.php @@ -13,8 +13,10 @@ include __DIR__ . '/../../sdks/php/src/Appwrite/Services/Foo.php'; include __DIR__ . '/../../sdks/php/src/Appwrite/Services/Bar.php'; include __DIR__ . '/../../sdks/php/src/Appwrite/Services/General.php'; +include __DIR__ . '/../../sdks/php/src/Appwrite/Models/Player.php'; use Appwrite\AppwriteException; +use Appwrite\Models\Player; use Appwrite\Client; use Appwrite\InputFile; use Appwrite\Query; @@ -92,6 +94,18 @@ $response = $general->enum(MockType::FIRST()); echo "{$response['result']}\n"; +// Request model tests +$player = new Player('player1', 'John Doe', 100); +$response = $general->createPlayer($player); +echo "{$response['result']}\n"; + +$players = [ + new Player('player1', 'John Doe', 100), + new Player('player2', 'Jane Doe', 200), +]; +$response = $general->createPlayers($players); +echo "{$response['result']}\n"; + try { $response = $general->error400(); } catch (AppwriteException $e) { diff --git a/tests/languages/python/tests.py b/tests/languages/python/tests.py index f43a2b7a1e..e030db2822 100644 --- a/tests/languages/python/tests.py +++ b/tests/languages/python/tests.py @@ -79,6 +79,16 @@ response = general.enum(MockType.FIRST) print(response['result']) +# Request model tests +response = general.create_player({'id': 'player1', 'name': 'John Doe', 'score': 100}) +print(response['result']) + +response = general.create_players([ + {'id': 'player1', 'name': 'John Doe', 'score': 100}, + {'id': 'player2', 'name': 'Jane Doe', 'score': 200} +]) +print(response['result']) + try: response = general.error400() except AppwriteException as e: diff --git a/tests/languages/ruby/tests.rb b/tests/languages/ruby/tests.rb index c932edbca7..71c1d22f0c 100644 --- a/tests/languages/ruby/tests.rb +++ b/tests/languages/ruby/tests.rb @@ -86,6 +86,16 @@ response = general.enum(mock_type: MockType::FIRST) puts response.result +# Request model tests +response = general.create_player(player: {id: 'player1', name: 'John Doe', score: 100}) +puts response.result + +response = general.create_players(players: [ + {id: 'player1', name: 'John Doe', score: 100}, + {id: 'player2', name: 'Jane Doe', score: 200} +]) +puts response.result + begin general.error400() rescue Exception => error diff --git a/tests/languages/swift/Tests.swift b/tests/languages/swift/Tests.swift index 8719dc6466..729c3290b2 100644 --- a/tests/languages/swift/Tests.swift +++ b/tests/languages/swift/Tests.swift @@ -116,6 +116,16 @@ class Tests: XCTestCase { mock = try await general.xenum(mockType: .first) print(mock.result) + // Request model tests + mock = try await general.createPlayer(player: Player(id: "player1", name: "John Doe", score: 100)) + print(mock.result) + + mock = try await general.createPlayers(players: [ + Player(id: "player1", name: "John Doe", score: 100), + Player(id: "player2", name: "Jane Doe", score: 200) + ]) + print(mock.result) + do { try await general.error400() } catch let error as AppwriteError { diff --git a/tests/languages/web/index.html b/tests/languages/web/index.html index 7e81de9c5e..57e87bf846 100644 --- a/tests/languages/web/index.html +++ b/tests/languages/web/index.html @@ -194,6 +194,16 @@ response = await general.enum(MockType.First); console.log(response.result); + // Request model tests + response = await general.createPlayer({ id: 'player1', name: 'John Doe', score: 100 }); + console.log(response.result); + + response = await general.createPlayers([ + { id: 'player1', name: 'John Doe', score: 100 }, + { id: 'player2', name: 'Jane Doe', score: 200 } + ]); + console.log(response.result); + try { response = await general.empty(); } catch (error) { diff --git a/tests/languages/web/node.js b/tests/languages/web/node.js index f4c7bed43a..bccdb86ef7 100644 --- a/tests/languages/web/node.js +++ b/tests/languages/web/node.js @@ -130,6 +130,16 @@ async function start() { response = await general.enum(MockType.First); console.log(response.result); + // Request model tests + response = await general.createPlayer({ id: 'player1', name: 'John Doe', score: 100 }); + console.log(response.result); + + response = await general.createPlayers([ + { id: 'player1', name: 'John Doe', score: 100 }, + { id: 'player2', name: 'Jane Doe', score: 200 } + ]); + console.log(response.result); + try { response = await general.empty(); } catch (error) { diff --git a/tests/resources/spec.json b/tests/resources/spec.json index f1f0b500d2..35375f85b1 100644 --- a/tests/resources/spec.json +++ b/tests/resources/spec.json @@ -521,6 +521,159 @@ ] } }, + "\/mock\/tests\/general\/models": { + "post": { + "summary": "Create Player", + "operationId": "generalCreatePlayer", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "general" + ], + "description": "Create a new player using a request model.", + "responses": { + "200": { + "description": "Mock", + "schema": { + "$ref": "#\/definitions\/mock" + } + } + }, + "x-appwrite": { + "method": "createPlayer", + "weight": 300, + "cookies": false, + "type": "", + "demo": "general\/create-player.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a player with request model.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "public", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "offline-model": "", + "offline-key": "", + "offline-response-key": "$id", + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "player": { + "type": "object", + "description": "Player object.", + "x-model": "player", + "default": null + } + }, + "required": [ + "player" + ] + } + } + ] + } + }, + "\/mock\/tests\/general\/models\/array": { + "post": { + "summary": "Create Players", + "operationId": "generalCreatePlayers", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "general" + ], + "description": "Create multiple players using an array of request models.", + "responses": { + "200": { + "description": "Mock", + "schema": { + "$ref": "#\/definitions\/mock" + } + } + }, + "x-appwrite": { + "method": "createPlayers", + "weight": 301, + "cookies": false, + "type": "", + "demo": "general\/create-players.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate players with array of request models.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "public", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "offline-model": "", + "offline-key": "", + "offline-response-key": "$id", + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "players": { + "type": "array", + "description": "Array of player objects.", + "x-model": "player", + "default": null, + "items": { + "$ref": "#\/definitions\/player" + } + } + }, + "required": [ + "players" + ] + } + } + ] + } + }, "\/mock\/tests\/foo": { "get": { "summary": "Get Foo", @@ -1985,6 +2138,33 @@ "required": [ "result" ] + }, + "player": { + "description": "Player", + "type": "object", + "x-request-model": true, + "properties": { + "id": { + "type": "string", + "description": "Player ID.", + "x-example": "player123" + }, + "name": { + "type": "string", + "description": "Player name.", + "x-example": "John Doe" + }, + "score": { + "type": "integer", + "description": "Player score.", + "x-example": 100 + } + }, + "required": [ + "id", + "name", + "score" + ] } }, "externalDocs": { From 9857b94f5a00aff6a858b88389bad6f4834c17c5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 16 Dec 2025 21:59:44 +1300 Subject: [PATCH 2/5] Fix tests --- mock-server/app/http.php | 1 - src/SDK/Language/DotNet.php | 29 +++++++++++++++++++ src/Spec/Spec.php | 2 +- src/Spec/Swagger2.php | 8 ++--- .../node/src/models/request_model.ts.twig | 3 ++ templates/php/src/Client.php.twig | 21 +++++++++++++- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/mock-server/app/http.php b/mock-server/app/http.php index 2f21459f0f..c7dd2ab859 100644 --- a/mock-server/app/http.php +++ b/mock-server/app/http.php @@ -18,7 +18,6 @@ use Utopia\MockServer\Utopia\Response; use Utopia\Swoole\Request; use Utopia\Swoole\Response as UtopiaSwooleResponse; -use Utopia\Validator\JSON; use Utopia\Validator\Text; use Utopia\Validator\Integer; use Utopia\Validator\ArrayList; diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 8241d2a997..456ccc4fd9 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -508,9 +508,38 @@ public function getFilters(): array } return $property; }), + new TwigFilter('propertyType', function (array $property, array $spec = []) { + return $this->getPropertyType($property, $spec); + }), ]; } + /** + * Get property type for request models + * + * @param array $property + * @param array $spec + * @return string + */ + protected function getPropertyType(array $property, array $spec = []): string + { + if (isset($property['sub_schema']) && !empty($property['sub_schema'])) { + $type = $this->toPascalCase($property['sub_schema']); + + if ($property['type'] === 'array') { + return 'List<' . $type . '>'; + } + return $type; + } + + if (isset($property['enum']) && !empty($property['enum'])) { + $enumName = $property['enumName'] ?? $property['name']; + return 'Appwrite.Enums.' . $this->toPascalCase($enumName); + } + + return $this->getTypeName($property, $spec); + } + /** * get sub_scheme and property_name functions * @return TwigFunction[] diff --git a/src/Spec/Spec.php b/src/Spec/Spec.php index 14f443818e..48ca00558d 100644 --- a/src/Spec/Spec.php +++ b/src/Spec/Spec.php @@ -116,7 +116,7 @@ abstract public function getMethods($service): array; abstract public function getTargetNamespace(array $method, string $service): string; /** - * @return string + * @return array */ abstract public function getGlobalHeaders(): array; diff --git a/src/Spec/Swagger2.php b/src/Spec/Swagger2.php index 677beafa2c..32e9ad7647 100644 --- a/src/Spec/Swagger2.php +++ b/src/Spec/Swagger2.php @@ -470,15 +470,15 @@ public function getDefinitions(): array $model = [ 'name' => $key, 'properties' => $schema['properties'] ?? [], - 'description' => $schema['description'] ?? [], + '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]['description'] = $def['description'] ?? ''; + $model['properties'][$name]['example'] = $def['x-example'] ?? null; $model['properties'][$name]['required'] = in_array($name, $model['required']); if (isset($def['items']['$ref'])) { //nested model @@ -524,7 +524,7 @@ public function getRequestModels(): array $model = [ 'name' => $key, 'properties' => $schema['properties'] ?? [], - 'description' => $schema['description'] ?? [], + 'description' => $schema['description'] ?? '', 'required' => $schema['required'] ?? [], 'additionalProperties' => $schema['additionalProperties'] ?? [] ]; diff --git a/templates/node/src/models/request_model.ts.twig b/templates/node/src/models/request_model.ts.twig index a762432bf8..19dccc75c3 100644 --- a/templates/node/src/models/request_model.ts.twig +++ b/templates/node/src/models/request_model.ts.twig @@ -2,6 +2,9 @@ {%~ if property.enum %} import { {{ property.enumName | caseUcfirst }} } from '../enums/{{ property.enumName | caseKebab }}'; {%~ endif %} +{%~ if property.sub_schema %} +import { {{ property.sub_schema | caseUcfirst }}, {{ property.sub_schema | caseCamel }}ToMap } from './{{ property.sub_schema | caseKebab }}'; +{%~ endif %} {%~ endfor %} /** diff --git a/templates/php/src/Client.php.twig b/templates/php/src/Client.php.twig index 8a7dbb56d6..a9569517de 100644 --- a/templates/php/src/Client.php.twig +++ b/templates/php/src/Client.php.twig @@ -137,7 +137,7 @@ class Client switch ($headers['content-type']) { case 'application/json': - $query = json_encode($params); + $query = json_encode($this->prepareParams($params)); break; case 'multipart/form-data': @@ -243,4 +243,23 @@ class Client return $output; } + + /** + * Prepare params for JSON encoding by converting model objects to arrays + * + * @param mixed $data + * @return mixed + */ + protected function prepareParams(mixed $data): mixed + { + if (is_array($data)) { + return array_map([$this, 'prepareParams'], $data); + } + + if (is_object($data) && method_exists($data, 'toArray')) { + return $data->toArray(); + } + + return $data; + } } From a1d5cc7af65290c9fabae6c0f223264bf7eff4d6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 17 Dec 2025 14:58:16 +1300 Subject: [PATCH 3/5] Fix tests --- src/SDK/Language/Go.php | 4 ++-- templates/dart/base/utils.twig | 4 ++-- templates/flutter/base/utils.twig | 4 ++-- templates/go/services/service.go.twig | 3 +++ templates/python/package/services/service.py.twig | 6 ++++++ templates/web/src/models.ts.twig | 14 ++++++++++++++ 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/SDK/Language/Go.php b/src/SDK/Language/Go.php index aefcb459c8..eb2a9a8bca 100644 --- a/src/SDK/Language/Go.php +++ b/src/SDK/Language/Go.php @@ -167,10 +167,10 @@ public function getTypeName(array $parameter, array $spec = []): string return '[]map[string]any'; } if (!empty($parameter['array']['model'])) { - return '[]' . $this->toPascalCase($parameter['array']['model']); + return '[]models.' . $this->toPascalCase($parameter['array']['model']); } if (!empty($parameter['model'])) { - $modelType = $this->toPascalCase($parameter['model']); + $modelType = 'models.' . $this->toPascalCase($parameter['model']); return $parameter['type'] === self::TYPE_ARRAY ? '[]' . $modelType : $modelType; } if (isset($parameter['items'])) { diff --git a/templates/dart/base/utils.twig b/templates/dart/base/utils.twig index 876f92859d..88405f4f09 100644 --- a/templates/dart/base/utils.twig +++ b/templates/dart/base/utils.twig @@ -1,9 +1,9 @@ {% macro map_parameter(parameters) %} {% for parameter in parameters %} {% if not parameter.nullable and not parameter.required %} -if ({{ parameter.name | caseCamel | overrideIdentifier }} != null) '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}!.value{% endif %}, +if ({{ parameter.name | caseCamel | overrideIdentifier }} != null) '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}!.value{% elseif parameter.array.model is not empty %}!.map((p) => p.toMap()).toList(){% elseif parameter.model is not empty %}!.toMap(){% endif %}, {% else %} -'{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}{% if not parameter.required %}?{% endif %}.value{% endif %}, +'{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}{% if not parameter.required %}?{% endif %}.value{% elseif parameter.array.model is not empty %}{% if not parameter.required %}?{% endif %}.map((p) => p.toMap()).toList(){% elseif parameter.model is not empty %}{% if not parameter.required %}?{% endif %}.toMap(){% endif %}, {% endif %} {% endfor %} {% endmacro %} diff --git a/templates/flutter/base/utils.twig b/templates/flutter/base/utils.twig index 0ffa596590..5f013335d5 100644 --- a/templates/flutter/base/utils.twig +++ b/templates/flutter/base/utils.twig @@ -1,9 +1,9 @@ {%- macro map_parameter(parameters) -%} {%- for parameter in parameters ~%} {% if not parameter.nullable and not parameter.required %} - if ({{ parameter.name | caseCamel | overrideIdentifier }} != null) '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}!.value{% endif %}, + if ({{ parameter.name | caseCamel | overrideIdentifier }} != null) '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}!.value{% elseif parameter.array.model is not empty %}!.map((p) => p.toMap()).toList(){% elseif parameter.model is not empty %}!.toMap(){% endif %}, {% else %} - '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}{% if not parameter.required %}?{% endif %}.value{% endif %}, + '{{ parameter.name }}': {{ parameter.name | caseCamel | overrideIdentifier }}{% if parameter.enumValues | length > 0 %}{% if not parameter.required %}?{% endif %}.value{% elseif parameter.array.model is not empty %}{% if not parameter.required %}?{% endif %}.map((p) => p.toMap()).toList(){% elseif parameter.model is not empty %}{% if not parameter.required %}?{% endif %}.toMap(){% endif %}, {% endif %} {%- endfor ~%} {%- endmacro ~%} diff --git a/templates/go/services/service.go.twig b/templates/go/services/service.go.twig index 0c065a15c8..c69a19b277 100644 --- a/templates/go/services/service.go.twig +++ b/templates/go/services/service.go.twig @@ -8,6 +8,9 @@ {%- if (parameter | typeName) ends with "InputFile" -%} {%- set requireFilesPkg = true -%} {%- endif -%} + {%- if parameter.model is not empty -%} + {%- set requireModelsPkg = true -%} + {%- endif -%} {% endfor %} {%- endfor -%} package {{ service.name | caseLower }} diff --git a/templates/python/package/services/service.py.twig b/templates/python/package/services/service.py.twig index 0f5bb03812..d425ef82e0 100644 --- a/templates/python/package/services/service.py.twig +++ b/templates/python/package/services/service.py.twig @@ -15,6 +15,12 @@ from ..enums.{{ parameter.enumName | caseSnake }} import {{ parameter.enumName | {% set added = added|merge([parameter.enumName]) %} {% endif %} {% endif %} +{% if parameter.model is not empty %} +{% if parameter.model not in added %} +from ..models.{{ parameter.model | caseSnake }} import {{ parameter.model | caseUcfirst }}; +{% set added = added|merge([parameter.model]) %} +{% endif %} +{% endif %} {% endfor %} {% endfor %} diff --git a/templates/web/src/models.ts.twig b/templates/web/src/models.ts.twig index d52b15b3a0..a6059dd514 100644 --- a/templates/web/src/models.ts.twig +++ b/templates/web/src/models.ts.twig @@ -31,4 +31,18 @@ export namespace Models { }; {% endif %} {% endfor %} +{% for requestModel in spec.requestModels %} + + /** + * {{ requestModel.description }} + */ + export type {{ requestModel.name | caseUcfirst }} = { +{% for property in requestModel.properties %} + /** + * {{ property.description | raw }} + */ + {{ property.name }}{% if not property.required %}?{% endif %}: {{ property | getSubSchema(spec, requestModel.name) | raw }}; +{% endfor %} + } +{% endfor %} } From f9e6884ba2f50d06b63974ba45cf39a334d914db Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 17 Dec 2025 15:32:12 +1300 Subject: [PATCH 4/5] Fix tests --- templates/node/src/models/request_model.ts.twig | 8 ++++++-- templates/php/src/Client.php.twig | 2 +- templates/python/package/models/request_model.py.twig | 1 - .../ruby/lib/container/models/request_model.rb.twig | 4 ++-- templates/web/src/models.ts.twig | 9 ++++++++- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/templates/node/src/models/request_model.ts.twig b/templates/node/src/models/request_model.ts.twig index 19dccc75c3..5ebf6c98bb 100644 --- a/templates/node/src/models/request_model.ts.twig +++ b/templates/node/src/models/request_model.ts.twig @@ -1,9 +1,13 @@ +{%~ set importedEnums = [] %} +{%~ set importedSchemas = [] %} {%~ for property in requestModel.properties %} -{%~ if property.enum %} +{%~ if property.enum and property.enumName not in importedEnums %} import { {{ property.enumName | caseUcfirst }} } from '../enums/{{ property.enumName | caseKebab }}'; +{%~ set importedEnums = importedEnums | merge([property.enumName]) %} {%~ endif %} -{%~ if property.sub_schema %} +{%~ if property.sub_schema and property.sub_schema not in importedSchemas %} import { {{ property.sub_schema | caseUcfirst }}, {{ property.sub_schema | caseCamel }}ToMap } from './{{ property.sub_schema | caseKebab }}'; +{%~ set importedSchemas = importedSchemas | merge([property.sub_schema]) %} {%~ endif %} {%~ endfor %} diff --git a/templates/php/src/Client.php.twig b/templates/php/src/Client.php.twig index a9569517de..be75cc796a 100644 --- a/templates/php/src/Client.php.twig +++ b/templates/php/src/Client.php.twig @@ -250,7 +250,7 @@ class Client * @param mixed $data * @return mixed */ - protected function prepareParams(mixed $data): mixed + protected function prepareParams($data) { if (is_array($data)) { return array_map([$this, 'prepareParams'], $data); diff --git a/templates/python/package/models/request_model.py.twig b/templates/python/package/models/request_model.py.twig index 9585deed14..c8ed475eef 100644 --- a/templates/python/package/models/request_model.py.twig +++ b/templates/python/package/models/request_model.py.twig @@ -5,7 +5,6 @@ from ..enums.{{ property.enumName | caseSnake }} import {{ property.enumName | c {%~ endif %} {%~ endfor %} - class {{ requestModel.name | caseUcfirst }}: """ {{ requestModel.description }} diff --git a/templates/ruby/lib/container/models/request_model.rb.twig b/templates/ruby/lib/container/models/request_model.rb.twig index f6010d67fc..db32a048c0 100644 --- a/templates/ruby/lib/container/models/request_model.rb.twig +++ b/templates/ruby/lib/container/models/request_model.rb.twig @@ -1,4 +1,4 @@ -#frozen_string_literal: true +# frozen_string_literal: true module {{ spec.title | caseUcfirst }} module Models @@ -53,7 +53,7 @@ module {{ spec.title | caseUcfirst }} ] 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(', ') + raise ArgumentError, "Invalid " + {{ property.name | caseSnake | escapeKeyword }}.to_s + ". Must be one of: " + valid_{{ property.name | caseSnake }}.join(', ') end {{ property.name | caseSnake | escapeKeyword }} diff --git a/templates/web/src/models.ts.twig b/templates/web/src/models.ts.twig index a6059dd514..c8d8675be0 100644 --- a/templates/web/src/models.ts.twig +++ b/templates/web/src/models.ts.twig @@ -36,7 +36,7 @@ export namespace Models { /** * {{ requestModel.description }} */ - export type {{ requestModel.name | caseUcfirst }} = { + export type {{ requestModel.name | caseUcfirst }}{{ requestModel.name | getGenerics(spec, true) | raw }} = { {% for property in requestModel.properties %} /** * {{ property.description | raw }} @@ -44,5 +44,12 @@ export namespace Models { {{ property.name }}{% if not property.required %}?{% endif %}: {{ property | getSubSchema(spec, requestModel.name) | raw }}; {% endfor %} } +{% if requestModel.additionalProperties %} + + export type Default{{ requestModel.name | caseUcfirst }}{{ requestModel.name | getGenerics(spec, true) | raw }} = {{ requestModel.name | caseUcfirst }}{{ requestModel.name | getGenerics(spec, true) | raw }} & { + [key: string]: any; + [__default]: true; + }; +{% endif %} {% endfor %} } From 9dfa14dafebd1dad0ddfa7fc213184dd015902a8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 17 Dec 2025 16:20:14 +1300 Subject: [PATCH 5/5] Fix ruby nil --- src/SDK/Language/Node.php | 2 +- .../src/models/{request_model.ts.twig => requestModel.ts.twig} | 0 templates/ruby/lib/container/models/request_model.rb.twig | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename templates/node/src/models/{request_model.ts.twig => requestModel.ts.twig} (100%) diff --git a/src/SDK/Language/Node.php b/src/SDK/Language/Node.php index 376e5b5cb1..6a43a6f346 100644 --- a/src/SDK/Language/Node.php +++ b/src/SDK/Language/Node.php @@ -284,7 +284,7 @@ public function getFiles(): array [ 'scope' => 'requestModel', 'destination' => 'src/models/{{ requestModel.name | caseKebab }}.ts', - 'template' => 'node/src/models/request_model.ts.twig', + 'template' => 'node/src/models/requestModel.ts.twig', ], ]; } diff --git a/templates/node/src/models/request_model.ts.twig b/templates/node/src/models/requestModel.ts.twig similarity index 100% rename from templates/node/src/models/request_model.ts.twig rename to templates/node/src/models/requestModel.ts.twig diff --git a/templates/ruby/lib/container/models/request_model.rb.twig b/templates/ruby/lib/container/models/request_model.rb.twig index db32a048c0..fe5d4adb9f 100644 --- a/templates/ruby/lib/container/models/request_model.rb.twig +++ b/templates/ruby/lib/container/models/request_model.rb.twig @@ -29,7 +29,7 @@ module {{ spec.title | caseUcfirst }} def to_map { {% for property in requestModel.properties %} - "{{ 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 }}{% endif %}{% if not loop.last %},{% endif %} {% endfor %} }