diff --git a/.coderabbit.yaml b/.coderabbit.yaml deleted file mode 100644 index 5920efd053..0000000000 --- a/.coderabbit.yaml +++ /dev/null @@ -1,4 +0,0 @@ -reviews: - path_filters: - - "!.agents/**" - - "!.claude/**" diff --git a/.github/workflows/tests.yml b/.github/workflows/ci.yml similarity index 69% rename from .github/workflows/tests.yml rename to .github/workflows/ci.yml index e20c8200ea..d9e3f89074 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,23 @@ -name: CI Workflow +name: CI concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -on: [pull_request] +on: + pull_request: + push: + branches: + - master jobs: - build: + tests: + name: Tests / ${{ matrix.sdk }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: - php-version: ['8.3'] + php-version: ['8.5'] sdk: [ Android5Java17, Android16Java17, @@ -77,18 +82,13 @@ jobs: docker --version composer install - - name: Lint - if: matrix.sdk == 'Lint' - run: | - composer lint - - name: Run Tests env: UNITY_LICENSE: ${{ matrix.sdk == 'Unity2021' && secrets.UNITY_LICENSE || '' }} - run: | - composer test tests/${{ matrix.sdk }}Test.php + run: composer test tests/${{ matrix.sdk }}Test.php lint: + name: Checks / Lint runs-on: ubuntu-latest steps: @@ -98,7 +98,7 @@ jobs: - name: Setup PHP with PECL extension uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0 with: - php-version: '8.3' + php-version: '8.5' extensions: curl - name: Install @@ -107,7 +107,42 @@ jobs: - name: Lint run: composer lint + refactor: + name: Checks / Refactor + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup PHP with PECL extension + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0 + with: + php-version: '8.5' + extensions: curl + + - name: Install + run: composer install + + - name: Run Rector + run: composer refactor:check + + twig-lint: + name: Checks / Twig + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + - name: Run djLint + run: uvx djlint templates/ --lint + max-line-length: + name: Checks / Twig line length runs-on: ubuntu-latest steps: @@ -116,6 +151,6 @@ jobs: - name: Make script executable run: chmod +x ./.github/scripts/max-line-length.sh - + - name: Check max lines run: ./.github/scripts/max-line-length.sh . 1200 "*.twig" diff --git a/.github/workflows/djlint.yml b/.github/workflows/djlint.yml deleted file mode 100644 index e9a0a07c82..0000000000 --- a/.github/workflows/djlint.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Twig Linting - -on: - pull_request: - paths: - - '**.twig' - - '.github/workflows/djlint.yml' - - 'pyproject.toml' - push: - branches: - - master - paths: - - '**.twig' - - '.github/workflows/djlint.yml' - - 'pyproject.toml' - -jobs: - djlint: - name: djLint - Twig Linter - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - - - name: Run djLint linter - run: | - uvx djlint templates/ --lint diff --git a/.github/workflows/sdk-build-validation.yml b/.github/workflows/validation.yml similarity index 99% rename from .github/workflows/sdk-build-validation.yml rename to .github/workflows/validation.yml index 994a21feb8..865f6d3818 100644 --- a/.github/workflows/sdk-build-validation.yml +++ b/.github/workflows/validation.yml @@ -1,4 +1,4 @@ -name: Appwrite SDK Build Validation +name: Validation concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -84,7 +84,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0 with: - php-version: '8.3' + php-version: '8.5' extensions: curl - name: Install Composer Dependencies diff --git a/AGENTS.md b/AGENTS.md index c5a3f68b99..356e59fb25 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,7 @@ The script strips Twig expressions before running `npm install`/`bun install`, t ## Repository at a Glance -- **Purpose:** Generate Appwrite SDKs for ~16 languages from Swagger/OpenAPI specs using Twig templates +- **Purpose:** Generate Appwrite SDKs and tooling targets for 20+ languages/platforms from Swagger/OpenAPI specs using Twig templates - **Language:** PHP (generator engine) + Twig (templates) - **Entry point:** `example.php` — runs generation for all or a specific SDK - **Output:** `examples//` — checked-in generated SDK output for verification @@ -59,7 +59,7 @@ examples// ← Generated SDK output (checked in for verificat example.php ← Entry point: regenerates all SDKs from specs ``` -**Supported SDKs:** PHP, Web, Node, CLI, Ruby, Python, Dart, Flutter, React Native, Go, Swift, Apple, DotNet, Android, Kotlin, GraphQL, Markdown, AgentSkills, CursorPlugin, ClaudePlugin, CodexPlugin +**Supported SDKs:** PHP, Web, Node, CLI, Ruby, Python, Dart, Flutter, React Native, Go, Swift, Apple, DotNet, Android, Kotlin, Unity, REST, GraphQL, Rust, AgentSkills, CursorPlugin, ClaudePlugin, CodexPlugin ## Primary Workflows @@ -68,11 +68,12 @@ example.php ← Entry point: regenerates all SDKs from specs 1. Edit template(s) in `templates//` 2. Regenerate: ```bash - docker run --rm -v $(pwd):/app -w /app php:8.3-cli php example.php + php example.php ``` 3. Diff `examples//` to verify the output is correct -4. Run linter: +4. Run linters and refactor check: ```bash + composer refactor:check composer lint-twig # or directly uvx djLint templates/ --lint @@ -116,7 +117,7 @@ public function getFiles(): array 3. Create `templates/newlang/` and add all Twig files 4. Register all template files in `getFiles()` 5. Add generation block to `example.php` -6. Generate: `docker run --rm -v $(pwd):/app -w /app php:8.3-cli php example.php newlang` +6. Generate: `php example.php newlang` 7. Inspect `examples/newlang/` ## File Reference Map @@ -139,6 +140,7 @@ Pass as first argument to generate only that SDK: | Argument | Language class | Output dir | |----------|---------------|------------| | `php` | PHP | `examples/php/` | +| `unity` | Unity | `examples/unity/` | | `web` | Web | `examples/web/` | | `node` | Node | `examples/node/` | | `cli` | CLI | `examples/cli/` | @@ -151,10 +153,11 @@ Pass as first argument to generate only that SDK: | `swift` | Swift | `examples/swift/` | | `apple` | Apple | `examples/apple/` | | `dotnet` | DotNet | `examples/dotnet/` | +| `rest` | REST | `examples/REST/` | | `android` | Android | `examples/android/` | | `kotlin` | Kotlin | `examples/kotlin/` | | `graphql` | GraphQL | `examples/graphql/` | -| `markdown` | Markdown | `examples/markdown/` | +| `rust` | Rust | `examples/rust/` | | `agent-skills` | AgentSkills | `examples/agent-skills/` | | `cursor-plugin` | CursorPlugin | `examples/cursor-plugin/` | | `claude-plugin` | ClaudePlugin | `examples/claude-plugin/` | @@ -186,19 +189,17 @@ Pass as first argument to generate only that SDK: ## Installing Dependencies ```bash -# With Composer installed locally composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist - -# With Docker -docker run --rm -it -v "$(pwd)":/app composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist ``` ## Running Tests ```bash -docker run --rm -v $(pwd):$(pwd):rw -w $(pwd) php:8.3-cli-alpine vendor/bin/phpunit +vendor/bin/phpunit ``` +If local PHP is missing, is not the required version, or has extension issues, use the matching PHP Docker image as a fallback for that command. + ## Pre-Submit Checklist Before submitting changes that touch templates or language classes: @@ -207,5 +208,6 @@ Before submitting changes that touch templates or language classes: - [ ] Inspected `examples//` output looks correct - [ ] Any new template files are listed in `getFiles()` of the language class - [ ] Any new language class is added to `example.php` +- [ ] Rector check passes (`composer refactor:check`) - [ ] Twig linter passes (`composer lint-twig`) - [ ] If a parent language was modified, child SDKs were also checked diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70533b0ba0..39fdbefa38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,7 +115,7 @@ sdk-generator/blob/master/example.php: Run the following command (make sure you have an updated docker version on your machine): ```bash -docker run --rm -v $(pwd):/app -w /app php:8.3-cli php example.php +docker run --rm -v $(pwd):/app -w /app php:8.5-cli php example.php ``` >Note: You can just add the new language next to the other languages in the `example.php` file. You don't need to rewrite the file completely. @@ -277,7 +277,7 @@ Also in `.travis.yml` add new env `SDK=[Language]` so that travis will run a tes Finally, you can run tests using: ```sh -docker run --rm -v $(pwd):$(pwd):rw -w $(pwd) -v /var/run/docker.sock:/var/run/docker.sock php:8.3-cli-alpine sh -c "apk add docker-cli && vendor/bin/phpunit" +docker run --rm -v $(pwd):$(pwd):rw -w $(pwd) -v /var/run/docker.sock:/var/run/docker.sock php:8.5-cli-alpine sh -c "apk add docker-cli && vendor/bin/phpunit" ``` ## SDK Generator Interface diff --git a/README.md b/README.md index aeff0f5e66..cd1ed5f71c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ⚙️ Appwrite SDK Generator [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -[![Twig Linting](https://github.com/appwrite/sdk-generator/actions/workflows/djlint.yml/badge.svg)](https://github.com/appwrite/sdk-generator/actions/workflows/djlint.yml) +[![CI](https://github.com/appwrite/sdk-generator/actions/workflows/ci.yml/badge.svg)](https://github.com/appwrite/sdk-generator/actions/workflows/ci.yml) [![X Account](https://img.shields.io/badge/follow-@appwrite-000000?style=flat-square&logo=x&logoColor=white)](https://x.com/appwrite) [![appwrite.io](https://img.shields.io/badge/appwrite-.io-f02e65?style=flat-square)](https://appwrite.io) @@ -174,7 +174,6 @@ php example.php agent-skills | CLI | `cli` | Node.js 20 and Bun 1.3.11 in CI | [NPM Coding Style] | NPM, Bun, native binaries | `examples/cli/` | | REST examples | `rest` | N/A | Markdown | N/A | `examples/REST/` | | GraphQL | `graphql` | N/A | GraphQL | N/A | `examples/graphql/` | -| Markdown docs | `markdown` | N/A | Markdown | N/A | `examples/markdown/` | | Agent Skills | `agent-skills` | N/A | Markdown | N/A | `examples/agent-skills/` | | Cursor Plugin | `cursor-plugin` | N/A | Markdown | N/A | `examples/cursor-plugin/` | | Claude Plugin | `claude-plugin` | N/A | Markdown | N/A | `examples/claude-plugin/` | diff --git a/composer.json b/composer.json index 748d9512a2..eb627563c9 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,8 @@ { "name": "appwrite/sdk-generator", - "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", + "description": "SDK generator for Appwrite APIs across multiple programming languages and platforms", "type": "library", "license": "MIT", - "minimum-stability": "dev", - "prefer-stable": true, "authors": [ { "name": "Eldad Fux", @@ -15,33 +13,45 @@ "test": "vendor/bin/phpunit", "lint": "vendor/bin/phpcs", "format": "vendor/bin/phpcbf", - "lint-twig": "uvx djlint templates/ --lint" + "refactor": "vendor/bin/rector process", + "refactor:check": "vendor/bin/rector process --dry-run", + "lint-twig": "uvx djlint templates/ --lint", + "check": [ + "@lint", + "@lint-twig", + "@refactor:check", + "@test" + ] }, "autoload": { "psr-4": { - "Appwrite\\SDK\\": "src/SDK", - "Appwrite\\Spec\\": "src/Spec" + "Appwrite\\SDK\\": "src/SDK/", + "Appwrite\\Spec\\": "src/Spec/" } }, "autoload-dev": { - "psr-4": {"Tests\\": "tests"} + "psr-4": { + "Tests\\": "tests/" + } }, "require": { - "php": ">=8.3", + "php": ">=8.5", "ext-curl": "*", - "ext-mbstring": "*", "ext-json": "*", - "twig/twig": "3.27.*", - "matthiasmullie/minify": "1.3.*" + "ext-mbstring": "*", + "matthiasmullie/minify": "1.3.*", + "twig/twig": "3.27.*" }, "require-dev": { - "phpunit/phpunit": "11.*", "brianium/paratest": "7.*", + "phpunit/phpunit": "11.*", + "rector/rector": "^2.4", "squizlabs/php_codesniffer": "3.*" }, "config": { + "sort-packages": true, "platform": { - "php": "8.3" + "php": "8.5" } } } diff --git a/composer.lock b/composer.lock index a6d3c32c5a..090abb49e7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "75cc89f334411451689d526c974d28d3", + "content-hash": "d2927fd329cc01d441ab39e27aecd122", "packages": [ { "name": "matthiasmullie/minify", @@ -452,16 +452,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.8.4", + "version": "v7.8.5", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4" + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/130a9bf0e269ee5f5b320108f794ad03e275cad4", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", + "reference": "9b324c8fc319cf9728b581c7a90e1c8f6361c5e5", "shasum": "" }, "require": { @@ -469,27 +469,27 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", + "fidry/cpu-core-counter": "^1.3.0", "jean85/pretty-package-versions": "^2.1.1", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^11.0.10", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^11.0.12", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-timer": "^7.0.1", - "phpunit/phpunit": "^11.5.24", + "phpunit/phpunit": "^11.5.46", "sebastian/environment": "^7.2.1", - "symfony/console": "^6.4.22 || ^7.3.0", - "symfony/process": "^6.4.20 || ^7.3.0" + "symfony/console": "^6.4.22 || ^7.3.4 || ^8.0.3", + "symfony/process": "^6.4.20 || ^7.3.4 || ^8.0.3" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan": "^2.1.33", "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "squizlabs/php_codesniffer": "^3.13.2", - "symfony/filesystem": "^6.4.13 || ^7.3.0" + "phpstan/phpstan-phpunit": "^2.0.11", + "phpstan/phpstan-strict-rules": "^2.0.7", + "squizlabs/php_codesniffer": "^3.13.5", + "symfony/filesystem": "^6.4.13 || ^7.3.2 || ^8.0.1" }, "bin": [ "bin/paratest", @@ -529,7 +529,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.8.4" + "source": "https://github.com/paratestphp/paratest/tree/v7.8.5" }, "funding": [ { @@ -541,7 +541,7 @@ "type": "paypal" } ], - "time": "2025-06-23T06:07:21+00:00" + "time": "2026-01-08T08:02:38+00:00" }, { "name": "fidry/cpu-core-counter", @@ -900,6 +900,70 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpstan/phpstan", + "version": "2.2.2", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-06-05T09:00:01+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "11.0.12", @@ -992,28 +1056,28 @@ }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1041,15 +1105,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", @@ -1397,6 +1473,66 @@ }, "time": "2021-11-05T16:47:00+00:00" }, + { + "name": "rector/rector", + "version": "2.4.5", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c", + "reference": "cbd86024be5014d3c14d9f0b3f7aae8ecbffd62c", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.56" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.4.5" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-05-26T21:03:22+00:00" + }, { "name": "sebastian/cli-parser", "version": "3.0.2", @@ -2516,47 +2652,49 @@ }, { "name": "symfony/console", - "version": "v7.3.6", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "url": "https://api.github.com/repos/symfony/console/zipball/f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", + "reference": "f5a856c6ecb56b3c21ed94a5b7bf940d857d110a", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4.1", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.4.6|^8.0.6" }, "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2590,7 +2728,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.6" + "source": "https://github.com/symfony/console/tree/v8.1.0" }, "funding": [ { @@ -2610,20 +2748,20 @@ "type": "tidelift" } ], - "time": "2025-11-04T01:21:42+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -2672,7 +2810,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -2692,20 +2830,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -2757,7 +2895,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -2777,24 +2915,104 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "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\\Php85\\": "" + }, + "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": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.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": "2026-05-26T02:25:22+00:00" }, { "name": "symfony/process", - "version": "v7.4.5", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "608476f4604102976d687c483ac63a79ba18cc97" + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", - "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "url": "https://api.github.com/repos/symfony/process/zipball/c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4.1" }, "type": "library", "autoload": { @@ -2822,7 +3040,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.5" + "source": "https://github.com/symfony/process/tree/v8.1.0" }, "funding": [ { @@ -2842,20 +3060,20 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:07:59+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -2873,7 +3091,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -2909,7 +3127,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -2929,38 +3147,38 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v7.3.4", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2999,7 +3217,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/string/tree/v8.1.0" }, "funding": [ { @@ -3019,7 +3237,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "theseer/tokenizer", @@ -3073,19 +3291,19 @@ } ], "aliases": [], - "minimum-stability": "dev", + "minimum-stability": "stable", "stability-flags": {}, - "prefer-stable": true, + "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.3", + "php": ">=8.5", "ext-curl": "*", - "ext-mbstring": "*", - "ext-json": "*" + "ext-json": "*", + "ext-mbstring": "*" }, "platform-dev": {}, "platform-overrides": { - "php": "8.3" + "php": "8.5" }, "plugin-api-version": "2.9.0" } diff --git a/example.php b/example.php index 82ae3a7bd5..fe32752062 100644 --- a/example.php +++ b/example.php @@ -1,6 +1,6 @@ 'NAME', 'version' => '0.0.0', @@ -105,20 +103,17 @@ function configureSDK($sdk, $overrides = []) { return $sdk; } - $requestedSdk = isset($argv[1]) ? $argv[1] : null; - $requestedPlatform = isset($argv[2]) ? $argv[2] : null; + $requestedSdk = $argv[1] ?? null; + $requestedPlatform = $argv[2] ?? null; - if ($requestedPlatform) { - $platform = $requestedPlatform; - } else { - $platform = 'console'; - // $platform = 'client'; - // $platform = 'server'; - } + $platform = $requestedPlatform ?: 'console'; + // $platform = 'client'; + // $platform = 'server'; $version = '1.9.x'; $speclessSDKs = ['agent-skills', 'cursor-plugin', 'claude-plugin', 'codex-plugin']; $needsSpec = !$requestedSdk || !in_array($requestedSdk, $speclessSDKs); + $spec = ''; if ($needsSpec) { $spec = getSSLPage("https://raw.githubusercontent.com/appwrite/specs/main/specs/{$version}/swagger2-{$version}-{$platform}.json"); @@ -305,14 +300,6 @@ function configureSDK($sdk, $overrides = []) { $sdk->generate(__DIR__ . '/examples/graphql'); } - // Markdown - if (!$requestedSdk || $requestedSdk === 'markdown') { - $markdown = new Markdown(); - $markdown->setNPMPackage('@appwrite.io/docs'); - $sdk = new SDK($markdown, new Swagger2($spec)); - configureSDK($sdk); - $sdk->generate(__DIR__ . '/examples/markdown'); - } // Agent Skills if (!$requestedSdk || $requestedSdk === 'agent-skills') { $sdk = new SDK(new AgentSkills(), new StaticSpec( @@ -372,9 +359,6 @@ function configureSDK($sdk, $overrides = []) { $sdk->generate(__DIR__ . '/examples/rust'); } } -catch (Exception $exception) { - echo 'Error: ' . $exception->getMessage() . ' on ' . $exception->getFile() . ':' . $exception->getLine() . "\n"; -} catch (Throwable $exception) { echo 'Error: ' . $exception->getMessage() . ' on ' . $exception->getFile() . ':' . $exception->getLine() . "\n"; } diff --git a/rector.php b/rector.php new file mode 100644 index 0000000000..f80c58005a --- /dev/null +++ b/rector.php @@ -0,0 +1,37 @@ +withImportNames() + ->withPaths([ + 'rector.php', + __DIR__ . '/example.php', + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withSkipPath(__DIR__ . '/vendor') + ->withSkipPath(__DIR__ . '/tests/resources') + ->withSkipPath(__DIR__ . '/tests/sdks/*') + ->withPhpSets(php85: true) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true + ) + ->withSets([ + PHPUnitSetList::PHPUNIT_CODE_QUALITY, + ]) + ->withSkip([ + ThrowWithPreviousExceptionRector::class, + DisallowedEmptyRuleFixerRector::class, + NewMethodCallWithoutParenthesesRector::class => [ + __DIR__ . '/tests/languages/php/test.php', + ], + ]); diff --git a/src/SDK/Language.php b/src/SDK/Language.php index e3c4805e47..8aa49f0c1a 100644 --- a/src/SDK/Language.php +++ b/src/SDK/Language.php @@ -2,6 +2,8 @@ namespace Appwrite\SDK; +use Normalizer; + abstract class Language { public const TYPE_INTEGER = 'integer'; @@ -17,9 +19,6 @@ abstract class Language */ protected $params = []; - /** - * @return string - */ abstract public function getName(): string; /** @@ -34,20 +33,17 @@ abstract public function getIdentifierOverrides(): array; /** * Get the static access operator for the language (e.g. '::' for PHP, '.' for JS) - * @return string */ abstract public function getStaticAccessOperator(): string; /** * Get the string quote character for the language (e.g. '"' for PHP, "'" for JS) - * @return string */ abstract public function getStringQuote(): string; /** * Wrap elements in an array syntax for the language * @param string $elements Comma-separated elements - * @return string */ abstract public function getArrayOf(string $elements): string; @@ -64,36 +60,20 @@ abstract public function getFiles(): array; * full file tree). Default implementation is a no-op. * * @param string $target Absolute path the SDK was generated into. - * @return void */ public function postGenerate(string $target): void { } - /** - * @param array $parameter - * @return string - */ abstract public function getTypeName(array $parameter, array $spec = []): string; - /** - * @param array $param - * @return string - */ abstract public function getParamDefault(array $param): string; /** - * @param array $param * @param string $lang Optional language variant (for multi-language SDKs) - * @return string */ abstract public function getParamExample(array $param, string $lang = ''): string; - /** - * @param string $key - * @param string $value - * @return Language - */ public function setParam(string $key, string $value): Language { $this->params[$key] = $value; @@ -101,9 +81,6 @@ public function setParam(string $key, string $value): Language return $this; } - /** - * @return array - */ public function getParams(): array { return $this->params; @@ -111,7 +88,6 @@ public function getParams(): array /** * Language specific filters. - * @return array */ public function getFilters(): array { @@ -120,7 +96,6 @@ public function getFilters(): array /** * Language specific functions. - * @return array */ public function getFunctions(): array { @@ -135,24 +110,23 @@ protected function toPascalCase(string $value): string protected function toCamelCase($str): string { // Normalize the string to decompose accented characters - $str = \Normalizer::normalize($str, \Normalizer::FORM_D); + $str = Normalizer::normalize($str, Normalizer::FORM_D); // Remove accents and other residual non-ASCII characters $str = \preg_replace('/\p{M}/u', '', $str); - $str = \preg_replace('/[^a-zA-Z0-9]+/', ' ', $str); - $str = \trim($str); + $str = \preg_replace('/[^a-zA-Z0-9]+/', ' ', (string) $str); + $str = \trim((string) $str); $str = \ucwords($str); $str = \str_replace(' ', '', $str); - $str = \lcfirst($str); - return $str; + return \lcfirst($str); } protected function toSnakeCase($str): string { // Normalize the string to decompose accented characters - $str = \Normalizer::normalize($str, \Normalizer::FORM_D); + $str = Normalizer::normalize($str, Normalizer::FORM_D); // Remove accents and other residual non-ASCII characters $str = \preg_replace('/\p{M}/u', '', $str); @@ -160,11 +134,10 @@ protected function toSnakeCase($str): string // Remove apostrophes before replacing non-word characters with underscores $str = \str_replace("'", '', $str); $str = \preg_replace('/[^a-zA-Z0-9]+/', '_', $str); - $str = \preg_replace('/_+/', '_', $str); - $str = \trim($str, '_'); - $str = \strtolower($str); + $str = \preg_replace('/_+/', '_', (string) $str); + $str = \trim((string) $str, '_'); - return $str; + return \strtolower($str); } protected function toUpperSnakeCase($str): string @@ -174,9 +147,6 @@ protected function toUpperSnakeCase($str): string /** * Escape reserved keywords by prefixing with 'x' - * - * @param string $value - * @return string */ public function escapeKeyword(string $value): string { @@ -207,11 +177,11 @@ public function extractPermissionParts(string $string): array $id = null; $innerRole = null; - if (strpos($roleString, ':') !== false) { + if (str_contains($roleString, ':')) { $role = explode(':', $roleString, 2)[0]; $idString = explode(':', $roleString, 2)[1]; - if (strpos($idString, '/') !== false) { + if (str_contains($idString, '/')) { $id = explode('/', $idString, 2)[0]; $innerRole = explode('/', $idString, 2)[1]; } else { @@ -245,7 +215,6 @@ public function hasPermissionParam(array $parameters): bool /** * Get the prefix for Permission and Role classes (e.g., 'sdk.' for Node) - * @return string */ protected function getPermissionPrefix(): string { @@ -255,8 +224,6 @@ protected function getPermissionPrefix(): string /** * Transform permission action name for language-specific casing * Override in child classes if needed (e.g., DotNet uses ucfirst) - * @param string $action - * @return string */ protected function transformPermissionAction(string $action): string { @@ -266,8 +233,6 @@ protected function transformPermissionAction(string $action): string /** * Transform permission role name for language-specific casing * Override in child classes if needed (e.g., DotNet uses ucfirst) - * @param string $role - * @return string */ protected function transformPermissionRole(string $role): string { @@ -277,7 +242,6 @@ protected function transformPermissionRole(string $role): string /** * Generate permission example code for the language * @param string $example Permission string example - * @return string */ public function getPermissionExample(string $example): string { diff --git a/src/SDK/Language/AgentSkills.php b/src/SDK/Language/AgentSkills.php index 4d7906fd7a..4866f06bee 100644 --- a/src/SDK/Language/AgentSkills.php +++ b/src/SDK/Language/AgentSkills.php @@ -2,6 +2,7 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; @@ -24,93 +25,56 @@ class AgentSkills extends Language protected string $skillDestination = 'skills/{{ spec.title | caseLower }}-%s/SKILL.md'; protected bool $prefixSkillName = true; - /** - * @return string - */ public function getName(): string { return 'AgentSkills'; } - /** - * @return array - */ public function getKeywords(): array { return []; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; } - /** - * @return string - */ public function getStaticAccessOperator(): string { return '.'; } - /** - * @return string - */ public function getStringQuote(): string { return '"'; } - /** - * @param string $elements - * @return string - */ public function getArrayOf(string $elements): string { return '[' . $elements . ']'; } - /** - * @param array $parameter - * @param array $spec - * @return string - */ public function getTypeName(array $parameter, array $spec = []): string { return $parameter['type'] ?? 'string'; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { return $param['default'] ?? ''; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { return $param['example'] ?? ''; } - /** - * @return array - */ + #[Override] public function getFilters(): array { return [ - new TwigFilter('skillName', function (string $lang, array $spec): string { - return $this->getSkillName($lang, $spec['title'] ?? ''); - }), + new TwigFilter('skillName', fn(string $lang, array $spec): string => $this->getSkillName($lang, $spec['title'] ?? '')), ]; } @@ -138,9 +102,6 @@ protected function getSkillFiles(): array return $files; } - /** - * @return array - */ public function getFiles(): array { $files = $this->getSkillFiles(); diff --git a/src/SDK/Language/Android.php b/src/SDK/Language/Android.php index 499b1f2241..0e660af449 100644 --- a/src/SDK/Language/Android.php +++ b/src/SDK/Language/Android.php @@ -1,20 +1,20 @@ 'packageName', 'executableName' => 'executable', @@ -112,16 +112,13 @@ class CLI extends Node 'private' ]; - /** - * @return string - */ + #[Override] public function getName(): string { return 'cli'; } /** - * @param string $name * @return $this */ public function setExecutableName(string $name): self @@ -132,7 +129,6 @@ public function setExecutableName(string $name): self } /** - * @param string $logo * @return $this */ public function setLogo(string $logo): self @@ -143,7 +139,6 @@ public function setLogo(string $logo): self } /** - * @param string $logo * @return $this */ public function setLogoUnescaped(string $logo): self @@ -170,21 +165,18 @@ public function setHomebrewTap(string $owner, string $name): self /** * Convert string to kebab-case. - * @param string $value - * @return string */ protected function toKebabCase(string $value): string { $value = preg_replace('/([a-z])([A-Z])/', '$1-$2', $value); - $value = preg_replace('/[\s_]+/', '-', $value); - return strtolower($value); + $value = preg_replace('/[\s_]+/', '-', (string) $value); + return strtolower((string) $value); } /** * Escape reserved keywords. - * @param string $name - * @return string */ + #[Override] public function escapeKeyword(string $name): string { $reserved = $this->reservedKeywords; @@ -246,11 +238,17 @@ private function getCliQueryConfig(array $method): array $builderParams[] = 'queries'; if ($hasFilteringQueries) { - array_push($builderParams, 'filter', 'where', 'sortAsc', 'sortDesc', 'cursorAfter', 'cursorBefore'); + $builderParams[] = 'filter'; + $builderParams[] = 'where'; + $builderParams[] = 'sortAsc'; + $builderParams[] = 'sortDesc'; + $builderParams[] = 'cursorAfter'; + $builderParams[] = 'cursorBefore'; } if ($hasPaginationQueries) { - array_push($builderParams, 'limit', 'offset'); + $builderParams[] = 'limit'; + $builderParams[] = 'offset'; } if ($hasSelectQueries) { @@ -280,9 +278,7 @@ private function getCliQueryConfig(array $method): array ]; } - /** - * @return array - */ + #[Override] public function getFiles(): array { return [ @@ -706,17 +702,16 @@ public function getFiles(): array } /** - * @param array $parameter * @param array $nestedTypes - * @return string */ + #[Override] public function getTypeName(array $parameter, array $spec = []): string { if (isset($parameter['enumName'])) { return \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); + return \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return $this->toPascalCase($parameter['array']['model']) . '[]'; @@ -743,11 +738,7 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @param string $lang - * @return string - */ + #[Override] public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -778,8 +769,8 @@ public function getParamExample(array $param, string $lang = ''): string } else { switch ($type) { case self::TYPE_ARRAY: - if (str_contains($example, '[') && str_contains($example, ']')) { - $trimmed = substr($example, 1, -1); + if (str_contains((string) $example, '[') && str_contains((string) $example, ']')) { + $trimmed = substr((string) $example, 1, -1); $split = explode(',', $trimmed); $output .= implode(' ', $split); } else { @@ -810,8 +801,8 @@ public function getParamExample(array $param, string $lang = ''): string /** * Language specific filters. - * @return array */ + #[Override] public function getFilters(): array { return array_merge(parent::getFilters(), [ @@ -822,14 +813,14 @@ public function getFilters(): array /** * Language specific functions. - * @return array */ + #[Override] public function getFunctions(): array { return [ /** Return true if the entered service->method is enabled for a console preview link */ - new TwigFunction('hasConsolePreview', fn($method, $service) => preg_match('/^([Gg]et|[Ll]ist)/', $method) - && !in_array(strtolower($method), $this->consoleIgnoreFunctions) + new TwigFunction('hasConsolePreview', fn($method, $service): bool => preg_match('/^([Gg]et|[Ll]ist)/', (string) $method) + && !in_array(strtolower((string) $method), $this->consoleIgnoreFunctions) && !in_array($service, $this->consoleIgnoreServices)), /** @@ -869,33 +860,31 @@ public function getFunctions(): array 'parser' => null, ]; } + } elseif ($type === 'boolean') { + return [ + 'method' => 'option', + 'syntax' => "--{$optionName} [value]", + 'parser' => null, + 'customParserCode' => "(value: string | undefined) =>\n value === undefined ? true : parseBool(value)", + ]; + } elseif ($type === 'integer' || $type === 'number') { + return [ + 'method' => 'option', + 'syntax' => "--{$optionName} <{$optionName}>", + 'parser' => 'parseInteger', + ]; + } elseif ($type === 'array') { + return [ + 'method' => 'option', + 'syntax' => "--{$optionName} [{$optionName}...]", + 'parser' => null, + ]; } else { - if ($type === 'boolean') { - return [ - 'method' => 'option', - 'syntax' => "--{$optionName} [value]", - 'parser' => null, - 'customParserCode' => "(value: string | undefined) =>\n value === undefined ? true : parseBool(value)", - ]; - } elseif ($type === 'integer' || $type === 'number') { - return [ - 'method' => 'option', - 'syntax' => "--{$optionName} <{$optionName}>", - 'parser' => 'parseInteger', - ]; - } elseif ($type === 'array') { - return [ - 'method' => 'option', - 'syntax' => "--{$optionName} [{$optionName}...]", - 'parser' => null, - ]; - } else { - return [ - 'method' => 'option', - 'syntax' => "--{$optionName} <{$optionName}>", - 'parser' => null, - ]; - } + return [ + 'method' => 'option', + 'syntax' => "--{$optionName} <{$optionName}>", + 'parser' => null, + ]; } }), diff --git a/src/SDK/Language/ClaudePlugin.php b/src/SDK/Language/ClaudePlugin.php index 94979d04bc..ecf53a5687 100644 --- a/src/SDK/Language/ClaudePlugin.php +++ b/src/SDK/Language/ClaudePlugin.php @@ -1,23 +1,25 @@ getSkillFiles(); diff --git a/src/SDK/Language/CodexPlugin.php b/src/SDK/Language/CodexPlugin.php index e6ce4c64d6..8ee9cfa9af 100644 --- a/src/SDK/Language/CodexPlugin.php +++ b/src/SDK/Language/CodexPlugin.php @@ -1,20 +1,20 @@ getSkillFiles(); diff --git a/src/SDK/Language/Dart.php b/src/SDK/Language/Dart.php index 95e35c9559..651035df71 100644 --- a/src/SDK/Language/Dart.php +++ b/src/SDK/Language/Dart.php @@ -2,6 +2,7 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; @@ -10,12 +11,12 @@ class Dart extends Language /** * @var array */ + #[Override] protected $params = [ 'packageName' => 'packageName', ]; /** - * @param string $name * @return $this */ public function setPackageName(string $name): self @@ -25,9 +26,6 @@ public function setPackageName(string $name): self return $this; } - /** - * @return string - */ public function getName(): string { return 'Dart'; @@ -35,8 +33,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -107,9 +103,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return [ @@ -136,10 +129,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @param array $parameter - * @return string - */ public function getTypeName(array $parameter, array $spec = []): string { if ( @@ -148,7 +137,7 @@ public function getTypeName(array $parameter, array $spec = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return 'List'; } @@ -157,7 +146,7 @@ public function getTypeName(array $parameter, array $spec = []): string return 'enums.' . \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return 'enums.' . \ucfirst($parameter['name']); + return 'enums.' . \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return 'ListtoPascalCase($parameter['array']['model']) . '>'; @@ -193,10 +182,6 @@ public function getTypeName(array $parameter, array $spec = []): string } } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -250,11 +235,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -289,7 +269,7 @@ public function getParamExample(array $param, string $lang = ''): string public function getModelToMapValue(array $property): string { $name = $this->escapeKeyword($property['name'] ?? ''); - $nullAware = !empty($property['required']) ? '' : '?'; + $nullAware = empty($property['required']) ? '?' : ''; if (!empty($property['sub_schema'])) { if (($property['type'] ?? '') === self::TYPE_ARRAY) { @@ -306,9 +286,6 @@ public function getModelToMapValue(array $property): string return $name; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -560,20 +537,19 @@ public function getFiles(): array ]; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('dartComment', function ($value) { + new TwigFilter('dartComment', function ($value): string { $value = explode("\n", $value); - foreach ($value as $key => $line) { + foreach (array_keys($value) as $key) { $value[$key] = " /// " . wordwrap($value[$key], 75, "\n /// "); } return implode("\n", $value); }, ['is_safe' => ['html']]), - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toCamelCase($value); - }), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toCamelCase($value)), + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -584,7 +560,7 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toCamelCase($enumKeys[$index]); @@ -607,13 +583,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return 'enums.' . \ucfirst($enumName) . '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => 'enums.' . \ucfirst($enumName) . '.' . $resolveKey($value), $values); return '[' . implode(', ', $items) . ']'; } @@ -621,9 +595,7 @@ public function getFilters(): array $value = ($example !== null && $example !== '') ? $example : $enumValues[0]; return 'enums.' . \ucfirst($enumName) . '.' . $resolveKey($value); }), - new TwigFilter('modelToMapValue', function (array $property) { - return $this->getModelToMapValue($property); - }, ['is_safe' => ['html']]), + new TwigFilter('modelToMapValue', fn(array $property): string => $this->getModelToMapValue($property), ['is_safe' => ['html']]), ]; } } diff --git a/src/SDK/Language/Deno.php b/src/SDK/Language/Deno.php index 0d13cbb57c..4244bcc9f6 100644 --- a/src/SDK/Language/Deno.php +++ b/src/SDK/Language/Deno.php @@ -2,11 +2,10 @@ namespace Appwrite\SDK\Language; +use Override; + class Deno extends JS { - /** - * @return string - */ public function getName(): string { return 'Deno'; @@ -27,9 +26,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -151,17 +147,14 @@ public function getFiles(): array ]; } - /** - * @param array $parameter - * @return string - */ + #[Override] public function getTypeName(array $parameter, array $spec = []): string { if (isset($parameter['enumName'])) { return \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); + return \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return $this->toPascalCase($parameter['array']['model']) . '[]'; @@ -186,11 +179,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -215,7 +203,7 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? preg_replace('/\n/', "\n ", $formatted) : $example), self::TYPE_STRING => "'{$example}'", diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index c26d3240de..5d06801d6a 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -2,15 +2,13 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; use Twig\TwigFunction; class DotNet extends Language { - /** - * @return string - */ public function getName(): string { return 'DotNet'; @@ -18,8 +16,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -135,9 +131,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return [ @@ -151,6 +144,7 @@ public function getStaticAccessOperator(): string return '.'; } + #[Override] public function escapeKeyword(string $value): string { if (in_array($value, $this->getKeywords())) { @@ -170,11 +164,13 @@ public function getArrayOf(string $elements): string return 'new List { ' . $elements . ' }'; } + #[Override] protected function transformPermissionAction(string $action): string { return ucfirst($action); } + #[Override] protected function transformPermissionRole(string $role): string { return ucfirst($role); @@ -189,10 +185,6 @@ public function getPropertyOverrides(): array ]; } - /** - * @param array $parameter - * @return string - */ public function getTypeName(array $parameter, array $spec = []): string { if ( @@ -201,7 +193,7 @@ public function getTypeName(array $parameter, array $spec = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return 'List'; } @@ -210,7 +202,7 @@ public function getTypeName(array $parameter, array $spec = []): string return 'Appwrite.Enums.' . \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return 'Appwrite.Enums.' . \ucfirst($parameter['name']); + return 'Appwrite.Enums.' . \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return 'ListgetTypeIdentifier($parameter['array']['model']) . '>'; @@ -237,10 +229,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -286,11 +274,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -344,7 +327,7 @@ public function getParamExample(array $param, string $lang = ''): string if ($example === '{}') { $output .= '[object]'; } else { - $decoded = json_decode($example, true); + $decoded = json_decode((string) $example, true); if ($decoded && is_array($decoded)) { $csharpObject = $this->formatCSharpAnonymousObject($decoded, 1); $output .= 'new ' . $csharpObject; @@ -365,9 +348,6 @@ public function getParamExample(array $param, string $lang = ''): string return $output; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -514,32 +494,22 @@ public function getFiles(): array ]; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('dotnetComment', function ($value) { + new TwigFilter('dotnetComment', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " /// " . wordwrap($line, 75, "\n /// "); } return implode("\n", $value); }, ['is_safe' => ['html']]), - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toPascalCase($value); - }), - new TwigFilter('overrideProperty', function (string $property, string $class) { - if (isset($this->getPropertyOverrides()[$class][$property])) { - return $this->getPropertyOverrides()[$class][$property]; - } - return $property; - }), - new TwigFilter('propertyType', function (array $property, array $spec = []) { - return $this->getPropertyType($property, $spec); - }), - new TwigFilter('toMapValue', function (array $property, string $resolvedName) { - return $this->getToMapExpression($property, $resolvedName); - }, ['is_safe' => ['html']]), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toPascalCase($value)), + new TwigFilter('overrideProperty', fn(string $property, string $class) => $this->getPropertyOverrides()[$class][$property] ?? $property), + new TwigFilter('propertyType', fn(array $property, array $spec = []): string => $this->getPropertyType($property, $spec)), + new TwigFilter('toMapValue', fn(array $property, string $resolvedName): string => $this->getToMapExpression($property, $resolvedName), ['is_safe' => ['html']]), + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -550,7 +520,7 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toPascalCase($enumKeys[$index]); @@ -573,13 +543,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return $enumName . '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => $enumName . '.' . $resolveKey($value), $values); return 'new List<' . $enumName . '> { ' . implode(', ', $items) . ' }'; } @@ -592,10 +560,6 @@ public function getFilters(): array /** * Get property type for request models - * - * @param array $property - * @param array $spec - * @return string */ protected function getPropertyType(array $property, array $spec = [], bool $fullyQualified = true): string { @@ -621,10 +585,11 @@ protected function getPropertyType(array $property, array $spec = [], bool $full * get sub_scheme and property_name functions * @return TwigFunction[] */ + #[Override] public function getFunctions(): array { return [ - new TwigFunction('sub_schema', function (array $property) { + new TwigFunction('sub_schema', function (array $property): string { $result = $this->getPropertyType($property, [], false); if (!($property['required'] ?? true)) { @@ -633,17 +598,12 @@ public function getFunctions(): array return $result; }, ['is_safe' => ['html']]), - new TwigFunction('property_name', function (array $definition, array $property) { - return $this->getPropertyName($definition, $property); - }), + new TwigFunction('property_name', fn(array $definition, array $property): string => $this->getPropertyName($definition, $property)), ]; } /** * Generate property name for C# model - * - * @param array $property - * @return string */ protected function getPropertyName(array $definition, array $property): string { @@ -679,9 +639,7 @@ protected function getTypeIdentifier(string $value): string * filter never derives the name itself, so there is no risk of drift * between the declared property and the ToMap() reference. * - * @param array $property * @param string $resolvedName C# identifier already produced by the template - * @return string */ protected function getToMapExpression(array $property, string $resolvedName): string { @@ -740,7 +698,7 @@ private function formatCSharpValue($value, int $indentLevel = 0): string if (array_keys($value) !== range(0, count($value) - 1)) { return $this->formatCSharpAnonymousObject($value, $indentLevel); } else { - $items = array_map(fn($item) => $this->formatCSharpValue($item, $indentLevel), $value); + $items = array_map(fn($item): string => $this->formatCSharpValue($item, $indentLevel), $value); return 'new[] { ' . implode(', ', $items) . ' }'; } } else { diff --git a/src/SDK/Language/Flutter.php b/src/SDK/Language/Flutter.php index a17254a332..a9c4e29331 100644 --- a/src/SDK/Language/Flutter.php +++ b/src/SDK/Language/Flutter.php @@ -1,27 +1,28 @@ 'packageName', ]; - /** - * @return string - */ + #[Override] public function getName(): string { return 'Flutter'; } - /** - * @return array - */ + #[Override] public function getFiles(): array { return [ diff --git a/src/SDK/Language/Go.php b/src/SDK/Language/Go.php index 85c206358f..b1273a630c 100644 --- a/src/SDK/Language/Go.php +++ b/src/SDK/Language/Go.php @@ -2,14 +2,12 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; class Go extends Language { - /** - * @return string - */ public function getName(): string { return 'Go'; @@ -17,8 +15,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -59,17 +55,11 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -206,9 +196,7 @@ public function getFiles(): array } /** - * @param array $parameter * @param array $nestedTypes - * @return string */ public function getTypeName(array $parameter, array $spec = []): string { @@ -240,10 +228,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -297,11 +281,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -338,18 +317,18 @@ public function getParamExample(array $param, string $lang = ''): string $output .= $example; break; case self::TYPE_ARRAY: - if (\str_starts_with($example, '[')) { - $example = \substr($example, 1); + if (\str_starts_with((string) $example, '[')) { + $example = \substr((string) $example, 1); } - if (\str_ends_with($example, ']')) { - $example = \substr($example, 0, -1); + if (\str_ends_with((string) $example, ']')) { + $example = \substr((string) $example, 0, -1); } $output .= $this->getTypeName($param) . '{' . $example . '}'; break; case self::TYPE_OBJECT: $output .= ($example === '{}') ? 'map[string]interface{}{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? 'map[string]interface{}' . preg_replace('/\n/', "\n ", $formatted) : 'map[string]interface{}' . $example); break; @@ -368,10 +347,11 @@ public function getParamExample(array $param, string $lang = ''): string return $output; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('godocComment', function ($value, $indent = 0) { + new TwigFilter('godocComment', function ($value, $indent = 0): string { $value = trim($value); $value = explode("\n", $value); $indent = \str_repeat(' ', $indent); @@ -380,18 +360,10 @@ public function getFilters(): array } return implode("\n" . $indent, $value); }, ['is_safe' => ['html']]), - new TwigFilter('propertyType', function (array $property, array $spec, string $generic = 'map[string]interface{}') { - return $this->getPropertyType($property, $spec, $generic); - }), - new TwigFilter('returnType', function (array $method, array $spec, string $namespace, string $generic = 'map[string]interface{}') { - return $this->getReturnType($method, $spec, $namespace, $generic); - }), - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toUpperSnakeCase($value); - }), - new TwigFilter('goPackagePath', function (array $sdk) { - return $this->getPackagePath($sdk); - }), + new TwigFilter('propertyType', fn(array $property, array $spec, string $generic = 'map[string]interface{}'): string => $this->getPropertyType($property, $spec, $generic)), + new TwigFilter('returnType', fn(array $method, array $spec, string $namespace, string $generic = 'map[string]interface{}'): string => $this->getReturnType($method, $spec, $namespace, $generic)), + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toUpperSnakeCase($value)), + new TwigFilter('goPackagePath', fn(array $sdk): string => $this->getPackagePath($sdk)), ]; } @@ -459,7 +431,7 @@ protected function getReturnType(array $method, array $spec, string $namespace, return 'interface{}'; } - $ret = ucfirst($method['responseModel']); + $ret = ucfirst((string) $method['responseModel']); return 'models.' . $ret; } diff --git a/src/SDK/Language/GraphQL.php b/src/SDK/Language/GraphQL.php index 19b581e9b6..12294dd644 100644 --- a/src/SDK/Language/GraphQL.php +++ b/src/SDK/Language/GraphQL.php @@ -4,9 +4,6 @@ class GraphQL extends HTTP { - /** - * @return string - */ public function getName(): string { return 'GraphQL'; @@ -29,7 +26,6 @@ public function getArrayOf(string $elements): string /** * @param $type - * @return string */ public function getTypeName(array $parameter, array $spec = []): string { @@ -71,10 +67,6 @@ public function getTypeName(array $parameter, array $spec = []): string return $type; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -126,11 +118,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -154,14 +141,11 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '"{}"' - : '"' . str_replace('"', '\\"', json_encode(json_decode($example, true))) . '"', + : '"' . str_replace('"', '\\"', json_encode(json_decode((string) $example, true))) . '"', self::TYPE_STRING => '"' . $example . '"', }; } - /** - * @return array - */ public function getFiles(): array { return [ diff --git a/src/SDK/Language/HTTP.php b/src/SDK/Language/HTTP.php index f1abcc4a95..9474cf318b 100644 --- a/src/SDK/Language/HTTP.php +++ b/src/SDK/Language/HTTP.php @@ -1,5 +1,7 @@ 'packageName', 'bowerPackage' => 'packageName', ]; /** - * @param string $name * @return $this */ public function setNPMPackage(string $name): self @@ -24,7 +25,6 @@ public function setNPMPackage(string $name): self } /** - * @param string $name * @return $this */ public function setBowerPackage(string $name): self @@ -36,8 +36,6 @@ public function setBowerPackage(string $name): self /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -111,18 +109,13 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; } /** - * @param array $parameter * @param array $nestedTypes - * @return string */ public function getTypeName(array $parameter, array $spec = []): string { @@ -132,7 +125,7 @@ public function getTypeName(array $parameter, array $spec = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return $enumType . '[]'; } @@ -141,7 +134,7 @@ public function getTypeName(array $parameter, array $spec = []): string return \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); + return \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return $this->toPascalCase($parameter['array']['model']) . '[]'; @@ -170,10 +163,6 @@ public function getTypeName(array $parameter, array $spec = []): string return $parameter['type']; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -223,13 +212,12 @@ public function getParamDefault(array $param): string return $output; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toPascalCase($value); - }), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toPascalCase($value)), + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -241,7 +229,7 @@ public function getFilters(): array $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; $prefix = $this->getPermissionPrefix(); - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toPascalCase($enumKeys[$index]); @@ -264,13 +252,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $prefix, $resolveKey) { - return $prefix . $enumName . '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => $prefix . $enumName . '.' . $resolveKey($value), $values); return '[' . implode(', ', $items) . ']'; } diff --git a/src/SDK/Language/Kotlin.php b/src/SDK/Language/Kotlin.php index 8e5f1a065a..d35c53f5da 100644 --- a/src/SDK/Language/Kotlin.php +++ b/src/SDK/Language/Kotlin.php @@ -2,14 +2,12 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; class Kotlin extends Language { - /** - * @return string - */ public function getName(): string { return 'Kotlin'; @@ -17,8 +15,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -91,9 +87,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; @@ -114,11 +107,6 @@ public function getArrayOf(string $elements): string return 'listOf(' . $elements . ')'; } - /** - * @param array $parameter - * @param array $spec - * @return string - */ public function getTypeName(array $parameter, array $spec = []): string { if ( @@ -127,7 +115,7 @@ public function getTypeName(array $parameter, array $spec = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return 'List'; } @@ -136,7 +124,7 @@ public function getTypeName(array $parameter, array $spec = []): string return 'io.appwrite.enums.' . \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return 'io.appwrite.enums.' . \ucfirst($parameter['name']); + return 'io.appwrite.enums.' . \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return 'ListtoPascalCase($parameter['array']['model']) . '>'; @@ -162,10 +150,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -222,9 +206,7 @@ public function getParamDefault(array $param): string } /** - * @param array $param * @param string $lang Language variant: 'kotlin' (default) or 'java' - * @return string */ public function getParamExample(array $param, string $lang = 'kotlin'): string { @@ -260,7 +242,7 @@ public function getParamExample(array $param, string $lang = 'kotlin'): string } else { switch ($type) { case self::TYPE_OBJECT: - $decoded = json_decode($example, true); + $decoded = json_decode((string) $example, true); if ($decoded && is_array($decoded)) { if ($lang === 'java') { $output .= $this->getJavaMapExample($decoded); @@ -300,9 +282,7 @@ public function getParamExample(array $param, string $lang = 'kotlin'): string /** * Generate Kotlin-style map initialization * - * @param array $data * @param int $indentLevel Indentation level for nested maps - * @return string */ protected function getKotlinMapExample(array $data, int $indentLevel = 0): string { @@ -342,9 +322,7 @@ protected function getKotlinMapExample(array $data, int $indentLevel = 0): strin /** * Generate Java-style map initialization using Map.of() * - * @param array $data * @param int $indentLevel Indentation level for nested maps - * @return string */ protected function getJavaMapExample(array $data, int $indentLevel = 0): string { @@ -386,7 +364,6 @@ protected function getJavaMapExample(array $data, int $indentLevel = 0): string * * @param string $example Array example like '[1, 2, 3]' or '[{"key": "value"}]' * @param string $lang Language variant: 'kotlin' or 'java' - * @return string */ protected function getArrayExample(string $example, string $lang = 'kotlin'): string { @@ -398,29 +375,22 @@ protected function getArrayExample(string $example, string $lang = 'kotlin'): st if (is_array($item)) { // Check if it's an associative array (object) or indexed array (nested array) $isObject = !array_is_list($item); - if ($isObject) { // It's an object/map, convert it - if ($lang === 'java') { - $arrayItems[] = $this->getJavaMapExample($item); - } else { - $arrayItems[] = $this->getKotlinMapExample($item); - } + $arrayItems[] = $lang === 'java' ? $this->getJavaMapExample($item) : $this->getKotlinMapExample($item); } else { // It's a nested array, recursively convert it $arrayItems[] = $this->getArrayExample(json_encode($item), $lang); } - } else { + } elseif (is_string($item)) { // Primitive value - if (is_string($item)) { - $arrayItems[] = '"' . $item . '"'; - } elseif (is_bool($item)) { - $arrayItems[] = $item ? 'true' : 'false'; - } elseif (is_null($item)) { - $arrayItems[] = 'null'; - } else { - $arrayItems[] = (string)$item; - } + $arrayItems[] = '"' . $item . '"'; + } elseif (is_bool($item)) { + $arrayItems[] = $item ? 'true' : 'false'; + } elseif (is_null($item)) { + $arrayItems[] = 'null'; + } else { + $arrayItems[] = (string)$item; } } return $lang === 'java' @@ -445,8 +415,8 @@ protected function getArrayExample(string $example, string $lang = 'kotlin'): st * * @param string $example Permission string like '["read(\"any\")"]' * @param string $lang Language variant: 'kotlin' or 'java' - * @return string */ + #[Override] public function getPermissionExample(string $example, string $lang = 'kotlin'): string { $permissions = []; @@ -481,9 +451,6 @@ public function getPermissionExample(string $example, string $lang = 'kotlin'): return 'listOf(' . $permissionsString . ')'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -647,48 +614,31 @@ public function getFiles(): array ]; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('returnType', function (array $method, array $spec, string $namespace, string $generic = 'T') { - return $this->getReturnType($method, $spec, $namespace, $generic); - }), - new TwigFilter('modelType', function (array $property, array $spec, string $generic = 'T') { - return $this->getModelType($property, $spec, $generic); - }), - new TwigFilter('propertyType', function (array $property, array $spec, string $generic = 'T') { - return $this->getPropertyType($property, $spec, $generic); - }), - new TwigFilter('hasGenericType', function (string $model, array $spec) { - return $this->hasGenericType($model, $spec); - }), - new TwigFilter('caseEnumKey', function (string $value) { + new TwigFilter('returnType', fn(array $method, array $spec, string $namespace, string $generic = 'T'): string => $this->getReturnType($method, $spec, $namespace, $generic)), + new TwigFilter('modelType', fn(array $property, array $spec, string $generic = 'T'): string => $this->getModelType($property, $spec, $generic)), + new TwigFilter('propertyType', fn(array $property, array $spec, string $generic = 'T'): string => $this->getPropertyType($property, $spec, $generic)), + new TwigFilter('hasGenericType', fn(string $model, array $spec): string => $this->hasGenericType($model, $spec)), + new TwigFilter('caseEnumKey', function (string $value): string { if (isset($this->getIdentifierOverrides()[$value])) { $value = $this->getIdentifierOverrides()[$value]; } return $this->toUpperSnakeCase($value); }), - new TwigFilter('propertyAssignment', function (array $property, array $spec) { - return $this->getPropertyAssignment($property, $spec); - }), - new TwigFilter('javaParamExample', function (array $param) { - return $this->getParamExample($param, 'java'); - }, ['is_safe' => ['html']]), - new TwigFilter('enumExample', function (array $param, string $lang = 'kotlin') { - return $this->getEnumExample($param, $lang); - }), - new TwigFilter('javaEnumExample', function (array $param) { - return $this->getEnumExample($param, 'java'); - }), + new TwigFilter('propertyAssignment', fn(array $property, array $spec): string => $this->getPropertyAssignment($property, $spec)), + new TwigFilter('javaParamExample', fn(array $param): string => $this->getParamExample($param, 'java'), ['is_safe' => ['html']]), + new TwigFilter('enumExample', fn(array $param, string $lang = 'kotlin'): string => $this->getEnumExample($param, $lang)), + new TwigFilter('javaEnumExample', fn(array $param): string => $this->getEnumExample($param, 'java')), ]; } /** * Generate enum example for Kotlin/Java * - * @param array $param * @param string $lang 'kotlin' or 'java' - * @return string */ protected function getEnumExample(array $param, string $lang = 'kotlin'): string { @@ -702,7 +652,7 @@ protected function getEnumExample(array $param, string $lang = 'kotlin'): string $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toUpperSnakeCase($enumKeys[$index]); @@ -725,13 +675,11 @@ protected function getEnumExample(array $param, string $lang = 'kotlin'): string $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return $enumName . '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => $enumName . '.' . $resolveKey($value), $values); $listOf = $lang === 'java' ? 'List.of' : 'listOf'; return $listOf . '(' . implode(', ', $items) . ')'; @@ -768,7 +716,7 @@ protected function getReturnType(array $method, array $spec, string $namespace, $ret = $this->toPascalCase($method['responseModel']); - if ($this->hasGenericType($method['responseModel'], $spec)) { + if ($this->hasGenericType($method['responseModel'], $spec) !== '' && $this->hasGenericType($method['responseModel'], $spec) !== '0') { $ret .= '<' . $generic . '>'; } @@ -777,7 +725,7 @@ protected function getReturnType(array $method, array $spec, string $namespace, protected function getModelType(array $definition, array $spec, string $generic = 'T'): string { - if ($this->hasGenericType($definition['name'], $spec)) { + if ($this->hasGenericType($definition['name'], $spec) !== '' && $this->hasGenericType($definition['name'], $spec) !== '0') { return $this->toPascalCase($definition['name']) . '<' . $generic . '>'; } return $this->toPascalCase($definition['name']); @@ -788,7 +736,7 @@ protected function getPropertyType(array $property, array $spec, string $generic if (\array_key_exists('sub_schema', $property)) { $type = $this->toPascalCase($property['sub_schema']); - if ($this->hasGenericType($property['sub_schema'], $spec)) { + if ($this->hasGenericType($property['sub_schema'], $spec) !== '' && $this->hasGenericType($property['sub_schema'], $spec) !== '0') { $type .= '<' . $generic . '>'; } @@ -797,7 +745,7 @@ protected function getPropertyType(array $property, array $spec, string $generic } } elseif (isset($property['enum'])) { $enumName = $property['enumName'] ?? $property['name']; - $type = \ucfirst($enumName); + $type = \ucfirst((string) $enumName); } else { $type = $this->getTypeName($property); } @@ -834,10 +782,6 @@ protected function hasGenericType(?string $model, array $spec): string /** * Generate property assignment logic for model deserialization - * - * @param array $property - * @param array $spec - * @return string */ protected function getPropertyAssignment(array $property, array $spec): string { @@ -849,7 +793,7 @@ protected function getPropertyAssignment(array $property, array $spec): string if (isset($property['sub_schema']) && !empty($property['sub_schema'])) { $subSchemaClass = $this->toPascalCase($property['sub_schema']); $hasGenericType = $this->hasGenericType($property['sub_schema'], $spec); - $nestedTypeParam = $hasGenericType ? ', nestedType' : ''; + $nestedTypeParam = $hasGenericType !== '' && $hasGenericType !== '0' ? ', nestedType' : ''; if ($property['type'] === 'array') { return "($mapKey as List>).map { " . @@ -883,12 +827,12 @@ protected function getPropertyAssignment(array $property, array $spec): string if ($property['type'] === 'integer') { return "($mapKey as$nullableModifier Number)" . - ($nullableModifier ? '?' : '') . '.toLong()'; + ($nullableModifier !== '' && $nullableModifier !== '0' ? '?' : '') . '.toLong()'; } if ($property['type'] === 'number') { return "($mapKey as$nullableModifier Number)" . - ($nullableModifier ? '?' : '') . '.toDouble()'; + ($nullableModifier !== '' && $nullableModifier !== '0' ? '?' : '') . '.toDouble()'; } // Handle other types (string, boolean, etc.) diff --git a/src/SDK/Language/Markdown.php b/src/SDK/Language/Markdown.php deleted file mode 100644 index 91a3c4e14b..0000000000 --- a/src/SDK/Language/Markdown.php +++ /dev/null @@ -1,272 +0,0 @@ - 'copy', - 'destination' => '.github/workflows/publish.yml', - 'template' => 'markdown/.github/workflows/publish.yml', - ], - // Package configuration - [ - 'scope' => 'default', - 'destination' => 'package.json', - 'template' => 'markdown/package.json.twig', - ], - [ - 'scope' => 'copy', - 'destination' => '.npmrc', - 'template' => 'markdown/.npmrc', - ], - [ - 'scope' => 'default', - 'destination' => 'README.md', - 'template' => 'markdown/README.md.twig', - ], - [ - 'scope' => 'copy', - 'destination' => 'tsconfig.json', - 'template' => 'markdown/tsconfig.json', - ], - // Source files - [ - 'scope' => 'copy', - 'destination' => 'src/types.ts', - 'template' => 'markdown/src/types.ts', - ], - [ - 'scope' => 'copy', - 'destination' => 'src/index.ts', - 'template' => 'markdown/src/index.ts', - ], - // Manifest - [ - 'scope' => 'copy', - 'destination' => 'src/manifest.ts', - 'template' => 'markdown/src/manifest.ts', - ], - // Build scripts - [ - 'scope' => 'copy', - 'destination' => 'scripts/build-manifest.ts', - 'template' => 'markdown/scripts/build-manifest.ts', - ], - // Documentation markdown files - [ - 'scope' => 'method', - 'destination' => 'docs/typescript/{{ service.name | caseLower }}/{{ method.name | caseKebab }}.md', - 'template' => 'markdown/typescript/method.md.twig', - ], - ]; - } - - /** - * @param array $parameter - * @param array $method - * @return string - */ - public function getTypeName(array $parameter, array $method = []): string - { - // For TypeScript/JavaScript-like languages - if (isset($parameter['enumName'])) { - return \ucfirst($parameter['enumName']); - } - if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); - } - if (isset($parameter['items'])) { - $parameter['array'] = $parameter['items']; - } - switch ($parameter['type']) { - case self::TYPE_INTEGER: - case self::TYPE_NUMBER: - return 'number'; - case self::TYPE_ARRAY: - if (!empty(($parameter['array'] ?? [])['type']) && !\is_array($parameter['array']['type'])) { - return $this->getTypeName($parameter['array']) . '[]'; - } - return 'any[]'; - case self::TYPE_FILE: - return 'File'; - case self::TYPE_OBJECT: - return 'object'; - } - return $parameter['type']; - } - - /** - * @param array $param - * @return string - */ - public function getParamDefault(array $param): string - { - $type = $param['type'] ?? ''; - $default = $param['default'] ?? ''; - $required = $param['required'] ?? false; - - if ($required) { - return ''; - } - - if (array_key_exists('default', $param)) { - return ' = ' . $default; - } - - return match ($type) { - self::TYPE_ARRAY => ' = []', - self::TYPE_OBJECT => ' = {}', - default => ' = null', - }; - } - - /** - * @param array $param - * @param string $lang - * @return string - */ - public function getParamExample(array $param, string $lang = ''): string - { - $type = $param['type'] ?? ''; - $example = $param['example'] ?? ''; - - $hasExample = !empty($example) || $example === 0 || $example === false; - - if (!$hasExample) { - return match ($type) { - self::TYPE_ARRAY => '[]', - self::TYPE_FILE => 'file', - self::TYPE_INTEGER, self::TYPE_NUMBER => '0', - self::TYPE_BOOLEAN => 'false', - self::TYPE_OBJECT => '{}', - self::TYPE_STRING => "''", - }; - } - - return match ($type) { - self::TYPE_ARRAY => $this->isPermissionString($example) ? $this->getPermissionExample($example) : $example, - self::TYPE_INTEGER, self::TYPE_NUMBER => (string)$example, - self::TYPE_FILE => 'file', - self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', - self::TYPE_OBJECT => ($example === '{}') - ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) - ? $formatted - : $example), - self::TYPE_STRING => "'{$example}'", - }; - } - - public function getPermissionExample(string $example): string - { - $permissions = $this->extractPermissionParts($example); - $result = []; - - foreach ($permissions as $permission) { - $action = ucfirst($permission['action']); - $role = ucfirst($this->toCamelCase($permission['role'])); - - if ($permission['id'] !== null) { - if ($permission['innerRole'] !== null) { - $result[] = "Permission.{$action}(Role.{$role}('{$permission['id']}', '{$permission['innerRole']}'))"; - } else { - $result[] = "Permission.{$action}(Role.{$role}('{$permission['id']}'))"; - } - } else { - $result[] = "Permission.{$action}(Role.{$role}())"; - } - } - - return '[' . implode(', ', $result) . ']'; - } - - public function getFilters(): array - { - return [ - new TwigFilter('getPropertyType', function ($value, $method = []) { - return $this->getTypeName($value, $method); - }), - new TwigFilter('comment', function ($value) { - $value = explode("\n", $value); - foreach ($value as $key => $line) { - $value[$key] = wordwrap($line, 80, "\n"); - } - return implode("\n", $value); - }, ['is_safe' => ['html']]), - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toPascalCase($value); - }), - new TwigFilter('getResponseModel', function (array $method) { - if (!empty($method['responseModel']) && $method['responseModel'] !== 'any') { - return 'Models.' . \ucfirst($method['responseModel']); - } - return null; - }), - new TwigFilter('needsPermissionImport', function (array $parameters) { - foreach ($parameters as $param) { - if (($param['type'] ?? '') === self::TYPE_ARRAY) { - $example = $param['example'] ?? ''; - if ($this->isPermissionString($example)) { - return true; - } - } - } - return false; - }), - new TwigFilter('decodeHtmlEntities', function (string $value) { - return html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); - }), - ]; - } -} diff --git a/src/SDK/Language/Node.php b/src/SDK/Language/Node.php index 355ac34387..d867cb5218 100644 --- a/src/SDK/Language/Node.php +++ b/src/SDK/Language/Node.php @@ -2,36 +2,42 @@ namespace Appwrite\SDK\Language; +use Override; +use Twig\TwigFilter; + class Node extends Web { - /** - * @return string - */ + #[Override] public function getName(): string { return 'NodeJS'; } + #[Override] public function getStaticAccessOperator(): string { return '.'; } + #[Override] public function getStringQuote(): string { return "'"; } + #[Override] public function getArrayOf(string $elements): string { return '[' . $elements . ']'; } + #[Override] protected function getPermissionPrefix(): string { return 'sdk.'; } + #[Override] public function getTypeName(array $parameter, array $method = []): string { if (($parameter['type'] ?? null) === self::TYPE_ARRAY) { @@ -49,6 +55,7 @@ public function getTypeName(array $parameter, array $method = []): string return parent::getTypeName($parameter, $method); } + #[Override] public function getReturn(array $method, array $spec): string { if ($method['type'] === 'webAuth') { @@ -69,7 +76,7 @@ public function getReturn(array $method, array $spec): string $ret = 'Promise<'; if ( - array_key_exists($method['responseModel'], $spec['definitions']) && + array_key_exists((string) $method['responseModel'], $spec['definitions']) && array_key_exists('additionalProperties', $spec['definitions'][$method['responseModel']]) && !$spec['definitions'][$method['responseModel']]['additionalProperties'] ) { @@ -83,24 +90,18 @@ public function getReturn(array $method, array $spec): string $this->populateGenerics($method['responseModel'], $spec, $models); $models = array_unique($models); - $models = array_filter($models, fn ($model) => $model != $this->toPascalCase($method['responseModel'])); + $models = array_filter($models, fn ($model): bool => $model != $this->toPascalCase($method['responseModel'])); - if (!empty($models)) { + if ($models !== []) { $ret .= '<' . implode(', ', $models) . '>'; } - $ret .= '>'; - - return $ret; + return $ret . '>'; } return 'Promise<{}>'; } - /** - * @param array $param - * @param string $lang - * @return string - */ + #[Override] public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -124,7 +125,7 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? preg_replace('/\n/', "\n ", $formatted) : $example), self::TYPE_STRING => "'{$example}'", @@ -133,9 +134,6 @@ public function getParamExample(array $param, string $lang = ''): string /** * Check if service has any file parameters - * - * @param array $service - * @return bool */ public function hasFileParam(array $service): bool { @@ -149,21 +147,15 @@ public function hasFileParam(array $service): bool return false; } - /** - * @return array - */ + #[Override] public function getFilters(): array { return \array_merge(parent::getFilters(), [ - new \Twig\TwigFilter('hasFileParam', function ($service) { - return $this->hasFileParam($service); - }), + new TwigFilter('hasFileParam', fn(array $service): bool => $this->hasFileParam($service)), ]); } - /** - * @return array - */ + #[Override] public function getFiles(): array { return [ diff --git a/src/SDK/Language/PHP.php b/src/SDK/Language/PHP.php index 171a0a4575..3171da76e3 100644 --- a/src/SDK/Language/PHP.php +++ b/src/SDK/Language/PHP.php @@ -2,6 +2,7 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; @@ -10,13 +11,13 @@ class PHP extends Language /** * @var array */ + #[Override] protected $params = [ 'composerVendor' => 'vendor-name', 'composerPackage' => 'package-name', ]; /** - * @param string $name * @return $this */ public function setComposerVendor(string $name): self @@ -27,7 +28,6 @@ public function setComposerVendor(string $name): self } /** - * @param string $name * @return $this */ public function setComposerPackage(string $name): self @@ -37,9 +37,6 @@ public function setComposerPackage(string $name): self return $this; } - /** - * @return string - */ public function getName(): string { return 'PHP'; @@ -47,8 +44,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -122,9 +117,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return [ @@ -152,9 +144,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -305,9 +294,7 @@ public function getFiles(): array protected function normalizeNamespace(string $namespace): string { $segments = explode('\\', $namespace); - $segments = array_map(function ($segment) { - return $this->toPascalCase($segment); - }, $segments); + $segments = array_map($this->toPascalCase(...), $segments); return implode('\\', $segments); } @@ -335,7 +322,7 @@ protected function getResponseModels(array $method, array $spec): array $models[] = $this->getModelClassName($modelName, $spec, true); } - if (empty($models) && !empty($method['responseModel']) && $method['responseModel'] !== 'any') { + if ($models === [] && !empty($method['responseModel']) && $method['responseModel'] !== 'any') { $models[] = $this->getModelClassName($method['responseModel'], $spec, true); } @@ -343,9 +330,7 @@ protected function getResponseModels(array $method, array $spec): array } /** - * @param array $parameter * @param array $nestedTypes - * @return string */ public function getTypeName(array $parameter, array $spec = []): string { @@ -360,7 +345,7 @@ public function getTypeName(array $parameter, array $spec = []): string return $this->applyIdentifierOverride(\ucfirst($parameter['enumName'])); } if (!empty($parameter['enumValues'])) { - return $this->applyIdentifierOverride(\ucfirst($parameter['name'])); + return $this->applyIdentifierOverride(\ucfirst((string) $parameter['name'])); } if (!empty($parameter['array']['model'])) { return 'array'; @@ -385,10 +370,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -424,7 +405,7 @@ public function getParamDefault(array $param): string $output .= $default; break; case self::TYPE_OBJECT: - $output .= $this->jsonToAssoc(json_decode($default, true)); + $output .= $this->jsonToAssoc(json_decode((string) $default, true)); break; case self::TYPE_BOOLEAN: $output .= ($default) ? 'true' : 'false'; @@ -438,11 +419,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -478,7 +454,7 @@ public function getParamExample(array $param, string $lang = ''): string $output .= $this->isPermissionString($example) ? $this->getPermissionExample($example) : $example; break; case self::TYPE_OBJECT: - $output .= $this->jsonToAssoc(json_decode($example, true)); + $output .= $this->jsonToAssoc(json_decode((string) $example, true)); break; case self::TYPE_BOOLEAN: $output .= ($example) ? 'true' : 'false'; @@ -502,7 +478,7 @@ public function getParamExample(array $param, string $lang = ''): string */ protected function jsonToAssoc(array $data, int $indent = 0): string { - if (empty($data)) { + if ($data === []) { return '[]'; } @@ -530,9 +506,7 @@ protected function jsonToAssoc(array $data, int $indent = 0): string $output .= ' ' . $itemIndent . '\'' . $key . '\' => ' . $value . $comma . "\n"; } - $output .= $baseIndent . ' ]'; - - return $output; + return $output . ($baseIndent . ' ]'); } protected function getMockDefinitionPayload(string $definitionName, array $spec, int $indentLevel = 2): string @@ -552,10 +526,10 @@ protected function getMockDefinitionPayload(string $definitionName, array $spec, $properties = array_values(array_filter( $definition['properties'] ?? [], - fn (array $property) => (bool)($property['required'] ?? false) + fn (array $property): bool => (bool)($property['required'] ?? false) )); - if (empty($properties)) { + if ($properties === []) { return 'array()'; } @@ -629,7 +603,7 @@ protected function formatPhpLiteral(mixed $value): string } if (is_array($value)) { - return empty($value) ? 'array()' : var_export($value, true); + return $value === [] ? 'array()' : var_export($value, true); } return (string)$value; @@ -639,9 +613,8 @@ protected function escapePhpString(string $value): string { $value = str_replace('\\', '\\\\', $value); $value = str_replace('"', '\\"', $value); - $value = str_replace('$', '\\$', $value); - return $value; + return str_replace('$', '\\$', $value); } protected function getReturn(array $method, array $spec = []): string @@ -652,7 +625,7 @@ protected function getReturn(array $method, array $spec = []): string $responseModels = $this->getResponseModels($method, $spec); - if (!empty($responseModels)) { + if ($responseModels !== []) { return implode('|', $responseModels); } @@ -661,9 +634,6 @@ protected function getReturn(array $method, array $spec = []): string /** * Generate method parameters string for PHP method signatures - * - * @param array $method - * @return string */ protected function getMethodParameters(array $method): string { @@ -675,7 +645,7 @@ protected function getMethodParameters(array $method): string $typeName = $this->getTypeName($parameter); $paramName = '$' . $this->escapeKeyword($this->toCamelCase($parameter['name'] ?? '')); - $default = !($parameter['required'] ?? true) ? ' = null' : ''; + $default = $parameter['required'] ?? true ? '' : ' = null'; $params[] = $nullablePrefix . $typeName . ' ' . $paramName . $default; } @@ -690,58 +660,39 @@ protected function getMethodParameters(array $method): string return $result; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('getReturn', function ($value, array $spec = []) { - return $this->getReturn($value, $spec); - }), - new TwigFilter('getResponseModels', function ($value, array $spec = []) { - return $this->getResponseModels($value, $spec); - }), - new TwigFilter('mockDefinitionPayload', function (string $definitionName, array $spec, int $indentLevel = 2) { - return $this->getMockDefinitionPayload($definitionName, $spec, $indentLevel); - }, ['is_safe' => ['html']]), - new TwigFilter('methodParameters', function ($value) { - return $this->getMethodParameters($value); - }), - new TwigFilter('deviceInfo', function ($value) { - return php_uname('s') . '; ' . php_uname('v') . '; ' . php_uname('m'); - }), - new TwigFilter('caseEnumKey', function (string $value) { + new TwigFilter('getReturn', fn(array $value, array $spec = []): string => $this->getReturn($value, $spec)), + new TwigFilter('getResponseModels', fn(array $value, array $spec = []): array => $this->getResponseModels($value, $spec)), + new TwigFilter('mockDefinitionPayload', fn(string $definitionName, array $spec, int $indentLevel = 2): string => $this->getMockDefinitionPayload($definitionName, $spec, $indentLevel), ['is_safe' => ['html']]), + new TwigFilter('methodParameters', fn(array $value): string => $this->getMethodParameters($value)), + new TwigFilter('deviceInfo', fn($value): string => php_uname('s') . '; ' . php_uname('v') . '; ' . php_uname('m')), + new TwigFilter('caseEnumKey', function (string $value): string { if (isset($this->getIdentifierOverrides()[$value])) { $value = $this->getIdentifierOverrides()[$value]; } $value = \preg_replace('/[^a-zA-Z0-9]/', '', $value); return $this->toUpperSnakeCase($value); }), - new TwigFilter('hasBearerAuth', function (array $headers) { - foreach ($headers as $header) { - if (isset($header['type']) && $header['type'] === 'bearer') { - return true; - } - } - return false; - }), - new TwigFilter('caseNamespace', function ($value) { + new TwigFilter('hasBearerAuth', fn(array $headers): bool => array_any($headers, fn($header): bool => isset($header['type']) && $header['type'] === 'bearer')), + new TwigFilter('caseNamespace', function ($value): string { $segments = explode('\\', $value); - $segments = array_map(function ($segment) { - return $this->toPascalCase($segment); - }, $segments); + $segments = array_map($this->toPascalCase(...), $segments); return implode('\\', $segments); }), - new TwigFilter('caseNamespacePath', function ($value) { + new TwigFilter('caseNamespacePath', function ($value): string { $segments = explode('\\', $value); - $segments = array_map(function ($segment) { - return $this->toPascalCase($segment); - }, $segments); + $segments = array_map($this->toPascalCase(...), $segments); return implode('/', $segments); }), - new TwigFilter('escapeJson', function ($value) { + new TwigFilter( + 'escapeJson', // Escape backslashes for JSON strings - return str_replace('\\', '\\\\', $value); - }), - new TwigFilter('enumExample', function (array $param) { + fn($value): string|array => str_replace('\\', '\\\\', $value) + ), + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -752,14 +703,14 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { - $cleaned = \preg_replace('/[^a-zA-Z0-9]/', '', $enumKeys[$index]); + $cleaned = \preg_replace('/[^a-zA-Z0-9]/', '', (string) $enumKeys[$index]); return $this->toUpperSnakeCase($cleaned); } if ($index !== false && isset($enumValues[$index])) { - $cleaned = \preg_replace('/[^a-zA-Z0-9]/', '', $enumValues[$index]); + $cleaned = \preg_replace('/[^a-zA-Z0-9]/', '', (string) $enumValues[$index]); return $this->toUpperSnakeCase($cleaned); } $fallback = $enumKeys[0] ?? $enumValues[0] ?? $value; @@ -778,13 +729,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return $enumName . '::' . $resolveKey($value) . '()'; - }, $values); + $items = array_map(fn($value): string => $enumName . '::' . $resolveKey($value) . '()', $values); return '[' . implode(', ', $items) . ']'; } diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index 195bc27cf1..ca9ef11863 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -2,18 +2,20 @@ namespace Appwrite\SDK\Language; +use stdClass; +use Override; use Appwrite\SDK\Language; use Exception; use Twig\TwigFilter; class Python extends Language { + #[Override] protected $params = [ 'pipPackage' => 'packageName', ]; /** - * @param string $name * @return $this */ public function setPipPackage(string $name): self @@ -23,9 +25,6 @@ public function setPipPackage(string $name): self return $this; } - /** - * @return string - */ public function getName(): string { return 'Python'; @@ -33,8 +32,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -76,9 +73,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; @@ -99,9 +93,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -314,8 +305,6 @@ public function getFiles(): array } /** - * @param array $parameter - * @return string * @throws Exception */ public function getTypeName(array $parameter, array $spec = []): string @@ -328,13 +317,13 @@ public function getTypeName(array $parameter, array $spec = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); $typeName = 'List[' . $enumType . ']'; } elseif (isset($parameter['enumName'])) { $typeName = \ucfirst($parameter['enumName']); } elseif (!empty($parameter['enumValues'])) { - $typeName = \ucfirst($parameter['name']); + $typeName = \ucfirst((string) $parameter['name']); } elseif (!empty($parameter['array']['model'])) { $typeName = 'List[' . $this->toPascalCase($parameter['array']['model']) . ']'; } elseif (!empty($parameter['model'])) { @@ -378,10 +367,6 @@ public function getTypeName(array $parameter, array $spec = []): string return $typeName; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -435,11 +420,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -463,7 +443,7 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'True' : 'False', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? preg_replace('/\n/', "\n ", str_replace(['true', 'false'], ['True', 'False'], $formatted)) : $example), self::TYPE_STRING => "'{$example}'", @@ -552,7 +532,7 @@ protected function getUnionType(array $types): string { $types = array_values(array_unique(array_filter($types))); - if (empty($types)) { + if ($types === []) { return 'Any'; } @@ -612,7 +592,7 @@ protected function getModelPropertyType(array $property, string $ownerName = '') if (!empty($property['sub_schemas'])) { $unionType = $this->getUnionType(array_map( - fn($schema) => $this->getModelName($schema), + $this->getModelName(...), $property['sub_schemas'] )); @@ -696,9 +676,9 @@ protected function getResponseType(array $method, string $serviceName = ''): str } if (!empty($method['responseModels']) && count($method['responseModels']) > 1) { - $types = array_map(fn($model) => $this->getDocsModelTypeName($model, $serviceName), array_filter( + $types = array_map(fn(string $model): string => $this->getDocsModelTypeName($model, $serviceName), array_filter( $method['responseModels'], - fn($model) => !empty($model) && $model !== 'any' + fn($model): bool => !empty($model) && $model !== 'any' )); return $this->getUnionType($types); @@ -713,10 +693,6 @@ protected function getResponseType(array $method, string $serviceName = ''): str /** * Check if a model or any of its sub-schemas has additionalProperties - * - * @param string|null $model - * @param array $spec - * @return bool */ protected function hasGenericType(?string $model, array $spec): bool { @@ -748,7 +724,6 @@ protected function hasGenericType(?string $model, array $spec): bool * Creates an example for a response model with the given name * * @param string $model - * @param array $spec * @return string */ protected function getResponseModelExample(?string $model, array $spec): mixed @@ -766,8 +741,8 @@ protected function getResponseModelExample(?string $model, array $spec): mixed } $result[$property['name']] = match ($property['type']) { - 'object' => (array_key_exists('sub_schema', $property) && $property['sub_schema']) ? ((object) $this->getResponseModelExample($property['sub_schema'], $spec)) : new \stdClass(), - 'array' => array(), + 'object' => (array_key_exists('sub_schema', $property) && $property['sub_schema']) ? ((object) $this->getResponseModelExample($property['sub_schema'], $spec)) : new stdClass(), + 'array' => [], 'string' => $property['example'] ?? '', 'boolean' => true, 'float' => (float) $property['example'], @@ -779,45 +754,25 @@ protected function getResponseModelExample(?string $model, array $spec): mixed return (object) $result; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toUpperSnakeCase($value); - }), - new TwigFilter('getPropertyType', function ($value, $method = []) { - return $this->getTypeName($value, $method); - }), - new TwigFilter('hasGenericType', function (string $model, array $spec) { - return $this->hasGenericType($model, $spec); - }), - new TwigFilter('hasGenericTypeProperty', function (array $properties, array $spec) { - foreach ($properties as $property) { - if (!empty($property['sub_schema']) && $this->hasGenericType($property['sub_schema'], $spec)) { - return true; - } - } - return false; - }), - new TwigFilter('getServicePropertyType', function (array $value, string $serviceName) { - return $this->getServicePropertyType($value, $serviceName); - }), - new TwigFilter('getModelPropertyType', function (array $value, string $ownerName = '') { - return $this->getModelPropertyType($value, $ownerName); - }), - new TwigFilter('getModelFieldName', function (array $value, array $properties) { - return $this->getModelFieldName($value, $properties); - }), - new TwigFilter('getResponseType', function (array $method, string $serviceName = '') { - return $this->getResponseType($method, $serviceName); - }), - new TwigFilter('formatParamValue', function (string $paramName, string $paramType, bool $isMultipartFormData) { + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toUpperSnakeCase($value)), + new TwigFilter('getPropertyType', fn(array $value, array $method = []): string => $this->getTypeName($value, $method)), + new TwigFilter('hasGenericType', fn(string $model, array $spec): bool => $this->hasGenericType($model, $spec)), + new TwigFilter('hasGenericTypeProperty', fn(array $properties, array $spec): bool => array_any($properties, fn($property): bool => !empty($property['sub_schema']) && $this->hasGenericType($property['sub_schema'], $spec))), + new TwigFilter('getServicePropertyType', fn(array $value, string $serviceName): string => $this->getServicePropertyType($value, $serviceName)), + new TwigFilter('getModelPropertyType', fn(array $value, string $ownerName = ''): string => $this->getModelPropertyType($value, $ownerName)), + new TwigFilter('getModelFieldName', fn(array $value, array $properties): string => $this->getModelFieldName($value, $properties)), + new TwigFilter('getResponseType', fn(array $method, string $serviceName = ''): string => $this->getResponseType($method, $serviceName)), + new TwigFilter('formatParamValue', function (string $paramName, string $paramType, bool $isMultipartFormData): string { if ($isMultipartFormData && $paramType === self::TYPE_BOOLEAN) { return "str({$paramName}).lower() if type({$paramName}) is bool else {$paramName}"; } return $paramName; }), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -828,7 +783,7 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toUpperSnakeCase($enumKeys[$index]); @@ -851,13 +806,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return $enumName . '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => $enumName . '.' . $resolveKey($value), $values); return '[' . implode(', ', $items) . ']'; } @@ -865,10 +818,8 @@ public function getFilters(): array $value = ($example !== null && $example !== '') ? $example : $enumValues[0]; return $enumName . '.' . $resolveKey($value); }), - new TwigFilter('requestModelExample', function (array $parameter, array $spec, string $serviceName = '') { - return $this->getRequestModelExample($parameter, $spec, $serviceName); - }), - new TwigFilter('responseModelExample', function (string $model, array $spec) { + new TwigFilter('requestModelExample', fn(array $parameter, array $spec, string $serviceName = ''): string => $this->getRequestModelExample($parameter, $spec, $serviceName)), + new TwigFilter('responseModelExample', function (string $model, array $spec): string { $result = $this->getResponseModelExample($model, $spec); $json = json_encode($result, JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION); diff --git a/src/SDK/Language/REST.php b/src/SDK/Language/REST.php index c83e1ec734..8c02148a7d 100644 --- a/src/SDK/Language/REST.php +++ b/src/SDK/Language/REST.php @@ -4,9 +4,6 @@ class REST extends HTTP { - /** - * @return string - */ public function getName(): string { return 'REST'; @@ -27,10 +24,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -82,11 +75,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -108,10 +96,10 @@ public function getParamExample(array $param, string $lang = ''): string return match ($type) { self::TYPE_ARRAY => (function () use ($example) { // If array of strings, make sure any sub-strings are escaped - if (\substr($example, 1, 1) === '"') { - $start = \substr($example, 0, 2); - $end = \substr($example, -2); - $contents = \substr($example, 2, -2); + if (\substr((string) $example, 1, 1) === '"') { + $start = \substr((string) $example, 0, 2); + $end = \substr((string) $example, -2); + $contents = \substr((string) $example, 2, -2); $contents = \addslashes($contents); return $start . $contents . $end; } else { @@ -122,12 +110,12 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) - ? (function () use ($formatted) { + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) + ? (function () use ($formatted): string|array|null { // Replace leading four spaces with two spaces for indentation $formatted = preg_replace('/^ /m', ' ', $formatted); // Add two spaces before the closing brace if it's on a new line at the end - $formatted = preg_replace('/\n(?=[^}]*}$)/', "\n ", $formatted); + $formatted = preg_replace('/\n(?=[^}]*}$)/', "\n ", (string) $formatted); return $formatted; })() : $example), @@ -135,9 +123,6 @@ public function getParamExample(array $param, string $lang = ''): string }; } - /** - * @return array - */ public function getFiles(): array { return [ diff --git a/src/SDK/Language/ReactNative.php b/src/SDK/Language/ReactNative.php index e65439face..b49ddd4c44 100644 --- a/src/SDK/Language/ReactNative.php +++ b/src/SDK/Language/ReactNative.php @@ -2,21 +2,18 @@ namespace Appwrite\SDK\Language; +use Override; use Twig\TwigFilter; class ReactNative extends Web { - /** - * @return string - */ + #[Override] public function getName(): string { return 'ReactNative'; } - /** - * @return array - */ + #[Override] public function getFiles(): array { return [ @@ -153,11 +150,7 @@ public function getFiles(): array ]; } - /** - * @param array $parameter - * @param array $method - * @return string - */ + #[Override] public function getTypeName(array $parameter, array $method = []): string { if (($parameter['type'] ?? '') === self::TYPE_FILE) { @@ -167,11 +160,7 @@ public function getTypeName(array $parameter, array $method = []): string return parent::getTypeName($parameter, $method); } - /** - * @param array $param - * @param string $lang - * @return string - */ + #[Override] public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -195,13 +184,14 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? preg_replace('/\n/', "\n ", $formatted) : $example), self::TYPE_STRING => "'{$example}'", }; } + #[Override] public function getReturn(array $method, array $spec): string { if ($method['type'] === 'webAuth') { @@ -220,7 +210,7 @@ public function getReturn(array $method, array $spec): string $ret = 'Promise<'; if ( - array_key_exists($method['responseModel'], $spec['definitions']) && + array_key_exists((string) $method['responseModel'], $spec['definitions']) && array_key_exists('additionalProperties', $spec['definitions'][$method['responseModel']]) && !$spec['definitions'][$method['responseModel']]['additionalProperties'] ) { @@ -234,15 +224,13 @@ public function getReturn(array $method, array $spec): string $this->populateGenerics($method['responseModel'], $spec, $models); $models = array_unique($models); - $models = array_filter($models, fn ($model) => $model != $this->toPascalCase($method['responseModel'])); + $models = array_filter($models, fn ($model): bool => $model != $this->toPascalCase($method['responseModel'])); - if (!empty($models)) { + if ($models !== []) { $ret .= '<' . implode(', ', $models) . '>'; } - $ret .= '>'; - - return $ret; + return $ret . '>'; } return 'Promise<{}>'; } diff --git a/src/SDK/Language/Ruby.php b/src/SDK/Language/Ruby.php index 698b4250ad..b61706db3f 100644 --- a/src/SDK/Language/Ruby.php +++ b/src/SDK/Language/Ruby.php @@ -2,17 +2,18 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; class Ruby extends Language { + #[Override] protected $params = [ 'gemPackage' => 'gemName', ]; /** - * @param string $name * @return $this */ public function setGemPackage(string $name): self @@ -22,9 +23,6 @@ public function setGemPackage(string $name): self return $this; } - /** - * @return string - */ public function getName(): string { return 'Ruby'; @@ -32,8 +30,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -78,9 +74,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return []; @@ -101,9 +94,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -216,9 +206,7 @@ public function getFiles(): array } /** - * @param array $parameter * @param array $nestedTypes - * @return string */ public function getTypeName(array $parameter, array $spec = []): string { @@ -233,7 +221,7 @@ public function getTypeName(array $parameter, array $spec = []): string return \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); + return \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return 'Array'; @@ -252,10 +240,6 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -305,11 +289,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -347,7 +326,7 @@ public function getParamExample(array $param, string $lang = ''): string $output .= $this->isPermissionString($example) ? $this->getPermissionExample($example) : $example; break; case self::TYPE_OBJECT: - $output .= $this->jsonToHash(json_decode($example, true)); + $output .= $this->jsonToHash(json_decode((string) $example, true)); break; case self::TYPE_BOOLEAN: $output .= ($example) ? 'true' : 'false'; @@ -367,12 +346,11 @@ public function getParamExample(array $param, string $lang = ''): string /** * Converts JSON Object To Ruby Native Hash * - * @return string * @var $data array */ protected function jsonToHash(array $data, int $indent = 0): string { - if (empty($data)) { + if ($data === []) { return '{}'; } @@ -401,25 +379,22 @@ protected function jsonToHash(array $data, int $indent = 0): string $output .= "\n"; } - $output .= str_repeat(' ', $indent + 2) . '}'; - - return $output; + return $output . (str_repeat(' ', $indent + 2) . '}'); } + #[Override] public function getFilters(): array { return [ - new TwigFilter('rubyComment', function ($value) { + new TwigFilter('rubyComment', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " # " . wordwrap($line, 75, "\n # "); } return implode("\n", $value); }, ['is_safe' => ['html']]), - new TwigFilter('caseEnumKey', function (string $value) { - return $this->toUpperSnakeCase($value); - }), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('caseEnumKey', fn(string $value): string => $this->toUpperSnakeCase($value)), + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -430,7 +405,7 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toUpperSnakeCase($enumKeys[$index]); @@ -453,13 +428,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($enumName, $resolveKey) { - return $enumName . '::' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => $enumName . '::' . $resolveKey($value), $values); return '[' . implode(', ', $items) . ']'; } diff --git a/src/SDK/Language/Rust.php b/src/SDK/Language/Rust.php index 3ff57d8fea..b1f5104992 100644 --- a/src/SDK/Language/Rust.php +++ b/src/SDK/Language/Rust.php @@ -2,17 +2,18 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; class Rust extends Language { + #[Override] protected $params = [ "cratePackage" => "packageName", ]; /** - * @param string $name * @return $this */ public function setCratePackage(string $name): self @@ -22,9 +23,6 @@ public function setCratePackage(string $name): self return $this; } - /** - * @return string - */ public function getName(): string { return "Rust"; @@ -32,8 +30,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -94,9 +90,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return [ @@ -136,9 +129,6 @@ public function getIdentifierOverrides(): array ]; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -276,11 +266,6 @@ public function getFiles(): array ]; } - /** - * @param array $parameter - * @param array $spec - * @return string - */ public function getTypeName(array $parameter, array $spec = []): string { $isArray = (($parameter["type"] ?? null) === self::TYPE_ARRAY); @@ -330,35 +315,21 @@ public function getTypeName(array $parameter, array $spec = []): string }; } - /** - * @return string - */ public function getStaticAccessOperator(): string { return '::'; } - /** - * @return string - */ public function getStringQuote(): string { return '"'; } - /** - * @param string $elements - * @return string - */ public function getArrayOf(string $elements): string { return 'vec![' . $elements . ']'; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param["type"] ?? ""; @@ -403,7 +374,7 @@ public function getParamDefault(array $param): string $output .= $default ? "true" : "false"; break; case self::TYPE_STRING: - $output .= "String::from(\"" . addslashes($default) . "\")"; + $output .= "String::from(\"" . addslashes((string) $default) . "\")"; break; case self::TYPE_OBJECT: $output .= "serde_json::Value::Null"; @@ -420,11 +391,6 @@ public function getParamDefault(array $param): string return $output; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param["type"] ?? ""; @@ -464,7 +430,7 @@ public function getParamExample(array $param, string $lang = ''): string $output .= $example ? "true" : "false"; break; case self::TYPE_STRING: - $output .= "\"" . addslashes($example) . "\""; + $output .= "\"" . addslashes((string) $example) . "\""; break; case self::TYPE_OBJECT: $output .= "serde_json::json!({})"; @@ -481,7 +447,7 @@ public function getParamExample(array $param, string $lang = ''): string if (\is_array($decoded)) { $formatted = array_map( - fn ($value) => $this->formatArrayItemExample($value, $items), + fn ($value): string => $this->formatArrayItemExample($value, $items), $decoded, ); @@ -489,14 +455,14 @@ public function getParamExample(array $param, string $lang = ''): string } } elseif (\is_array($example)) { $formatted = array_map( - fn ($value) => $this->formatArrayItemExample($value, $items), + fn ($value): string => $this->formatArrayItemExample($value, $items), $example, ); return "vec![" . implode(", ", $formatted) . "]"; } - if (preg_match('/^\[(.*)]$/s', $example, $match)) { + if (preg_match('/^\[(.*)]$/s', (string) $example, $match)) { $example = $match[1]; } $output .= "vec![" . $example . "]"; @@ -510,6 +476,7 @@ public function getParamExample(array $param, string $lang = ''): string return $output; } + #[Override] public function getPermissionExample(string $example): string { $permissions = []; @@ -541,15 +508,13 @@ public function getPermissionExample(string $example): string return $this->getArrayOf(implode(", ", $permissions)); } - /** - * @return array - */ + #[Override] public function getFilters(): array { return [ new TwigFilter( "rustdocComment", - function ($value, $indent = 0) { + function ($value, $indent = 0): string { $value = trim($value); $value = explode("\n", $value); $indent = \str_repeat(" ", $indent); @@ -560,59 +525,18 @@ function ($value, $indent = 0) { }, ["is_safe" => ["html"]], ), - new TwigFilter("propertyType", function ( - array $property, - array $spec = [], - string $generic = "serde_json::Value", - ) { - return $this->getPropertyType($property, $spec, $generic); - }), - new TwigFilter("returnType", function ( - array $method, - array $spec, - string $namespace, - string $generic = "serde_json::Value", - ) { - return $this->getReturnType($method, $spec, $namespace, $generic); - }), - new TwigFilter("caseEnumKey", function (string $value) { - return $this->toPascalCase($value); - }), - new TwigFilter("docsArgumentExample", function (array $param, string $crateName) { - return $this->getDocsArgumentExample($param, $crateName); - }, ["is_safe" => ["html"]]), - new TwigFilter("inputType", function ( - array $property, - array $spec = [], - string $generic = "serde_json::Value", - ) { - return $this->getInputType($property, $spec, $generic); - }), - new TwigFilter("paramValue", function ( - array $property, - string $paramName, - array $spec = [], - ) { - return $this->getParamValue($property, $paramName, $spec); - }, ["is_safe" => ["html"]]), - new TwigFilter("rustType", function ($value) { - return str_replace(['<', '>'], ['<', '>'], $value); - }, ["is_safe" => ["html"]]), - new TwigFilter("rustCrateName", function ($value) { - return str_replace('-', '_', $value); - }), - new TwigFilter("stripProtocol", function ($value) { - return str_replace(['https://', 'http://'], '', $value); - }), + new TwigFilter("propertyType", fn(array $property, array $spec = [], string $generic = "serde_json::Value"): string => $this->getPropertyType($property, $spec, $generic)), + new TwigFilter("returnType", fn(array $method, array $spec, string $namespace, string $generic = "serde_json::Value"): string => $this->getReturnType($method, $spec, $namespace, $generic)), + new TwigFilter("caseEnumKey", fn(string $value): string => $this->toPascalCase($value)), + new TwigFilter("docsArgumentExample", fn(array $param, string $crateName): string => $this->getDocsArgumentExample($param, $crateName), ["is_safe" => ["html"]]), + new TwigFilter("inputType", fn(array $property, array $spec = [], string $generic = "serde_json::Value"): string => $this->getInputType($property, $spec, $generic)), + new TwigFilter("paramValue", fn(array $property, string $paramName, array $spec = []): string => $this->getParamValue($property, $paramName, $spec), ["is_safe" => ["html"]]), + new TwigFilter("rustType", fn($value): string|array => str_replace(['<', '>'], ['<', '>'], $value), ["is_safe" => ["html"]]), + new TwigFilter("rustCrateName", fn($value): string|array => str_replace('-', '_', $value)), + new TwigFilter("stripProtocol", fn($value): string|array => str_replace(['https://', 'http://'], '', $value)), ]; } - /** - * @param array $property - * @param array $spec - * @param string $generic - * @return string - */ protected function getPropertyType(array $property, array $spec, string $generic = "serde_json::Value"): string { if (\array_key_exists("sub_schemas", $property) && !empty($property["sub_schemas"])) { @@ -620,7 +544,7 @@ protected function getPropertyType(array $property, array $spec, string $generic } if (\array_key_exists("sub_schema", $property)) { - $type = "crate::models::" . ucfirst($property["sub_schema"]); + $type = "crate::models::" . ucfirst((string) $property["sub_schema"]); if ($property["type"] === "array") { $type = "Vec<" . $type . ">"; @@ -634,11 +558,6 @@ protected function getPropertyType(array $property, array $spec, string $generic /** * Get input type for method parameters (uses impl Into for better DX) - * - * @param array $property - * @param array $spec - * @param string $generic - * @return string */ protected function getInputType(array $property, array $spec, string $generic = "serde_json::Value"): string { @@ -698,7 +617,7 @@ protected function toCaseSnake(string $value): string $ret = $matches[0]; foreach ($ret as &$match) { - $match = $match == strtoupper($match) + $match = $match === strtoupper($match) ? strtolower($match) : lcfirst($match); } @@ -738,7 +657,7 @@ protected function getEnumExample(array $param, string $prefix = ""): string $example = $param["example"] ?? null; $isArray = ($param["type"] ?? "") === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== "") { @@ -769,12 +688,12 @@ protected function getEnumExample(array $param, string $prefix = ""): string $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } $items = array_map( - fn ($value) => $enumPath . $resolveKey($value), + fn ($value): string => $enumPath . $resolveKey($value), $values, ); @@ -809,11 +728,6 @@ protected function getDocsArgumentExample(array $param, string $crateName): stri /** * Get parameter value conversion expression for method body - * - * @param array $property - * @param string $paramName - * @param array $spec - * @return string */ protected function getParamValue(array $property, string $paramName, array $spec): string { @@ -833,13 +747,6 @@ protected function getParamValue(array $property, string $paramName, array $spec return $paramName; } - /** - * @param array $method - * @param array $spec - * @param string $namespace - * @param string $generic - * @return string - */ protected function getReturnType( array $method, array $spec, @@ -873,26 +780,21 @@ protected function getReturnType( return "crate::error::Result"; } - $ret = ucfirst($method["responseModel"]); + $ret = ucfirst((string) $method["responseModel"]); return "crate::error::Result"; } protected function isEmptyResponse(array $responses): bool { - foreach ($responses as $code => $response) { + foreach (array_keys($responses) as $code) { if (!in_array((int)$code, [204, 205])) { return false; } } - return !empty($responses); + return $responses !== []; } - /** - * @param string|null $model - * @param array $spec - * @return bool - */ protected function hasGenericType(?string $model, array $spec): bool { if (empty($model) || $model === "any") { diff --git a/src/SDK/Language/Swift.php b/src/SDK/Language/Swift.php index 1523785e54..b5214ebf89 100644 --- a/src/SDK/Language/Swift.php +++ b/src/SDK/Language/Swift.php @@ -2,14 +2,12 @@ namespace Appwrite\SDK\Language; +use Override; use Appwrite\SDK\Language; use Twig\TwigFilter; class Swift extends Language { - /** - * @return string - */ public function getName(): string { return 'Swift'; @@ -17,8 +15,6 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ public function getKeywords(): array { @@ -91,9 +87,6 @@ public function getKeywords(): array ]; } - /** - * @return array - */ public function getIdentifierOverrides(): array { return [ @@ -116,9 +109,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -320,10 +310,6 @@ public function getFiles(): array ]; } - /** - * @param array $parameter - * @return string - */ public function getTypeName(array $parameter, array $spec = [], bool $isProperty = false): string { if ( @@ -332,7 +318,7 @@ public function getTypeName(array $parameter, array $spec = [], bool $isProperty ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return '[' . ($spec['title'] ?? '') . 'Enums.' . $enumType . ']'; } @@ -341,7 +327,7 @@ public function getTypeName(array $parameter, array $spec = [], bool $isProperty return ($spec['title'] ?? '') . 'Enums.' . \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return ($spec['title'] ?? '') . 'Enums.' . \ucfirst($parameter['name']); + return ($spec['title'] ?? '') . 'Enums.' . \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return '[' . ($spec['title'] ?? '') . 'Models.' . $this->toPascalCase($parameter['array']['model']) . ']'; @@ -368,10 +354,6 @@ public function getTypeName(array $parameter, array $spec = [], bool $isProperty }; } - /** - * @param array $param - * @return string - */ public function getParamDefault(array $param): string { $type = $param['type'] ?? ''; @@ -430,11 +412,6 @@ public function getParamDefault(array $param): string } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -481,7 +458,7 @@ public function getParamExample(array $param, string $lang = ''): string $output .= "\"{$example}\""; break; case self::TYPE_OBJECT: - $decoded = json_decode($example, true); + $decoded = json_decode((string) $example, true); if ($decoded && is_array($decoded)) { $output .= $this->jsonToSwiftDict($decoded); } else { @@ -496,14 +473,10 @@ public function getParamExample(array $param, string $lang = ''): string /** * Converts JSON Object To Swift Native Dictionary - * - * @param array $data - * @param int $indent - * @return string */ protected function jsonToSwiftDict(array $data, int $indent = 0): string { - if (empty($data)) { + if ($data === []) { return '[:]'; } @@ -531,9 +504,7 @@ protected function jsonToSwiftDict(array $data, int $indent = 0): string $output .= ' ' . $itemIndent . '"' . $key . '": ' . $value . $comma . "\n"; } - $output .= ' ' . $baseIndent . ']'; - - return $output; + return $output . (' ' . $baseIndent . ']'); } public function getModelToMapValue(array $property): string @@ -543,7 +514,7 @@ public function getModelToMapValue(array $property): string $name = "`{$name}`"; } $name = \str_replace('$', '', $name); - $nullAware = !empty($property['required']) ? '' : '?'; + $nullAware = empty($property['required']) ? '?' : ''; if (!empty($property['sub_schema'])) { if (($property['type'] ?? '') === self::TYPE_ARRAY) { @@ -564,47 +535,36 @@ public function getModelToMapValue(array $property): string return $name; } + #[Override] public function getFilters(): array { return [ - new TwigFilter('swiftComment', function ($value) { + new TwigFilter('swiftComment', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " /// " . wordwrap($line, 75, "\n /// "); } return implode("\n", $value); }, ['is_safe' => ['html']]), - new TwigFilter('returnType', function (array $method, array $spec, string $generic = 'T') { - return $this->getReturnType($method, $spec, $generic); - }), - new TwigFilter('modelType', function (array $property, array $spec, string $generic = 'T : Codable') { - return $this->getModelType($property, $spec, $generic); - }), - new TwigFilter('propertyType', function (array $property, array $spec, string $generic = 'T') { - return $this->getPropertyType($property, $spec, $generic); - }), - new TwigFilter('isAnyCodableArray', function (array $property, array $spec) { - return $this->isAnyCodableArray($property, $spec); - }), - new TwigFilter('isAnyCodableObject', function (array $property, array $spec) { - return $this->isAnyCodableObject($property, $spec); - }), - new TwigFilter('hasGenericType', function (string $model, array $spec) { - return $this->hasGenericType($model, $spec); - }), + new TwigFilter('returnType', fn(array $method, array $spec, string $generic = 'T'): string => $this->getReturnType($method, $spec, $generic)), + new TwigFilter('modelType', fn(array $property, array $spec, string $generic = 'T : Codable'): string => $this->getModelType($property, $spec, $generic)), + new TwigFilter('propertyType', fn(array $property, array $spec, string $generic = 'T'): string => $this->getPropertyType($property, $spec, $generic)), + new TwigFilter('isAnyCodableArray', fn(array $property, array $spec): bool => $this->isAnyCodableArray($property, $spec)), + new TwigFilter('isAnyCodableObject', fn(array $property, array $spec): bool => $this->isAnyCodableObject($property, $spec)), + new TwigFilter('hasGenericType', fn(string $model, array $spec): string => $this->hasGenericType($model, $spec)), new TwigFilter('escapeSwiftKeyword', function ($value) { if (\in_array($value, $this->getKeywords())) { return "`{$value}`"; } return $value; }), - new TwigFilter('caseEnumKey', function (string $value) { + new TwigFilter('caseEnumKey', function (string $value): string { if (isset($this->getIdentifierOverrides()[$value])) { $value = $this->getIdentifierOverrides()[$value]; } return $this->toCamelCase($value); }), - new TwigFilter('enumExample', function (array $param) { + new TwigFilter('enumExample', function (array $param): string { $enumValues = $param['enumValues'] ?? []; if (empty($enumValues)) { return ''; @@ -614,7 +574,7 @@ public function getFilters(): array $example = $param['example'] ?? null; $isArray = ($param['type'] ?? '') === self::TYPE_ARRAY; - $resolveKey = function ($value) use ($enumValues, $enumKeys) { + $resolveKey = function ($value) use ($enumValues, $enumKeys): string { $index = array_search($value, $enumValues, true); if ($index !== false && isset($enumKeys[$index]) && $enumKeys[$index] !== '') { return $this->toCamelCase($enumKeys[$index]); @@ -637,13 +597,11 @@ public function getFilters(): array $values = $example; } - if (empty($values)) { + if ($values === []) { $values = [$enumValues[0]]; } - $items = array_map(function ($value) use ($resolveKey) { - return '.' . $resolveKey($value); - }, $values); + $items = array_map(fn($value): string => '.' . $resolveKey($value), $values); return '[' . implode(', ', $items) . ']'; } @@ -651,9 +609,7 @@ public function getFilters(): array $value = ($example !== null && $example !== '') ? $example : $enumValues[0]; return '.' . $resolveKey($value); }), - new TwigFilter('modelToMapValue', function (array $property) { - return $this->getModelToMapValue($property); - }, ['is_safe' => ['html']]), + new TwigFilter('modelToMapValue', fn(array $property): string => $this->getModelToMapValue($property), ['is_safe' => ['html']]), ]; } @@ -684,16 +640,16 @@ protected function getReturnType(array $method, array $spec, string $generic): s $ret = $this->toPascalCase($method['responseModel']); - if ($this->hasGenericType($method['responseModel'], $spec)) { + if ($this->hasGenericType($method['responseModel'], $spec) !== '' && $this->hasGenericType($method['responseModel'], $spec) !== '0') { $ret .= '<' . $generic . '>'; } - return \ucfirst($spec['title']) . 'Models.' . $ret; + return \ucfirst((string) $spec['title']) . 'Models.' . $ret; } protected function getModelType(array $definition, array $spec, string $generic): string { - if ($this->hasGenericType($definition['name'], $spec)) { + if ($this->hasGenericType($definition['name'], $spec) !== '' && $this->hasGenericType($definition['name'], $spec) !== '0') { return $this->toPascalCase($definition['name']) . '<' . $generic . '>'; } return $this->toPascalCase($definition['name']); @@ -704,7 +660,7 @@ protected function getPropertyType(array $property, array $spec, string $generic if (\array_key_exists('sub_schema', $property)) { $type = $this->toPascalCase($property['sub_schema']); - if ($this->hasGenericType($property['sub_schema'], $spec)) { + if ($this->hasGenericType($property['sub_schema'], $spec) !== '' && $this->hasGenericType($property['sub_schema'], $spec) !== '0') { $type .= '<' . $generic . '>'; } @@ -720,10 +676,6 @@ protected function getPropertyType(array $property, array $spec, string $generic /** * Check if a property is an array that results in [AnyCodable] type - * - * @param array $property - * @param array $spec - * @return bool */ protected function isAnyCodableArray(array $property, array $spec): bool { diff --git a/src/SDK/Language/Unity.php b/src/SDK/Language/Unity.php index 2e3e445de9..b82dc7a738 100644 --- a/src/SDK/Language/Unity.php +++ b/src/SDK/Language/Unity.php @@ -2,8 +2,16 @@ namespace Appwrite\SDK\Language; +use Override; +use RecursiveIteratorIterator; +use RecursiveDirectoryIterator; +use FilesystemIterator; +use RuntimeException; +use SplFileInfo; + class Unity extends DotNet { + #[Override] protected $params = [ 'packageName' => 'package-name', ]; @@ -15,9 +23,7 @@ public function setPackageName(string $name): self return $this; } - /** - * @return string - */ + #[Override] public function getName(): string { return 'Unity'; @@ -25,9 +31,8 @@ public function getName(): string /** * Get Language Keywords List - * - * @return array */ + #[Override] public function getKeywords(): array { $base = parent::getKeywords(); @@ -43,9 +48,7 @@ public function getKeywords(): array return array_values(array_unique(array_merge($base, $unity))); } - /** - * @return array - */ + #[Override] public function getFiles(): array { $files = [ @@ -486,9 +489,7 @@ public function getFiles(): array 'Assets/Editor/{{ spec.title | caseUcfirst }}SetupWindow.cs', ]; - $files = array_filter($files, function ($file) use ($excludeInTest): bool { - return !in_array($file['destination'], $excludeInTest); - }); + $files = array_filter($files, fn(array $file): bool => !in_array($file['destination'], $excludeInTest)); } return $files; @@ -500,9 +501,6 @@ public function getFiles(): array * Immutable UPM packages need committed .meta files (Unity won't generate * them), else assets are ignored. GUIDs are path-derived for stable diffs; * existing metas (asmdef, .dll plugins) are left untouched. - * - * @param string $target - * @return void */ public function postGenerate(string $target): void { @@ -516,30 +514,20 @@ public function postGenerate(string $target): void $normalizedTarget = rtrim(str_replace('\\', '/', $target), '/'); - $iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($target, \FilesystemIterator::SKIP_DOTS), - \RecursiveIteratorIterator::SELF_FIRST + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($target, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $item) { - /** @var \SplFileInfo $item */ + /** @var SplFileInfo $item */ $path = $item->getPathname(); $relative = ltrim(substr(str_replace('\\', '/', $path), strlen($normalizedTarget)), '/'); if ($relative === '') { continue; } - - // Unity ignores hidden entries and anything inside a folder (or - // file) suffixed with '~' (e.g. Samples~, Documentation~), so - // emitting metas there would be wrong. Skip the whole subtree. - $skip = false; - foreach (explode('/', $relative) as $segment) { - if ($segment === '' || $segment[0] === '.' || str_ends_with($segment, '~')) { - $skip = true; - break; - } - } + $skip = array_any(explode('/', $relative), fn($segment): bool => $segment === '' || $segment[0] === '.' || str_ends_with((string) $segment, '~')); if ($skip) { continue; } @@ -555,7 +543,7 @@ public function postGenerate(string $target): void } if (file_put_contents($meta, $this->getMetaContents($relative, $item->isDir())) === false) { - throw new \RuntimeException("Failed to write meta file: {$meta}"); + throw new RuntimeException("Failed to write meta file: {$meta}"); } } } @@ -565,7 +553,6 @@ public function postGenerate(string $target): void * * @param string $relativePath Package-relative path (forward slashes). * @param bool $isDir Whether the asset is a directory. - * @return string */ private function getMetaContents(string $relativePath, bool $isDir): string { @@ -630,7 +617,6 @@ private function getMetaContents(string $relativePath, bool $isDir): string * Build PluginImporter meta lines enabling a single target platform * (and the editor stub), with every other platform disabled. * - * @param string $guid * @param string $platform Unity platform key (e.g. 'WebGL', 'Android'). * @return array */ diff --git a/src/SDK/Language/Web.php b/src/SDK/Language/Web.php index ab497f426b..0b3188a41f 100644 --- a/src/SDK/Language/Web.php +++ b/src/SDK/Language/Web.php @@ -2,13 +2,11 @@ namespace Appwrite\SDK\Language; +use Override; use Twig\TwigFilter; class Web extends JS { - /** - * @return string - */ public function getName(): string { return 'Web'; @@ -29,9 +27,6 @@ public function getArrayOf(string $elements): string return '[' . $elements . ']'; } - /** - * @return array - */ public function getFiles(): array { return [ @@ -168,11 +163,6 @@ public function getFiles(): array ]; } - /** - * @param array $param - * @param string $lang - * @return string - */ public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; @@ -197,7 +187,7 @@ public function getParamExample(array $param, string $lang = ''): string self::TYPE_BOOLEAN => ($example) ? 'true' : 'false', self::TYPE_OBJECT => ($example === '{}') ? '{}' - : (($formatted = json_encode(json_decode($example, true), JSON_PRETTY_PRINT)) + : (($formatted = json_encode(json_decode((string) $example, true), JSON_PRETTY_PRINT)) ? preg_replace('/\n/', "\n ", $formatted) : $example), self::TYPE_STRING => "'{$example}'", @@ -224,6 +214,7 @@ public function getReadOnlyProperties(array $parameter, string $responseModel, a return $properties; } + #[Override] public function getTypeName(array $parameter, array $method = []): string { if ( @@ -232,7 +223,7 @@ public function getTypeName(array $parameter, array $method = []): string ) { $enumType = isset($parameter['enumName']) ? \ucfirst($parameter['enumName']) - : \ucfirst($parameter['name']); + : \ucfirst((string) $parameter['name']); return $enumType . '[]'; } @@ -241,7 +232,7 @@ public function getTypeName(array $parameter, array $method = []): string return \ucfirst($parameter['enumName']); } if (!empty($parameter['enumValues'])) { - return \ucfirst($parameter['name']); + return \ucfirst((string) $parameter['name']); } if (!empty($parameter['array']['model'])) { return 'Models.' . $this->toPascalCase($parameter['array']['model']) . '[]'; @@ -274,7 +265,7 @@ public function getTypeName(array $parameter, array $method = []): string $unionTypes[] = 'Models.' . $this->toPascalCase($modelName); } } - if (!empty($unionTypes)) { + if ($unionTypes !== []) { return '(' . implode(' | ', $unionTypes) . ')[]'; } } @@ -285,7 +276,7 @@ public function getTypeName(array $parameter, array $method = []): string case self::TYPE_FILE: return 'File'; case self::TYPE_OBJECT: - if (empty($method)) { + if ($method === []) { return $parameter['type']; } switch ($method['responseModel']) { @@ -340,12 +331,12 @@ public function getGenerics(string $model, array $spec, bool $skipFirst = false) $this->populateGenerics($model, $spec, $generics, $skipFirst); } - if (empty($generics)) { + if ($generics === []) { return ''; } $generics = array_unique($generics); - $generics = array_map(fn ($type) => "{$type} extends Models.{$type} = Models.Default{$type}", $generics); + $generics = array_map(fn ($type): string => "{$type} extends Models.{$type} = Models.Default{$type}", $generics); return '<' . implode(', ', $generics) . '>'; } @@ -370,7 +361,7 @@ protected function getUnionReturnType(array $method, array $spec): ?string $modelType = ''; if ( - array_key_exists($model, $spec['definitions']) && + array_key_exists((string) $model, $spec['definitions']) && array_key_exists('additionalProperties', $spec['definitions'][$model]) && !$spec['definitions'][$model]['additionalProperties'] ) { @@ -382,16 +373,16 @@ protected function getUnionReturnType(array $method, array $spec): ?string $models = []; $this->populateGenerics($model, $spec, $models); $models = array_unique($models); - $models = array_filter($models, fn ($m) => $m != $this->toPascalCase($model)); + $models = array_filter($models, fn ($m): bool => $m != $this->toPascalCase($model)); - if (!empty($models)) { + if ($models !== []) { $modelType .= '<' . implode(', ', $models) . '>'; } $unionTypes[] = $modelType; } - if (empty($unionTypes)) { + if ($unionTypes === []) { return null; } @@ -418,7 +409,7 @@ public function getReturn(array $method, array $spec): string $ret = 'Promise<'; if ( - array_key_exists($method['responseModel'], $spec['definitions']) && + array_key_exists((string) $method['responseModel'], $spec['definitions']) && array_key_exists('additionalProperties', $spec['definitions'][$method['responseModel']]) && !$spec['definitions'][$method['responseModel']]['additionalProperties'] ) { @@ -432,15 +423,13 @@ public function getReturn(array $method, array $spec): string $this->populateGenerics($method['responseModel'], $spec, $models); $models = array_unique($models); - $models = array_filter($models, fn ($model) => $model != $this->toPascalCase($method['responseModel'])); + $models = array_filter($models, fn ($model): bool => $model != $this->toPascalCase($method['responseModel'])); - if (!empty($models)) { + if ($models !== []) { $ret .= '<' . implode(', ', $models) . '>'; } - $ret .= '>'; - - return $ret; + return $ret . '>'; } return 'Promise<{}>'; @@ -457,10 +446,10 @@ public function getSubSchema(array $property, array $spec, string $methodName = $generics = []; $this->populateGenerics($property['sub_schema'], $spec, $generics); - $generics = array_filter($generics, fn ($model) => $model != $this->toPascalCase($property['sub_schema'])); + $generics = array_filter($generics, fn ($model): bool => $model != $this->toPascalCase($property['sub_schema'])); $ret .= $this->toPascalCase($property['sub_schema']); - if (!empty($generics)) { + if ($generics !== []) { $ret .= '<' . implode(', ', $generics) . '>'; } if ($property['type'] === 'array') { @@ -481,25 +470,16 @@ public function getSubSchema(array $property, array $spec, string $methodName = return $this->getTypeName($property); } + #[Override] public function getFilters(): array { return \array_merge(parent::getFilters(), [ - new TwigFilter('getPropertyType', function ($value, $method = []) { - return $this->getTypeName($value, $method); - }), - new TwigFilter('getReadOnlyProperties', function ($value, $responseModel, $spec = []) { - return $this->getReadOnlyProperties($value, $responseModel, $spec); - }), - new TwigFilter('getSubSchema', function (array $property, array $spec, string $methodName = '') { - return $this->getSubSchema($property, $spec, $methodName); - }), - new TwigFilter('getGenerics', function (string $model, array $spec, bool $skipAdditional = false) { - return $this->getGenerics($model, $spec, $skipAdditional); - }), - new TwigFilter('getReturn', function (array $method, array $spec) { - return $this->getReturn($method, $spec); - }), - new TwigFilter('getOverloadCondition', function (array $method) { + new TwigFilter('getPropertyType', fn(array $value, array $method = []): string => $this->getTypeName($value, $method)), + new TwigFilter('getReadOnlyProperties', fn(array $value, string $responseModel, array $spec = []): array => $this->getReadOnlyProperties($value, $responseModel, $spec)), + new TwigFilter('getSubSchema', fn(array $property, array $spec, string $methodName = ''): string => $this->getSubSchema($property, $spec, $methodName)), + new TwigFilter('getGenerics', fn(string $model, array $spec, bool $skipAdditional = false): string => $this->getGenerics($model, $spec, $skipAdditional)), + new TwigFilter('getReturn', fn(array $method, array $spec): string => $this->getReturn($method, $spec)), + new TwigFilter('getOverloadCondition', function (array $method): string { $params = $method['parameters']['all'] ?? []; $hasRequired = false; @@ -537,18 +517,16 @@ public function getFilters(): array $condition .= ' && (' . implode(' || ', $keys) . ')'; } - $condition .= ')'; - - return $condition; + return $condition . ')'; }, ['is_safe' => ['html']]), - new TwigFilter('comment2', function ($value) { + new TwigFilter('comment2', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " * " . wordwrap($line, 75, "\n * "); } return implode("\n", $value); }, ['is_safe' => ['html']]), - new TwigFilter('comment3', function ($value) { + new TwigFilter('comment3', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " * " . wordwrap($line, 75, "\n * "); diff --git a/src/SDK/SDK.php b/src/SDK/SDK.php index 4e7ee4c282..e0b2692b8a 100644 --- a/src/SDK/SDK.php +++ b/src/SDK/SDK.php @@ -2,6 +2,8 @@ namespace Appwrite\SDK; +use MatthiasMullie\Minify\JS; +use MatthiasMullie\Minify\CSS; use Exception; use Appwrite\Spec\Spec; use Throwable; @@ -20,29 +22,10 @@ class SDK { - /** - * @var Language|null - */ - protected ?Language $language = null; - - /** - * @var Spec|null - */ - protected ?Spec $spec = null; - - /** - * @var Environment|null - */ protected ?Environment $twig = null; - /** - * @var array - */ protected array $defaultHeaders = []; - /** - * @var array - */ protected array $params = [ 'namespace' => '', 'name' => '', @@ -71,41 +54,23 @@ class SDK 'test' => 'false' ]; - /** - * @var array - */ protected array $excludeRules = [ 'services' => [], 'methods' => [], 'definitions' => [] ]; - /** - * @var array|null - */ protected ?array $excludeIndex = null; - /** - * @var array|null - */ protected ?array $filteredServicesCache = null; - /** - * @var array|null - */ protected ?array $filteredModelDataCache = null; /** * SDK constructor. - * - * @param Language $language - * @param Spec $spec */ - public function __construct(Language $language, Spec $spec) + public function __construct(protected Language $language, protected Spec $spec) { - $this->language = $language; - $this->spec = $spec; - $this->twig = new Environment(new FilesystemLoader(__DIR__ . '/../../templates'), [ 'debug' => true ]); @@ -126,134 +91,77 @@ public function __construct(Language $language, Spec $spec) $this->twig->addExtension(new DebugExtension()); - $this->twig->addFilter(new TwigFilter('caseLower', function ($value) { - return strtolower((string)$value); - })); - $this->twig->addFilter(new TwigFilter('caseUpper', function ($value) { - return strtoupper((string)$value); - })); - $this->twig->addFilter(new TwigFilter('caseUcfirst', function ($value) { - return ucfirst($this->helperCamelCase($value)); - })); - $this->twig->addFilter(new TwigFilter('caseUcwords', function ($value) { - return ucwords($value, " -_"); - })); - $this->twig->addFilter(new TwigFilter('caseLcfirst', function ($value) { - return lcfirst((string)$value); - })); - $this->twig->addFilter(new TwigFilter('caseCamel', function ($value) { - return $this->helperCamelCase($value); - })); - $this->twig->addFilter(new TwigFilter('removeDash', function ($value) { - return str_replace('-', '', $value); - })); - $this->twig->addFilter(new TwigFilter('caseDash', function ($value) { - return str_replace([' ', '_'], '-', strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $value))); - })); - $this->twig->addFilter(new TwigFilter('caseKebab', function ($value) { - return strtolower(preg_replace('/(?twig->addFilter(new TwigFilter('caseSlash', function ($value) { - return str_replace([' ', '_', '.'], '/', strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1/', $value))); - })); - $this->twig->addFilter(new TwigFilter('caseDot', function ($value) { - return str_replace([' ', '_'], '.', strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1.', $value))); - })); - $this->twig->addFilter(new TwigFilter('caseSnake', function ($value) { + $this->twig->addFilter(new TwigFilter('caseLower', fn($value) => strtolower((string)$value))); + $this->twig->addFilter(new TwigFilter('caseUpper', fn($value) => strtoupper((string)$value))); + $this->twig->addFilter(new TwigFilter('caseUcfirst', fn(?string $value): string => ucfirst($this->helperCamelCase($value)))); + $this->twig->addFilter(new TwigFilter('caseUcwords', fn($value): string => ucwords((string) $value, " -_"))); + $this->twig->addFilter(new TwigFilter('caseLcfirst', fn($value): string => lcfirst((string)$value))); + $this->twig->addFilter(new TwigFilter('caseCamel', fn(?string $value): string => $this->helperCamelCase($value))); + $this->twig->addFilter(new TwigFilter('removeDash', fn($value): string|array => str_replace('-', '', $value))); + $this->twig->addFilter(new TwigFilter('caseDash', fn($value) => str_replace([' ', '_'], '-', strtolower((string) preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', (string) $value))))); + $this->twig->addFilter(new TwigFilter('caseKebab', fn($value) => strtolower((string) preg_replace('/(?twig->addFilter(new TwigFilter('caseSlash', fn($value) => str_replace([' ', '_', '.'], '/', strtolower((string) preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1/', (string) $value))))); + $this->twig->addFilter(new TwigFilter('caseDot', fn($value) => str_replace([' ', '_'], '.', strtolower((string) preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1.', (string) $value))))); + $this->twig->addFilter(new TwigFilter('caseSnake', function ($value): string { preg_match_all('!([A-Za-z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $value, $matches); $ret = $matches[0]; foreach ($ret as &$match) { - $match = $match == strtoupper($match) + $match = $match === strtoupper($match) ? strtolower($match) : lcfirst($match); } return implode('_', $ret); })); - $this->twig->addFilter(new TwigFilter('caseJson', function ($value) { - return (is_array($value)) ? json_encode($value) : $value; - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('caseArray', function ($value) { - return (is_array($value)) ? json_encode($value) : '[]'; - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('typeName', function ($value, $spec = []) { - return $this->language->getTypeName($value, $spec); - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('getValidResponseModels', function ($value) { - return $this->getValidResponseModels($value); - })); - $this->twig->addFilter(new TwigFilter('paramDefault', function ($value) { - return $this->language->getParamDefault($value); - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('paramExample', function ($value) { - return $this->language->getParamExample($value); - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('comment1', function ($value) { + $this->twig->addFilter(new TwigFilter('caseJson', fn($value) => (is_array($value)) ? json_encode($value) : $value, ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('caseArray', fn($value) => (is_array($value)) ? json_encode($value) : '[]', ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('typeName', fn(array $value, array $spec = []): string => $this->language->getTypeName($value, $spec), ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('getValidResponseModels', fn(array $value): array => $this->getValidResponseModels($value))); + $this->twig->addFilter(new TwigFilter('paramDefault', fn(array $value): string => $this->language->getParamDefault($value), ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('paramExample', fn(array $value): string => $this->language->getParamExample($value), ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('comment1', function ($value): string { $value = explode("\n", $value); foreach ($value as $key => $line) { $value[$key] = " * " . wordwrap($line, 75, "\n * "); } return implode("\n", $value); }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('escapeDollarSign', function ($value) { + $this->twig->addFilter(new TwigFilter('escapeDollarSign', function ($value): string|array { $value = str_replace('\\', '\\\\', $value ?? ''); // Escape backslashes first $value = str_replace('"', '\\"', $value); // Escape double quotes $value = str_replace('$', '\\$', $value); // Escape dollar signs return $value; }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('paramsQuery', function ($value) { + $this->twig->addFilter(new TwigFilter('paramsQuery', function ($value): string { $query = ''; - foreach ($value as $key => $param) { - $query .= (!empty($query)) ? " + '&" : ""; + foreach ($value as $param) { + $query .= (empty($query)) ? "" : " + '&"; $query .= "{$param['name']}=' + {$param['name']}"; } return $query; }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('html', function ($value) { - return $value; - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('escapeKeyword', function ($value) use ($language) { - return $language->escapeKeyword($value); - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('caseHTML', function ($value) { - return $value; - }, ['is_safe' => ['html']])); - $this->twig->addFilter(new TwigFilter('removeDollarSign', function ($value) { - return str_replace('$', '', $value); - })); - $this->twig->addFilter(new TwigFilter('unescape', function ($value) { - return html_entity_decode($value); - })); - $this->twig->addFilter(new TwigFilter('overrideIdentifier', function ($value) use ($language) { - if (isset($language->getIdentifierOverrides()[$value])) { - return $language->getIdentifierOverrides()[$value]; - } - return $value; - })); - $this->twig->addFilter(new TwigFilter('capitalizeFirst', function ($value) { - return ucfirst($value); - })); - $this->twig->addFilter(new TwigFilter('caseSpace', function ($value) { - return preg_replace('/([a-z])([A-Z])/', '$1 $2', $value); - })); - $this->twig->addFilter(new TwigFilter('caseSnakeExceptFirstDot', function ($value) { + $this->twig->addFilter(new TwigFilter('html', fn($value) => $value, ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('escapeKeyword', fn(string $value): string => $language->escapeKeyword($value), ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('caseHTML', fn($value) => $value, ['is_safe' => ['html']])); + $this->twig->addFilter(new TwigFilter('removeDollarSign', fn($value): string|array => str_replace('$', '', $value))); + $this->twig->addFilter(new TwigFilter('unescape', fn($value): string => html_entity_decode((string) $value))); + $this->twig->addFilter(new TwigFilter('overrideIdentifier', fn($value) => $language->getIdentifierOverrides()[$value] ?? $value)); + $this->twig->addFilter(new TwigFilter('capitalizeFirst', fn($value): string => ucfirst((string) $value))); + $this->twig->addFilter(new TwigFilter('caseSpace', fn($value): ?string => preg_replace('/([a-z])([A-Z])/', '$1 $2', (string) $value))); + $this->twig->addFilter(new TwigFilter('caseSnakeExceptFirstDot', function ($value): string { $parts = explode('.', $value, 2); - $toSnake = function ($str) { + $toSnake = function ($str): string { preg_match_all('!([A-Za-z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $str, $matches); - return implode('_', array_map(function ($m) { - return $m === strtoupper($m) ? strtolower($m) : lcfirst($m); - }, $matches[0])); + return implode('_', array_map(fn(string $m): string => $m === strtoupper($m) ? strtolower($m) : lcfirst($m), $matches[0])); }; if (count($parts) < 2) { return $toSnake($value); } return $parts[0] . '.' . $toSnake($parts[1]); })); - $this->twig->addFilter(new TwigFilter('hasPermissionParam', function ($value) { - return $this->language->hasPermissionParam($value); - })); - $this->twig->addFilter(new TwigFilter('stripMarkdown', function ($value) { + $this->twig->addFilter(new TwigFilter('hasPermissionParam', fn(array $value): bool => $this->language->hasPermissionParam($value))); + $this->twig->addFilter(new TwigFilter('stripMarkdown', function ($value): string|array|null { if ($value === null) { return ''; } @@ -263,7 +171,7 @@ public function __construct(Language $language, Spec $spec) // useless in a terminal, so we drop the URL and keep just the text. $value = preg_replace_callback( '/\[([^\]]+)\]\(([^)]+)\)/', - function ($m) { + function (array $m): string { $text = $m[1]; $url = trim($m[2]); if (preg_match('/^https?:\/\//i', $url)) { @@ -283,10 +191,6 @@ function ($m) { })); } - /** - * @param array $headers - * @return $this - */ public function setDefaultHeaders(array $headers): SDK { $this->defaultHeaders = $headers; @@ -294,10 +198,6 @@ public function setDefaultHeaders(array $headers): SDK return $this; } - /** - * @param string $namespace - * @return $this - */ public function setNamespace(string $namespace): SDK { $this->setParam('namespace', $namespace); @@ -305,10 +205,6 @@ public function setNamespace(string $namespace): SDK return $this; } - /** - * @param string $name - * @return $this - */ public function setName(string $name): SDK { $this->setParam('name', $name); @@ -316,10 +212,6 @@ public function setName(string $name): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setDescription(string $text): SDK { $this->setParam('description', $text); @@ -327,10 +219,6 @@ public function setDescription(string $text): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setShortDescription(string $text): SDK { $this->setParam('shortDescription', $text); @@ -338,10 +226,6 @@ public function setShortDescription(string $text): SDK return $this; } - /** - * @param string $version - * @return $this - */ public function setVersion(string $version): SDK { $this->setParam('version', $version); @@ -349,10 +233,6 @@ public function setVersion(string $version): SDK return $this; } - /** - * @param string $platform - * @return $this - */ public function setPlatform(string $platform): SDK { $this->setParam('platform', $platform); @@ -360,10 +240,6 @@ public function setPlatform(string $platform): SDK return $this; } - /** - * @param string $license - * @return $this - */ public function setLicense(string $license): SDK { $this->setParam('license', $license); @@ -371,10 +247,6 @@ public function setLicense(string $license): SDK return $this; } - /** - * @param string $content - * @return $this - */ public function setLicenseContent(string $content): SDK { $this->setParam('licenseContent', $content); @@ -382,10 +254,6 @@ public function setLicenseContent(string $content): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setGitRepo(string $url): SDK { $this->setParam('gitRepo', $url); @@ -393,10 +261,6 @@ public function setGitRepo(string $url): SDK return $this; } - /** - * @param string $name - * @return $this - */ public function setGitRepoName(string $name): SDK { $this->setParam('gitRepoName', $name); @@ -404,10 +268,6 @@ public function setGitRepoName(string $name): SDK return $this; } - /** - * @param string $name - * @return $this - */ public function setGitUserName(string $name): SDK { $this->setParam('gitUserName', $name); @@ -415,10 +275,6 @@ public function setGitUserName(string $name): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setGitURL(string $url): SDK { $this->setParam('gitURL', $url); @@ -426,10 +282,6 @@ public function setGitURL(string $url): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setLogo(string $url): SDK { $this->setParam('logo', $url); @@ -437,10 +289,6 @@ public function setLogo(string $url): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setCoverImage(string $url): SDK { $this->setParam('coverImage', $url); @@ -448,10 +296,6 @@ public function setCoverImage(string $url): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setURL(string $url): SDK { $this->setParam('url', $url); @@ -459,10 +303,6 @@ public function setURL(string $url): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setShareText(string $text): SDK { $this->setParam('shareText', $text); @@ -470,10 +310,6 @@ public function setShareText(string $text): SDK return $this; } - /** - * @param string $user - * @return $this - */ public function setShareVia(string $user): SDK { $this->setParam('shareVia', $user); @@ -481,10 +317,6 @@ public function setShareVia(string $user): SDK return $this; } - /** - * @param string $url - * @return $this - */ public function setShareURL(string $url): SDK { $this->setParam('shareURL', $url); @@ -494,7 +326,6 @@ public function setShareURL(string $url): SDK /** * @param string $tags Comma separated list - * @return $this */ public function setShareTags(string $tags): SDK { @@ -503,10 +334,6 @@ public function setShareTags(string $tags): SDK return $this; } - /** - * @param string $message - * @return $this - */ public function setWarning(string $message): SDK { $this->setParam('warning', $message); @@ -516,7 +343,6 @@ public function setWarning(string $message): SDK /** * @param $message string - * @return $this */ public function setGettingStarted(string $message): SDK { @@ -525,10 +351,6 @@ public function setGettingStarted(string $message): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setReadme(string $text): SDK { $this->setParam('readme', $text); @@ -536,10 +358,6 @@ public function setReadme(string $text): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setChangelog(string $text): SDK { $this->setParam('changelog', $text); @@ -547,10 +365,6 @@ public function setChangelog(string $text): SDK return $this; } - /** - * @param string $text - * @return $this - */ public function setExamples(string $text): SDK { $this->setParam('examples', $text); @@ -558,11 +372,6 @@ public function setExamples(string $text): SDK return $this; } - /** - * @param string $channel - * @param string $url - * @return $this - */ public function setDiscord(string $channel, string $url): SDK { $this->setParam('discordChannel', $channel); @@ -571,10 +380,6 @@ public function setDiscord(string $channel, string $url): SDK return $this; } - /** - * @param string $handle - * @return $this - */ public function setTwitter(string $handle): SDK { $this->setParam('twitterHandle', $handle); @@ -582,10 +387,6 @@ public function setTwitter(string $handle): SDK return $this; } - /** - * @param string $test - * @return $this - */ public function setTest(string $test): SDK { $this->setParam('test', $test); @@ -593,11 +394,6 @@ public function setTest(string $test): SDK return $this; } - /** - * @param string $key - * @param string $value - * @return SDK - */ public function setParam(string $key, string $value): SDK { $this->params[$key] = $value; @@ -605,18 +401,11 @@ public function setParam(string $key, string $value): SDK return $this; } - /** - * @param string $name - * @return string - */ public function getParam(string $name): string { return $this->params[$name] ?? ''; } - /** - * @return array - */ public function getParams(): array { return $this->params; @@ -624,8 +413,6 @@ public function getParams(): array /** * Get services filtered by exclusion rules - * - * @return array */ protected function getFilteredServices(): array { @@ -645,7 +432,7 @@ protected function getFilteredServices(): array $methods = $this->getFilteredMethods($allMethods, $serviceName); - if (empty($methods)) { + if ($methods === []) { continue; } @@ -658,35 +445,21 @@ protected function getFilteredServices(): array return $this->filteredServicesCache; } - /** - * @param array $methods - * @param string $serviceName - * @return array - */ protected function getFilteredMethods(array $methods, string $serviceName = ''): array { - return \array_values(\array_filter($methods, fn (array $method) => !$this->isMethodExcluded($method, $serviceName))); + return \array_values(\array_filter($methods, fn (array $method): bool => !$this->isMethodExcluded($method, $serviceName))); } - /** - * @return array - */ protected function getFilteredDefinitions(): array { return $this->getFilteredModelData()['definitions']; } - /** - * @return array - */ protected function getFilteredRequestModels(): array { return $this->getFilteredModelData()['requestModels']; } - /** - * @return array - */ protected function getFilteredRequestEnums(?array $filteredServices = null): array { $filteredServices ??= $this->getFilteredServices(); @@ -714,9 +487,6 @@ protected function getFilteredRequestEnums(?array $filteredServices = null): arr return \array_values($list); } - /** - * @return array - */ protected function getFilteredResponseEnums(?array $filteredDefinitions = null): array { $filteredDefinitions ??= $this->getFilteredDefinitions(); @@ -753,9 +523,6 @@ protected function getFilteredResponseEnums(?array $filteredDefinitions = null): return \array_values($list); } - /** - * @return array - */ protected function getFilteredRequestModelEnums(?array $filteredRequestModels = null): array { $filteredRequestModels ??= $this->getFilteredRequestModels(); @@ -790,9 +557,6 @@ protected function getFilteredRequestModelEnums(?array $filteredRequestModels = return \array_values($list); } - /** - * @return array - */ protected function getFilteredAllEnums( ?array $filteredRequestEnums = null, ?array $filteredRequestModelEnums = null, @@ -833,13 +597,6 @@ protected function getFilteredAllEnums( return \array_values($list); } - /** - * @param array $list - * @param string $enumName - * @param array $enumValues - * @param array $enumKeys - * @return void - */ protected function mergeEnumValues(array &$list, string $enumName, array $enumValues, array $enumKeys = []): void { if (!isset($list[$enumName])) { @@ -860,9 +617,6 @@ protected function mergeEnumValues(array &$list, string $enumName, array $enumVa } } - /** - * @return array - */ protected function getFilteredModelData(): array { if ($this->filteredModelDataCache !== null) { @@ -905,7 +659,7 @@ protected function getFilteredModelData(): array } } - while (!empty($queue)) { + while ($queue !== []) { $modelName = array_key_first($queue); if ($modelName === null) { @@ -982,7 +736,6 @@ protected function getFilteredModelData(): array } /** - * @param string $target * @throws Throwable * @throws LoaderError * @throws RuntimeError @@ -1089,7 +842,7 @@ public function generate(string $target): void } break; case 'definition': - foreach ($filteredDefinitions as $key => $definition) { + foreach ($filteredDefinitions as $definition) { $params['definition'] = $definition; if ($this->exclude($file, $params)) { @@ -1100,7 +853,7 @@ public function generate(string $target): void } break; case 'requestModel': - foreach ($filteredRequestModels as $key => $requestModel) { + foreach ($filteredRequestModels as $requestModel) { $params['requestModel'] = $requestModel; if ($this->exclude($file, $params)) { @@ -1137,7 +890,7 @@ public function generate(string $target): void } break; case 'enum': - foreach ($filteredAllEnums as $key => $enum) { + foreach ($filteredAllEnums as $enum) { $params['enum'] = $enum; $this->render($template, $destination, $block, $params, $minify); @@ -1158,7 +911,6 @@ public function generate(string $target): void * 'methods' => [['name' => 'methodName'], ['type' => 'methodType']], * 'definitions' => [['name' => 'definitionName']] * ] - * @return $this */ public function setExclude(array $rules): SDK { @@ -1175,11 +927,6 @@ public function setExclude(array $rules): SDK return $this; } - /** - * @param string $serviceName - * @param array $methods - * @return bool - */ protected function isServiceExcluded(string $serviceName, array $methods): bool { $excludeIndex = $this->getExcludeIndex(); @@ -1203,11 +950,6 @@ protected function isServiceExcluded(string $serviceName, array $methods): bool return false; } - /** - * @param array $method - * @param string $serviceName - * @return bool - */ protected function isMethodExcluded(array $method, string $serviceName = ''): bool { $excludeIndex = $this->getExcludeIndex(); @@ -1225,17 +967,11 @@ protected function isMethodExcluded(array $method, string $serviceName = ''): bo return isset($excludeIndex['types'][$method['type'] ?? '']); } - /** - * @return array - */ protected function getExcludedDefinitions(): array { return $this->getExcludeIndex()['definitions']; } - /** - * @return array - */ protected function getExcludeIndex(): array { if ($this->excludeIndex !== null) { @@ -1291,9 +1027,8 @@ protected function getExcludeIndex(): array * * @param $file * @param $params - * @return bool */ - protected function exclude($file, $params): bool + protected function exclude(array $file, array $params): bool { $exclude = array_merge_recursive($file['exclude'] ?? [], $this->excludeRules); @@ -1356,57 +1091,22 @@ protected function exclude($file, $params): bool if (\in_array($params['method']['type'] ?? '', $types)) { return true; } - - if (\in_array($params['definition']['name'] ?? '', $definitions)) { - return true; - } - - return false; + return \in_array($params['definition']['name'] ?? '', $definitions); } - /** - * @param array $methods - * @return bool - */ protected function hasUploads(array $methods): bool { - foreach ($methods as $method) { - if (isset($method['type']) && $method['type'] === 'upload') { - return true; - } - } - - return false; + return array_any($methods, fn($method): bool => isset($method['type']) && $method['type'] === 'upload'); } - /** - * @param array $methods - * @return bool - */ protected function hasLocation(array $methods): bool { - foreach ($methods as $method) { - if (isset($method['type']) && $method['type'] === 'location') { - return true; - } - } - - return false; + return array_any($methods, fn($method): bool => isset($method['type']) && $method['type'] === 'location'); } - /** - * @param array $methods - * @return bool - */ protected function hasWebAuth(array $methods): bool { - foreach ($methods as $method) { - if (isset($method['type']) && $method['type'] === 'webAuth') { - return true; - } - } - - return false; + return array_any($methods, fn($method): bool => isset($method['type']) && $method['type'] === 'webAuth'); } protected function isConsoleOnly(string $serviceName): bool @@ -1423,11 +1123,6 @@ protected function isConsoleOnly(string $serviceName): bool } /** - * @param TemplateWrapper $template - * @param string $destination - * @param string|null $block - * @param array $params - * @param bool $minify * * @throws Throwable * @throws Twig_Error_Loader @@ -1454,11 +1149,11 @@ protected function render(TemplateWrapper $template, string $destination, ?strin switch ($ext) { case 'js': - $minifier = new Minify\JS($destination); + $minifier = new JS($destination); $minifier->minify($destination); break; case 'css': - $minifier = new Minify\CSS($destination); + $minifier = new CSS($destination); $minifier->minify($destination); break; default: @@ -1468,9 +1163,6 @@ protected function render(TemplateWrapper $template, string $destination, ?strin } /** - * @param string $url - * @param string $destination - * @param array $params * * @throws Exception */ @@ -1495,26 +1187,20 @@ protected function download(string $url, string $destination, array $params = [] } } - /** - * @param string|null $str - * @return string - */ protected function helperCamelCase(?string $str): string { if ($str == null) { return ''; } $str = preg_replace('/[^a-z0-9' . implode("", []) . ']+/i', ' ', $str); - $str = trim($str); + $str = trim((string) $str); $str = ucwords($str); $str = str_replace(" ", "", $str); - $str = lcfirst($str); - return $str; + return lcfirst($str); } /** - * @param array $method * @return array */ protected function getValidResponseModels(array $method): array @@ -1530,7 +1216,7 @@ protected function getValidResponseModels(array $method): array } if ( - empty($responseModels) + $responseModels === [] && !empty($method['responseModel']) && $method['responseModel'] !== 'any' ) { diff --git a/src/Spec/Spec.php b/src/Spec/Spec.php index 6bf35c294b..c1d1a74687 100644 --- a/src/Spec/Spec.php +++ b/src/Spec/Spec.php @@ -7,9 +7,9 @@ abstract class Spec extends ArrayObject { - private const SET_TYPE_ASSIGN = 'assign'; - private const SET_TYPE_PREPEND = 'prepend'; - private const SET_TYPE_APPEND = 'append'; + private const string SET_TYPE_ASSIGN = 'assign'; + private const string SET_TYPE_PREPEND = 'prepend'; + private const string SET_TYPE_APPEND = 'append'; /** * Spec constructor. @@ -134,7 +134,6 @@ public function getRequestModels(): array * * @param string $name * @param mixed $default - * @return mixed */ public function getAttribute($name, $default = null): mixed { @@ -158,10 +157,7 @@ public function getAttribute($name, $default = null): mixed * * Method for setting a specific field attribute * - * @param string $key - * @param mixed $value * @param array $parameter - * @return mixed */ public function setAttribute(string $key, mixed $value, $type = self::SET_TYPE_ASSIGN): mixed { diff --git a/src/Spec/Spec/Service.php b/src/Spec/Spec/Service.php index c57d0a09fd..6d1543e939 100644 --- a/src/Spec/Spec/Service.php +++ b/src/Spec/Spec/Service.php @@ -1,5 +1,7 @@ title; } + #[Override] public function getDescription(): string { return $this->description; } + #[Override] public function getNamespace(): string { return $this->namespace !== '' ? $this->namespace : $this->title; } + #[Override] public function getVersion(): string { return $this->version; } + #[Override] public function getEndpoint(): string { return $this->endpoint; } + #[Override] public function getEndpointDocs(): string { return $this->endpointDocs; } + #[Override] public function getLicenseName(): string { return $this->licenseName; } + #[Override] public function getLicenseURL(): string { return $this->licenseURL; } + #[Override] public function getContactName(): string { return $this->contactName; } + #[Override] public function getContactURL(): string { return $this->contactURL; } + #[Override] public function getContactEmail(): string { return $this->contactEmail; diff --git a/src/Spec/Swagger2.php b/src/Spec/Swagger2.php index febbfc8ddf..877f7aeca5 100644 --- a/src/Spec/Swagger2.php +++ b/src/Spec/Swagger2.php @@ -2,46 +2,37 @@ namespace Appwrite\Spec; +use Override; use stdClass; class Swagger2 extends Spec { - /** - * @return string - */ + #[Override] public function getTitle(): string { return $this->getAttribute('info.title', ''); } - /** - * @return string - */ + #[Override] public function getDescription(): string { return $this->getAttribute('info.description', ''); } - /** - * @return string - */ + #[Override] public function getNamespace(): string { $namespace = $this->getAttribute('info.namespace', ''); return $namespace !== '' ? $namespace : $this->getTitle(); } - /** - * @return string - */ + #[Override] public function getVersion(): string { return $this->getAttribute('info.version', ''); } - /** - * @return string - */ + #[Override] public function getEndpoint(): string { return $this->getAttribute('schemes.0', 'https') . @@ -49,9 +40,7 @@ public function getEndpoint(): string $this->getAttribute('basePath', ''); } - /** - * @return string - */ + #[Override] public function getEndpointDocs(): string { $servers = $this->getAttribute('servers', []); @@ -66,60 +55,46 @@ public function getEndpointDocs(): string $server = $servers[0]; foreach ($servers as $candidate) { - if (isset($candidate['url']) && \str_contains($candidate['url'], '{region}')) { + if (isset($candidate['url']) && \str_contains((string) $candidate['url'], '{region}')) { $server = $candidate; break; } } - return \preg_replace_callback('/\{([^}]+)\}/', function (array $matches) { - return '<' . \strtoupper($matches[1]) . '>'; - }, $server['url'] ?? '') ?? ''; + return \preg_replace_callback('/\{([^}]+)\}/', fn(array $matches): string => '<' . \strtoupper($matches[1]) . '>', $server['url'] ?? '') ?? ''; } - /** - * @return string - */ + #[Override] public function getLicenseName(): string { return $this->getAttribute('info.license.name', ''); } - /** - * @return string - */ + #[Override] public function getLicenseURL(): string { return $this->getAttribute('info.license.url', ''); } - /** - * @return string - */ + #[Override] public function getContactName(): string { return $this->getAttribute('info.contact.name', ''); } - /** - * @return string - */ + #[Override] public function getContactURL(): string { return $this->getAttribute('info.contact.url', ''); } - /** - * @return string - */ + #[Override] public function getContactEmail(): string { return $this->getAttribute('info.contact.email', ''); } - /** - * @return array - */ + #[Override] public function getServices(): array { $list = []; @@ -131,7 +106,7 @@ public function getServices(): array foreach ($path as $method) { if (isset($method['tags'])) { foreach ($method['tags'] as $tag) { - if (!array_key_exists($tag, $list)) { + if (!array_key_exists((string) $tag, $list)) { $methods = $this->getMethods($tag); $list[$tag] = [ 'name' => $tag, @@ -161,10 +136,10 @@ protected function parseMethod(string $methodName, string $pathName, array $meth $methodSecurity = $method['security'][0] ?? []; foreach ($methodAuth as $i => $node) { - $methodAuth[$i] = (array_key_exists($i, $security)) ? [...$security[$i], 'global' => $i !== 'Project'] : []; + $methodAuth[$i] = (array_key_exists((string) $i, $security)) ? [...$security[$i], 'global' => $i !== 'Project'] : []; } foreach ($methodSecurity as $i => $node) { - $methodSecurity[$i] = (array_key_exists($i, $security)) ? [...$security[$i], 'global' => $i !== 'Project'] : []; + $methodSecurity[$i] = (array_key_exists((string) $i, $security)) ? [...$security[$i], 'global' => $i !== 'Project'] : []; } $methodSecurityHeaders = []; @@ -198,7 +173,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth // check for union types if (isset($desc['schema']['x-oneOf'])) { $responseModels = \array_map( - fn($schema) => $this->normalizeSchemaRef($schema['$ref'] ?? ''), + fn(array $schema): string => $this->normalizeSchemaRef($schema['$ref'] ?? ''), $desc['schema']['x-oneOf'] ); @@ -206,7 +181,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth // set to first model // for backward compatibility - if (!empty($responseModels)) { + if ($responseModels !== []) { $responseModel = $responseModels[0]; } } @@ -331,7 +306,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth ]; $default = $value['default'] ?? null; if ($temp['type'] === 'object' && is_array($default)) { - $default = (empty($default)) ? new stdClass() : $default; + $default = ($default === []) ? new stdClass() : $default; } if (isset($value['enum'])) { @@ -356,9 +331,7 @@ protected function parseMethod(string $methodName, string $pathName, array $meth $output['parameters']['all'][] = $param; } - usort($output['parameters']['all'], function ($a, $b) { - return $b['required'] - $a['required']; - }); + usort($output['parameters']['all'], fn(array $a, array $b): int => (int) $b['required'] - (int) $a['required']); return $output; } @@ -397,13 +370,13 @@ protected function parseUnionDiscriminator(array $schema): array $mapping[$modelName] = \array_filter( $conditions, - fn($value) => $value !== null + fn($value): bool => $value !== null ); } } if ( - empty($mapping) + $mapping === [] && isset($discriminator['propertyName'], $discriminator['mapping']) && \is_array($discriminator['mapping']) ) { @@ -420,7 +393,7 @@ protected function parseUnionDiscriminator(array $schema): array } } - if (empty($mapping)) { + if ($mapping === []) { return []; } @@ -434,21 +407,19 @@ protected function parseUnionDiscriminator(array $schema): array $cases[$modelName] = $mapping[$modelName]; } - if (empty($cases)) { + if ($cases === []) { $cases = $mapping; } - uksort($cases, function (string $left, string $right) use ($cases): int { - return \count($cases[$right]) <=> \count($cases[$left]); - }); + uksort($cases, fn(string $left, string $right): int => \count($cases[$right]) <=> \count($cases[$left])); return $cases; } /** * @param string $service - * @return array */ + #[Override] public function getMethods($service): array { $list = []; @@ -495,9 +466,8 @@ public function getMethods($service): array * @param $pathName * @param $method * @param $additionalMethod - * @return array */ - private function handleAdditionalMethods($methodName, $pathName, $method, $additionalMethod): array + private function handleAdditionalMethods(string $methodName, string $pathName, $method, array $additionalMethod): array { $duplicatedMethod = $method; $duplicatedMethod['x-appwrite']['method'] = $additionalMethod['name']; @@ -522,7 +492,7 @@ private function handleAdditionalMethods($methodName, $pathName, $method, $addit if (\is_array($desc['model'])) { $convertedResponse[$code] = [ 'schema' => [ - 'x-oneOf' => \array_map(fn($model) => [ + 'x-oneOf' => \array_map(fn($model): array => [ '$ref' => $model, ], $desc['model']), ], @@ -541,7 +511,7 @@ private function handleAdditionalMethods($methodName, $pathName, $method, $addit $duplicatedMethod['responses'] = $convertedResponse; // Remove non-whitelisted parameters on body parameters, also set required. - $handleParams = function (&$params) use ($additionalMethod) { + $handleParams = function (array &$params) use ($additionalMethod): void { if (!empty($additionalMethod['parameters'])) { foreach ($params as $key => $param) { if (empty($param['in']) || $param['in'] !== 'body' || empty($param['schema']['properties'])) { @@ -562,7 +532,9 @@ private function handleAdditionalMethods($methodName, $pathName, $method, $addit } }; - $handleParams($duplicatedMethod['parameters']); + if (is_array($duplicatedMethod['parameters'] ?? null)) { + $handleParams($duplicatedMethod['parameters']); + } // Overwrite description and name if method has one if (!empty($additionalMethod['name'])) { @@ -576,19 +548,13 @@ private function handleAdditionalMethods($methodName, $pathName, $method, $addit return $this->parseMethod($methodName, $pathName, $duplicatedMethod); } - /** - * @param array $method - * @param string $service - * @return string - */ + #[Override] public function getTargetNamespace(array $method, string $service): string { return $method['namespace'] ?? $service; } - /** - * @return array - */ + #[Override] public function getGlobalHeaders(): array { $list = []; @@ -622,6 +588,7 @@ public function getGlobalHeaders(): array return $list; } + #[Override] public function getDefinitions(): array { $list = []; @@ -657,22 +624,22 @@ public function getDefinitions(): array if (isset($def['items']['x-anyOf'])) { //nested model - $model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']); + $model['properties'][$name]['sub_schemas'] = \array_map(fn(array $schema): string|array => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-anyOf']); } if (isset($def['items']['x-oneOf'])) { //nested model - $model['properties'][$name]['sub_schemas'] = \array_map(fn($schema) => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']); + $model['properties'][$name]['sub_schemas'] = \array_map(fn(array $schema): string|array => str_replace('#/definitions/', '', $schema['$ref']), $def['items']['x-oneOf']); } if (isset($def['enum'])) { // enum property $model['properties'][$name]['enum'] = $def['enum']; - $model['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst($key) . ucfirst($name); + $model['properties'][$name]['enumName'] = $def['x-enum-name'] ?? ucfirst((string) $key) . ucfirst((string) $name); $model['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? []; } elseif (($model['properties'][$name]['type'] ?? null) === 'array' && isset($def['items']['enum'])) { $model['properties'][$name]['enumValues'] = $def['items']['enum']; - $model['properties'][$name]['enumName'] = $def['items']['x-enum-name'] ?? ucfirst($key) . ucfirst($name); + $model['properties'][$name]['enumName'] = $def['items']['x-enum-name'] ?? ucfirst((string) $key) . ucfirst((string) $name); $model['properties'][$name]['enumKeys'] = $def['items']['x-enum-keys'] ?? []; } } @@ -684,9 +651,8 @@ public function getDefinitions(): array /** * Get request model definitions - * - * @return array */ + #[Override] public function getRequestModels(): array { $list = []; @@ -717,20 +683,20 @@ public function getRequestModels(): array } if (isset($def['items']['x-anyOf'])) { - $model['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(array $schema): string|array => 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']); + $model['properties'][$name]['sub_schemas'] = \array_map(fn(array $schema): string|array => 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]['enumName'] = $def['x-enum-name'] ?? ucfirst((string) $key) . ucfirst((string) $name); $model['properties'][$name]['enumKeys'] = $def['x-enum-keys'] ?? []; } elseif (($model['properties'][$name]['type'] ?? null) === 'array' && isset($def['items']['enum'])) { $model['properties'][$name]['enumValues'] = $def['items']['enum']; - $model['properties'][$name]['enumName'] = $def['items']['x-enum-name'] ?? ucfirst($key) . ucfirst($name); + $model['properties'][$name]['enumName'] = $def['items']['x-enum-name'] ?? ucfirst((string) $key) . ucfirst((string) $name); $model['properties'][$name]['enumKeys'] = $def['items']['x-enum-keys'] ?? []; } } @@ -740,14 +706,12 @@ public function getRequestModels(): array return $list; } - /** - * @return array - */ + #[Override] public function getRequestEnums(): array { $list = []; - foreach ($this->getServices() as $key => $service) { + foreach (array_keys($this->getServices()) as $key) { foreach ($this->getMethods($key) as $method) { if (isset($method['parameters']) && is_array($method['parameters'])) { foreach ($method['parameters']['all'] as $parameter) { @@ -768,9 +732,7 @@ public function getRequestEnums(): array return \array_values($list); } - /** - * @return array - */ + #[Override] public function getResponseEnums(): array { $list = []; @@ -780,7 +742,7 @@ public function getResponseEnums(): array if (isset($model['properties']) && is_array($model['properties'])) { foreach ($model['properties'] as $propertyName => $property) { if (isset($property['enum'])) { - $enumName = $property['x-enum-name'] ?? ucfirst($modelName) . ucfirst($propertyName); + $enumName = $property['x-enum-name'] ?? ucfirst((string) $modelName) . ucfirst((string) $propertyName); if (!isset($list[$enumName])) { $list[$enumName] = [ @@ -795,7 +757,7 @@ public function getResponseEnums(): array if ((($property['type'] ?? null) === 'array') && isset($property['items']['enum'])) { $enumName = $property['items']['x-enum-name'] ?? $property['enumName'] - ?? ucfirst($modelName) . ucfirst($propertyName); + ?? ucfirst((string) $modelName) . ucfirst((string) $propertyName); if (!isset($list[$enumName])) { $list[$enumName] = [ @@ -812,9 +774,6 @@ public function getResponseEnums(): array return \array_values($list); } - /** - * @return array - */ public function getRequestModelEnums(): array { $list = []; @@ -826,7 +785,7 @@ public function getRequestModelEnums(): array foreach ($model['properties'] as $propertyName => $property) { if (isset($property['enum'])) { - $enumName = $property['enumName'] ?? ucfirst($modelName) . ucfirst($propertyName); + $enumName = $property['enumName'] ?? ucfirst((string) $modelName) . ucfirst((string) $propertyName); if (!isset($list[$enumName])) { $list[$enumName] = [ @@ -838,7 +797,7 @@ public function getRequestModelEnums(): array } if ((($property['type'] ?? null) === 'array') && isset($property['enumValues'])) { - $enumName = $property['enumName'] ?? ucfirst($modelName) . ucfirst($propertyName); + $enumName = $property['enumName'] ?? ucfirst((string) $modelName) . ucfirst((string) $propertyName); if (!isset($list[$enumName])) { $list[$enumName] = [ @@ -854,9 +813,6 @@ public function getRequestModelEnums(): array return \array_values($list); } - /** - * @return array - */ public function getAllEnums(): array { $list = []; diff --git a/templates/markdown/.github/workflows/publish.yml b/templates/markdown/.github/workflows/publish.yml deleted file mode 100644 index 9b268ab4c0..0000000000 --- a/templates/markdown/.github/workflows/publish.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Publish to NPM - -on: - release: - types: [published] - workflow_dispatch: - -permissions: - id-token: write - contents: read - -jobs: - publish: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Use Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: '24.14.1' - registry-url: 'https://registry.npmjs.org' - - - name: Pin npm for trusted publishing - run: npm install -g npm@11.10.0 - - - name: Determine release tag - id: release_tag - run: | - if [[ "${{ github.ref }}" == *"-rc"* ]] || [[ "${{ github.ref }}" == *"-RC"* ]]; then - echo "tag=next" >> "$GITHUB_OUTPUT" - else - echo "tag=latest" >> "$GITHUB_OUTPUT" - fi - - - name: Install dependencies - run: npm install - - - name: Publish - run: npm publish --provenance --access public --tag ${{ steps.release_tag.outputs.tag }} diff --git a/templates/markdown/.gitignore b/templates/markdown/.gitignore deleted file mode 100644 index df4ac7351a..0000000000 --- a/templates/markdown/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -# Dependencies -node_modules/ - -# Build output -dist/ - -# Lock files (optional - remove if you want to commit) -package-lock.json - -# OS files -.DS_Store - -# IDE -.idea/ -.vscode/ -*.swp -*.swo - -# Debug logs -npm-debug.log* diff --git a/templates/markdown/.npmrc b/templates/markdown/.npmrc deleted file mode 100644 index 7253a5ceee..0000000000 --- a/templates/markdown/.npmrc +++ /dev/null @@ -1 +0,0 @@ -min-release-age=7 diff --git a/templates/markdown/README.md.twig b/templates/markdown/README.md.twig deleted file mode 100644 index 08cbab1892..0000000000 --- a/templates/markdown/README.md.twig +++ /dev/null @@ -1,130 +0,0 @@ -# {{ spec.title }} {{ sdk.name }} SDK - -![License](https://img.shields.io/github/license/{{ sdk.gitUserName|url_encode }}/{{ sdk.gitRepoName|url_encode }}.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-{{ spec.version|url_encode }}-blue.svg?style=flat-square) -{% if sdk.twitterHandle %} -[![Twitter Account](https://img.shields.io/twitter/follow/{{ sdk.twitterHandle }}?color=00acee&label=twitter&style=flat-square)](https://twitter.com/{{ sdk.twitterHandle }}) -{% endif %} -{% if sdk.discordChannel %} -[![Discord](https://img.shields.io/discord/{{ sdk.discordChannel }}?label=discord&style=flat-square)]({{ sdk.discordUrl }}) -{% endif %} -{% if sdk.warning %} - -{{ sdk.warning|raw }} -{% endif %} - -{{ sdk.description }} - -This SDK provides programmatic access to {{ spec.title }} documentation, designed for AI consumption with lazy-loading and search capabilities. - -## Installation - -```bash -npm install {{ language.params.npmPackage }} -``` - -## Usage - -### Get Table of Contents - -Retrieve a lightweight table of contents without loading document content: - -```typescript -import { getTableOfContents } from '{{ language.params.npmPackage }}'; - -const toc = getTableOfContents('typescript'); -// → { language: 'typescript', services: [{ name: 'account', methods: [...] }] } -``` - -### Get Specific Documentation - -Load a specific markdown document by path: - -```typescript -import { getMarkdown } from '{{ language.params.npmPackage }}'; - -const doc = await getMarkdown('typescript', 'account/create-session'); -// → '# createSession\n\nDescription: ...' -``` - -### Search Documentation - -Search across all documents by keywords: - -```typescript -import { searchDocs } from '{{ language.params.npmPackage }}'; - -const results = await searchDocs('typescript', 'MFA authentication', { limit: 5 }); -// → [{ path: 'account/create-mfa-...', title: '...', snippet: '...' }] -``` - -### Using the SDK Class - -For more control, instantiate the SDK class directly: - -```typescript -import { DocsSDK } from '{{ language.params.npmPackage }}'; - -const sdk = new DocsSDK(); - -// Get all available languages -const languages = sdk.getLanguages(); - -// Load all docs for a service -const accountDocs = await sdk.getServiceDocs('typescript', 'account'); - -// Clear cache to free memory -sdk.clearCache(); -``` - -## API Reference - -### Functions - -| Function | Description | -|----------|-------------| -| `getLanguages()` | Get list of available SDK languages | -| `getTableOfContents(language)` | Get TOC for a language (lightweight, no content) | -| `getMarkdown(language, path)` | Get specific markdown document | -| `searchDocs(language, query, options?)` | Search documentation by keywords | - -### DocsSDK Class - -| Method | Description | -|--------|-------------| -| `getLanguages()` | Get list of available SDK languages | -| `getTableOfContents(language)` | Get TOC for a language | -| `getMarkdown(language, path)` | Get specific markdown document (cached) | -| `getServiceDocs(language, service)` | Get all docs for a service | -| `searchDocs(language, query, options?)` | Search documentation | -| `clearCache()` | Clear the content cache | -| `getCacheStats()` | Get cache statistics | - -### Search Options - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `limit` | number | 10 | Maximum results to return | -| `minScore` | number | 0.1 | Minimum relevance score (0-1) | -| `services` | string[] | all | Filter to specific services | - -## Building from Source - -```bash -# Install dependencies -npm install - -# Generate manifest from markdown files -npm run build:manifest - -# Compile TypeScript -npm run build -``` - -## Contribution - -This library is auto-generated by the custom [SDK Generator](https://github.com/appwrite/sdk-generator). To learn more about how you can help us improve this SDK, please check the [contribution guide](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md) before sending a pull-request. - -## License - -Please see the [{{ spec.licenseName }}]({{ spec.licenseURL }}) license file for more information. diff --git a/templates/markdown/package.json b/templates/markdown/package.json deleted file mode 100644 index 50f3c22f98..0000000000 --- a/templates/markdown/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "sdk-for-markdown", - "version": "0.1.0", - "description": "Markdown documentation SDK for AI consumption", - "type": "module", - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "files": [ - "dist", - "docs" - ], - "scripts": { - "build": "tsc", - "build:manifest": "tsx scripts/build-manifest.ts", - "prepublishOnly": "npm run build:manifest && npm run build" - }, - "keywords": [ - "sdk", - "documentation", - "markdown", - "ai" - ], - "license": "MIT", - "devDependencies": { - "@types/node": "20.11.25", - "tsx": "4.19.2", - "typescript": "5.4.2" - } -} diff --git a/templates/markdown/package.json.twig b/templates/markdown/package.json.twig deleted file mode 100644 index 0e74b49bef..0000000000 --- a/templates/markdown/package.json.twig +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "{{ language.params.npmPackage | caseDash }}", - "version": "{{ sdk.version }}", - "description": "{{ sdk.shortDescription }}", - "homepage": "{{ spec.contactURL }}", - "type": "module", - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "files": [ - "dist", - "docs" - ], - "scripts": { - "build": "tsc", - "build:manifest": "tsx scripts/build-manifest.ts", - "prepublishOnly": "npm run build:manifest && npm run build" - }, - "keywords": [ - "{{ spec.title | caseLower }}", - "sdk", - "documentation", - "markdown", - "ai" - ], - "license": "{{ sdk.license }}", - "repository": { - "type": "git", - "url": "{{ sdk.gitURL }}" - }, - "devDependencies": { - "@types/node": "20.11.25", - "tsx": "4.19.2", - "typescript": "5.4.2" - } -} diff --git a/templates/markdown/scripts/build-manifest.ts b/templates/markdown/scripts/build-manifest.ts deleted file mode 100644 index 82be9e0f6e..0000000000 --- a/templates/markdown/scripts/build-manifest.ts +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env tsx -/** - * Build script to generate the manifest from markdown files. - * Run with: npx tsx scripts/build-manifest.ts - */ - -import { readdir, readFile, writeFile, stat } from 'fs/promises'; -import { join, dirname } from 'path'; -import { fileURLToPath } from 'url'; -import type { - Manifest, - TableOfContents, - ServiceEntry, - MethodEntry, -} from '../src/types.js'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const ROOT = join(__dirname, '..'); -const DOCS_DIR = join(ROOT, 'docs'); -const MANIFEST_OUTPUT = join(ROOT, 'src', 'manifest.ts'); -const GENERATED_TYPES_OUTPUT = join(ROOT, 'src', 'generated-types.ts'); - -/** - * Convert kebab-case to Title Case - */ -function kebabToTitle(kebab: string): string { - return kebab - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - -/** - * Extract metadata from markdown content - */ -function extractMetadata(content: string): { - title: string; - description: string; - deprecated: boolean; -} { - const lines = content.split('\n'); - - // Extract title from first H1 - const titleLine = lines.find((line) => line.startsWith('# ')); - const title = titleLine ? titleLine.replace(/^#\s+/, '').trim() : ''; - - // Check for deprecation notice - const deprecated = content.includes('> ⚠️ This method is deprecated'); - - // Extract description - look for first paragraph after title - let description = ''; - let foundTitle = false; - for (const line of lines) { - if (line.startsWith('# ')) { - foundTitle = true; - continue; - } - if (foundTitle && line.trim() && !line.startsWith('#') && !line.startsWith('>')) { - description = line - .replace(/\*\*/g, '') - .replace(/`/g, '') - .trim() - .slice(0, 200); - break; - } - } - - return { title, description, deprecated }; -} - -/** - * Check if a path is a directory - */ -async function isDirectory(path: string): Promise { - try { - const stats = await stat(path); - return stats.isDirectory(); - } catch { - return false; - } -} - -/** - * Build manifest for a single language - */ -async function buildLanguageManifest( - language: string, - languagePath: string -): Promise { - const services: ServiceEntry[] = []; - - // Read all service directories - const entries = await readdir(languagePath); - - for (const serviceName of entries.sort()) { - const servicePath = join(languagePath, serviceName); - - if (!(await isDirectory(servicePath))) continue; - - const methods: MethodEntry[] = []; - const files = await readdir(servicePath); - - for (const file of files.sort()) { - if (!file.endsWith('.md')) continue; - - const methodName = file.replace('.md', ''); - const filePath = join(servicePath, file); - const content = await readFile(filePath, 'utf-8'); - const { title, description, deprecated } = extractMetadata(content); - - methods.push({ - name: methodName, - title: title || kebabToTitle(methodName), - ...(description && { description }), - ...(deprecated && { deprecated }), - }); - } - - if (methods.length > 0) { - services.push({ - name: serviceName, - title: kebabToTitle(serviceName), - methods, - }); - } - } - - return { - language, - generatedAt: new Date().toISOString(), - services, - }; -} - -/** - * Generate TypeScript type definitions from the manifest - */ -function generateTypeDefinitions(manifest: Manifest): string { - const languages = Object.keys(manifest); - - if (languages.length === 0) { - return `/** - * Auto-generated type definitions for type-safe SDK usage. - * Generated at: ${new Date().toISOString()} - */ - -/** No languages available - manifest is empty */ -export type Language = never; - -/** Method paths mapped by language */ -export type MethodPaths = Record; - -/** Service names mapped by language */ -export type ServiceNames = Record; -`; - } - - // Generate Language union type - const languageUnion = languages.map((l) => `'${l}'`).join(' | '); - - // Generate MethodPaths type - maps each language to its valid paths - const methodPathsEntries = languages.map((lang) => { - const toc = manifest[lang]; - const paths: string[] = []; - for (const service of toc.services) { - for (const method of service.methods) { - paths.push(`${service.name}/${method.name}`); - } - } - if (paths.length === 0) { - return ` ${lang}: never;`; - } - const pathUnion = paths.map((p) => `'${p}'`).join(' | '); - return ` ${lang}: ${pathUnion};`; - }); - - // Generate ServiceNames type - maps each language to its valid service names - const serviceNamesEntries = languages.map((lang) => { - const toc = manifest[lang]; - const services = toc.services.map((s) => s.name); - if (services.length === 0) { - return ` ${lang}: never;`; - } - const serviceUnion = services.map((s) => `'${s}'`).join(' | '); - return ` ${lang}: ${serviceUnion};`; - }); - - return `/** - * Auto-generated type definitions for type-safe SDK usage. - * This file is generated by scripts/build-manifest.ts - * - * Generated at: ${new Date().toISOString()} - */ - -/** Available SDK languages */ -export type Language = ${languageUnion}; - -/** Method paths mapped by language */ -export interface MethodPaths { -${methodPathsEntries.join('\n')} -} - -/** Service names mapped by language */ -export interface ServiceNames { -${serviceNamesEntries.join('\n')} -} -`; -} - -/** - * Main build function - */ -async function build(): Promise { - console.log('Building manifest from markdown files...\n'); - - const manifest: Manifest = {}; - - // Check if docs directory exists - let docsExists = false; - try { - await stat(DOCS_DIR); - docsExists = true; - } catch { - console.log(`No docs directory found at ${DOCS_DIR}`); - console.log('Creating empty manifest...\n'); - } - - if (docsExists) { - // Read all language directories - const languages = await readdir(DOCS_DIR); - - for (const language of languages) { - const languagePath = join(DOCS_DIR, language); - - if (!(await isDirectory(languagePath))) continue; - - console.log(`Processing language: ${language}`); - manifest[language] = await buildLanguageManifest(language, languagePath); - - const methodCount = manifest[language].services.reduce( - (sum, s) => sum + s.methods.length, - 0 - ); - console.log( - ` → ${manifest[language].services.length} services, ${methodCount} methods\n` - ); - } - } - - // Generate the manifest.ts file - const output = `import type { Manifest } from './types.js'; - -/** - * Pre-generated manifest containing table of contents for all languages. - * This is populated at build time by scripts/build-manifest.ts - * - * Generated at: ${new Date().toISOString()} - */ -export const manifest: Manifest = ${JSON.stringify(manifest, null, 2)}; -`; - - await writeFile(MANIFEST_OUTPUT, output, 'utf-8'); - console.log(`Manifest written to ${MANIFEST_OUTPUT}`); - - // Generate and write type definitions - const typesOutput = generateTypeDefinitions(manifest); - await writeFile(GENERATED_TYPES_OUTPUT, typesOutput, 'utf-8'); - console.log(`Type definitions written to ${GENERATED_TYPES_OUTPUT}`); - - // Print summary - const totalLanguages = Object.keys(manifest).length; - const totalServices = Object.values(manifest).reduce( - (sum, lang) => sum + lang.services.length, - 0 - ); - const totalMethods = Object.values(manifest).reduce( - (sum, lang) => - sum + lang.services.reduce((s, svc) => s + svc.methods.length, 0), - 0 - ); - - console.log('\nSummary:'); - console.log(` Languages: ${totalLanguages}`); - console.log(` Services: ${totalServices}`); - console.log(` Methods: ${totalMethods}`); -} - -build().catch((error) => { - console.error('Build failed:', error); - process.exit(1); -}); diff --git a/templates/markdown/src/generated-types.ts b/templates/markdown/src/generated-types.ts deleted file mode 100644 index a66e475411..0000000000 --- a/templates/markdown/src/generated-types.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Auto-generated type definitions for type-safe SDK usage. - * This file is generated by scripts/build-manifest.ts - * - * Run `npm run build:manifest` to regenerate this file. - */ - -/** No languages available - manifest is empty */ -export type Language = never; - -/** Method paths mapped by language */ -export type MethodPaths = Record; - -/** Service names mapped by language */ -export type ServiceNames = Record; diff --git a/templates/markdown/src/index.ts b/templates/markdown/src/index.ts deleted file mode 100644 index 8203600b99..0000000000 --- a/templates/markdown/src/index.ts +++ /dev/null @@ -1,390 +0,0 @@ -import { readFile } from 'fs/promises'; -import { join, dirname } from 'path'; -import { fileURLToPath } from 'url'; -import { manifest } from './manifest.js'; -import type { - TableOfContents, - SearchResult, - SearchOptions, - SDKOptions, -} from './types.js'; -import type { Language, MethodPaths, ServiceNames } from './generated-types.js'; - -export type { - TableOfContents, - SearchResult, - SearchOptions, - SDKOptions, - Manifest, - MethodEntry, - ServiceEntry, -} from './types.js'; - -export type { Language, MethodPaths, ServiceNames } from './generated-types.js'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -/** - * SDK for accessing documentation programmatically. - * Designed for AI consumption with lazy-loading and search capabilities. - */ -export class DocsSDK { - private docsPath: string; - private contentCache: Map = new Map(); - - constructor(options: SDKOptions = {}) { - this.docsPath = options.docsPath ?? join(__dirname, '..', 'docs'); - } - - /** - * Get the list of available SDK languages - */ - getLanguages(): Language[] { - return Object.keys(manifest) as Language[]; - } - - /** - * Get the table of contents for a specific language. - * This is lightweight and doesn't load any markdown content. - * - * @param language - SDK language (e.g., "typescript") - * @param options - Optional filter options - * @param options.service - Filter to a specific service - * @param options.skipDeprecated - Exclude deprecated methods from results - * @returns Table of contents with services and methods - * - * @example - * ```ts - * const toc = sdk.getTableOfContents("typescript"); - * // → { language: "typescript", services: [{ name: "account", methods: [...] }] } - * - * const accountToc = sdk.getTableOfContents("typescript", { service: "account" }); - * // → { language: "typescript", services: [{ name: "account", methods: [...] }] } - * - * const nonDeprecated = sdk.getTableOfContents("typescript", { skipDeprecated: true }); - * // → excludes deprecated methods - * ``` - */ - getTableOfContents( - language: L, - options?: { service?: ServiceNames[L]; skipDeprecated?: boolean } - ): TableOfContents | null { - const toc = manifest[language]; - if (!toc) return null; - - const { service, skipDeprecated } = options ?? {}; - - let services = toc.services; - - if (service) { - const filteredService = services.find((s) => s.name === service); - if (!filteredService) return null; - services = [filteredService]; - } - - if (skipDeprecated) { - services = services.map((s) => ({ - ...s, - methods: s.methods.filter((m) => !m.deprecated), - })); - } - - return { - ...toc, - services, - }; - } - - /** - * Get a specific markdown document by path. - * Content is loaded lazily and cached for subsequent requests. - * - * @param language - SDK language (e.g., "typescript") - * @param path - Document path (e.g., "account/create-session") - * @returns Markdown content or null if not found - * - * @example - * ```ts - * const content = await sdk.getMarkdown("typescript", "account/create-session"); - * // → "# Create Session\n\nDescription: ..." - * ``` - */ - async getMarkdown( - language: L, - path: MethodPaths[L] - ): Promise { - const cacheKey = `${language}/${path}`; - - if (this.contentCache.has(cacheKey)) { - return this.contentCache.get(cacheKey)!; - } - - try { - const filePath = join(this.docsPath, language, `${path}.md`); - const content = await readFile(filePath, 'utf-8'); - this.contentCache.set(cacheKey, content); - return content; - } catch { - return null; - } - } - - /** - * Get all markdown documents for a service. - * - * @param language - SDK language (e.g., "typescript") - * @param service - Service name (e.g., "account") - * @returns Map of method names to markdown content - * - * @example - * ```ts - * const docs = await sdk.getServiceDocs("typescript", "account"); - * // → Map { "create-session" => "# Create Session\n...", ... } - * ``` - */ - async getServiceDocs( - language: L, - service: ServiceNames[L] - ): Promise> { - const toc = this.getTableOfContents(language); - const result = new Map(); - - if (!toc) return result; - - const serviceEntry = toc.services.find((s) => s.name === service); - if (!serviceEntry) return result; - - await Promise.all( - serviceEntry.methods.map(async (method) => { - const path = `${service}/${method.name}` as MethodPaths[L]; - const content = await this.getMarkdown(language, path); - if (content) { - result.set(method.name, content); - } - }) - ); - - return result; - } - - /** - * Search documentation by keywords. - * Performs a simple text-based search across all documents. - * - * @param language - SDK language (e.g., "typescript") - * @param query - Search query - * @param options - Search options - * @returns Array of search results sorted by relevance - * - * @example - * ```ts - * const results = await sdk.searchDocs("typescript", "MFA authentication"); - * // → [{ path: "account/create-mfa-...", title: "...", snippet: "..." }] - * ``` - */ - async searchDocs( - language: L, - query: string, - options: SearchOptions = {} - ): Promise { - const { limit = 10, minScore = 0.1, services: filterServices } = options; - const toc = this.getTableOfContents(language); - - if (!toc) return []; - - const results: SearchResult[] = []; - const queryTerms = query.toLowerCase().split(/\s+/).filter(Boolean); - - const servicesToSearch = filterServices - ? toc.services.filter((s) => filterServices.includes(s.name)) - : toc.services; - - await Promise.all( - servicesToSearch.flatMap((service) => - service.methods.map(async (method) => { - const path = `${service.name}/${method.name}` as MethodPaths[L]; - const content = await this.getMarkdown(language, path); - - if (!content) return; - - const score = this.calculateScore(content, queryTerms, method.title); - - if (score >= minScore) { - results.push({ - path, - title: method.title, - snippet: this.extractSnippet(content, queryTerms), - service: service.name, - method: method.name, - score, - }); - } - }) - ) - ); - - return results.sort((a, b) => b.score - a.score).slice(0, limit); - } - - /** - * Calculate relevance score for a document - */ - private calculateScore( - content: string, - queryTerms: string[], - title: string - ): number { - const lowerContent = content.toLowerCase(); - const lowerTitle = title.toLowerCase(); - - let score = 0; - let matchedTerms = 0; - - for (const term of queryTerms) { - // Title matches are weighted higher - if (lowerTitle.includes(term)) { - score += 0.4; - matchedTerms++; - } - - // Count content occurrences - const regex = new RegExp(term, 'gi'); - const matches = lowerContent.match(regex); - if (matches) { - // Diminishing returns for multiple matches - score += Math.min(matches.length * 0.1, 0.3); - matchedTerms++; - } - } - - // Bonus for matching all terms - if (matchedTerms === queryTerms.length * 2) { - score += 0.2; - } - - return Math.min(score, 1); - } - - /** - * Extract a relevant snippet containing query terms - */ - private extractSnippet(content: string, queryTerms: string[]): string { - const lines = content.split('\n').filter((line) => line.trim()); - const snippetLength = 150; - - // Find the first line containing any query term - for (const line of lines) { - const lowerLine = line.toLowerCase(); - if (queryTerms.some((term) => lowerLine.includes(term))) { - // Clean markdown formatting - const cleaned = line - .replace(/^#+\s*/, '') - .replace(/\*\*/g, '') - .replace(/`/g, '') - .trim(); - - if (cleaned.length <= snippetLength) { - return cleaned; - } - - // Find the position of the first matching term - const termPositions = queryTerms - .map((term) => lowerLine.indexOf(term)) - .filter((pos) => pos !== -1); - const firstMatch = Math.min(...termPositions); - - const start = Math.max(0, firstMatch - 50); - const end = Math.min(cleaned.length, start + snippetLength); - - let snippet = cleaned.slice(start, end); - if (start > 0) snippet = '...' + snippet; - if (end < cleaned.length) snippet = snippet + '...'; - - return snippet; - } - } - - // Fallback: return the first meaningful content line - const descriptionLine = lines.find( - (line) => - !line.startsWith('#') && !line.startsWith('|') && line.length > 20 - ); - - if (descriptionLine) { - const cleaned = descriptionLine.replace(/\*\*/g, '').replace(/`/g, ''); - return cleaned.length > snippetLength - ? cleaned.slice(0, snippetLength) + '...' - : cleaned; - } - - return ''; - } - - /** - * Clear the content cache to free memory - */ - clearCache(): void { - this.contentCache.clear(); - } - - /** - * Get cache statistics - */ - getCacheStats(): { size: number; entries: string[] } { - return { - size: this.contentCache.size, - entries: Array.from(this.contentCache.keys()), - }; - } -} - -// Convenience functions for direct usage without instantiation -let defaultInstance: DocsSDK | null = null; - -function getDefaultInstance(): DocsSDK { - if (!defaultInstance) { - defaultInstance = new DocsSDK(); - } - return defaultInstance; -} - -/** - * Get the table of contents for a specific language - */ -export function getTableOfContents( - language: L, - options?: { service?: ServiceNames[L]; skipDeprecated?: boolean } -): TableOfContents | null { - return getDefaultInstance().getTableOfContents(language, options); -} - -/** - * Get a specific markdown document - */ -export function getMarkdown( - language: L, - path: MethodPaths[L] -): Promise { - return getDefaultInstance().getMarkdown(language, path); -} - -/** - * Search documentation by keywords - */ -export function searchDocs( - language: L, - query: string, - options?: SearchOptions -): Promise { - return getDefaultInstance().searchDocs(language, query, options); -} - -/** - * Get list of available languages - */ -export function getLanguages(): Language[] { - return getDefaultInstance().getLanguages(); -} - -// Default export -export default DocsSDK; diff --git a/templates/markdown/src/manifest.ts b/templates/markdown/src/manifest.ts deleted file mode 100644 index 562a923cb0..0000000000 --- a/templates/markdown/src/manifest.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Manifest } from './types.js'; - -/** - * Pre-generated manifest containing table of contents for all languages. - * This is populated at build time by scripts/build-manifest.ts - * - * Generated at: 2026-01-21T05:36:22.048Z - */ -export const manifest: Manifest = {}; diff --git a/templates/markdown/src/types.ts b/templates/markdown/src/types.ts deleted file mode 100644 index f89f8a38b7..0000000000 --- a/templates/markdown/src/types.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Represents a single method/endpoint documentation entry - */ -export interface MethodEntry { - /** Method name in kebab-case (e.g., "create-session") */ - name: string; - /** Human-readable title (e.g., "Create Session") */ - title: string; - /** Brief description of the method */ - description?: string; - /** Whether this method is deprecated */ - deprecated?: boolean; -} - -/** - * Represents a service containing multiple methods - */ -export interface ServiceEntry { - /** Service name in lowercase (e.g., "account") */ - name: string; - /** Human-readable service title (e.g., "Account") */ - title: string; - /** Brief description of the service */ - description?: string; - /** List of methods in this service */ - methods: MethodEntry[]; -} - -/** - * Table of contents for a specific SDK language - */ -export interface TableOfContents { - /** SDK language identifier (e.g., "typescript") */ - language: string; - /** Version of the SDK documentation */ - version?: string; - /** Timestamp when the manifest was generated */ - generatedAt: string; - /** List of services with their methods */ - services: ServiceEntry[]; -} - -/** - * Search result for a documentation query - */ -export interface SearchResult { - /** Full path to the document (e.g., "account/create-session") */ - path: string; - /** Document title */ - title: string; - /** Matching text snippet */ - snippet: string; - /** Service name */ - service: string; - /** Method name */ - method: string; - /** Relevance score (higher is better) */ - score: number; -} - -/** - * Options for search queries - */ -export interface SearchOptions { - /** Maximum number of results to return */ - limit?: number; - /** Minimum relevance score threshold (0-1) */ - minScore?: number; - /** Filter to specific services */ - services?: string[]; -} - -/** - * Complete manifest structure stored in manifest.json - */ -export interface Manifest { - [language: string]: TableOfContents; -} - -/** - * Options for initializing the SDK - */ -export interface SDKOptions { - /** Custom path to the docs directory */ - docsPath?: string; -} diff --git a/templates/markdown/tsconfig.json b/templates/markdown/tsconfig.json deleted file mode 100644 index 7df3b597e8..0000000000 --- a/templates/markdown/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "scripts"] -} diff --git a/templates/markdown/typescript/method.md.twig b/templates/markdown/typescript/method.md.twig deleted file mode 100644 index 836dd49736..0000000000 --- a/templates/markdown/typescript/method.md.twig +++ /dev/null @@ -1,82 +0,0 @@ -# {{ method.name | caseCamel }} -{% if method.deprecated %} - -⚠️ **DEPRECATED**{% if method.since %} since {{ method.since }}{% endif %}{% if method.replaceWith %} - Use `{{ method.replaceWith }}` instead{% endif %} - -{% endif %} - -Description: {{ method.description | decodeHtmlEntities | trim }} - -## Parameters -{% if method.parameters.all | length > 0 %} - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -{% for parameter in method.parameters.all -%} -| `{{ parameter.name | caseCamel }}` | `{{ parameter | getPropertyType }}` | {% if parameter.required %}✅{% else %}❌{% endif %} | {{ parameter.description | decodeHtmlEntities }}{% if parameter.default %} (Default: `{{ parameter.default }}`){% endif %}{% if parameter.enumValues | length > 0 %}
**Allowed:** {% for value in parameter.enumValues %}`{{ value }}`{% if not loop.last %}, {% endif %}{% endfor %}{% endif %} | -{% endfor %} -{%- else %} - -This method does not accept any parameters. -{% endif %} - -## Usage - -```typescript -import { Client, {{ service.name | caseUcfirst }}{% for parameter in method.parameters.all %}{% if parameter.enumValues | length > 0 %}, {{ parameter.enumName | caseUcfirst }}{% endif %}{% endfor %}{% if method | getResponseModel %}, Models{% endif %}{% if method.parameters.all | needsPermissionImport %}, Permission, Role{% endif %} } from '{{ spec.title | caseLower }}'; - -const client = new Client() - .setEndpoint('{{ spec.endpointDocs | raw }}') -{%- if method.auth|length > 0 %} -{% for node in method.auth -%} -{% for key,header in node|keys -%} - .set{{header}}('{{ node[header]['x-appwrite']['demo'] | raw }}') -{%- endfor %} -{%- endfor -%} -{% endif -%} -; - -const {{ service.name | caseCamel }} = new {{ service.name | caseUcfirst }}(client); -{% if method.type == 'location' %} -const result{% if method | getResponseModel %}: {{ method | getResponseModel }}{% endif %} = {{ service.name | caseCamel }}.{{ method.name | caseCamel }}({% if method.parameters.all | length == 0 %}); -{% else -%} -{ -{% for parameter in method.parameters.all %} - {{ parameter.name | caseCamel }}: {% if parameter.enumValues | length > 0 %}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}{% else %}{{ parameter | paramExample }}{% endif %}, -{% endfor -%} -}); -{% endif -%} -{% elseif method.type != 'webAuth' %} -const result{% if method | getResponseModel %}: {{ method | getResponseModel }}{% endif %} = await {{ service.name | caseCamel }}.{{ method.name | caseCamel }}({% if method.parameters.all | length == 0 %}); -{% else -%} -{ -{% for parameter in method.parameters.all %} - {{ parameter.name | caseCamel }}: {% if parameter.enumValues | length > 0 %}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}{% else %}{{ parameter | paramExample }}{% endif %}, -{% endfor -%} -}); -{% endif -%} -{% else -%} -{{ service.name | caseCamel }}.{{ method.name | caseCamel }}({% if method.parameters.all | length == 0 %}); -{% else -%} -{ -{% for parameter in method.parameters.all %} - {{ parameter.name | caseCamel }}: {% if parameter.enumValues | length > 0 %}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}{% else %}{{ parameter | paramExample }}{% endif %}, -{% endfor -%} -}); -{% endif -%} -{% endif -%} -``` -{% if method | getResponseModel %} - -## Response Model - -Returns a `{{ method | getResponseModel }}` object with the following properties: -{% if method.responseModel and spec.definitions[method.responseModel] %} - -| Property | Type | Description | -|----------|------|-------------| -{% for property in spec.definitions[method.responseModel].properties -%} -| `{{ property.name | caseCamel }}` | `{{ property | getPropertyType }}` | {{ property.description | decodeHtmlEntities }} | -{% endfor %} -{%- endif %} -{% endif %} diff --git a/templates/php/src/Client.php.twig b/templates/php/src/Client.php.twig index 29a52a62a1..67d6b64eaf 100644 --- a/templates/php/src/Client.php.twig +++ b/templates/php/src/Client.php.twig @@ -329,10 +329,8 @@ class Client } } - switch(substr($contentType, 0, strpos($contentType, ';'))) { - case 'application/json': - $responseBody = json_decode($responseBody, true); - break; + if (str_starts_with($contentType, 'application/json')) { + $responseBody = json_decode($responseBody, true); } if (curl_errno($ch)) { diff --git a/tests/Android16Java17Test.php b/tests/Android16Java17Test.php index e5ee661aee..0df08dc222 100644 --- a/tests/Android16Java17Test.php +++ b/tests/Android16Java17Test.php @@ -1,24 +1,38 @@ &2 && cat library/result.txt"'; + #[Override] protected array $expectedOutput = [ ...Base::PING_RESPONSE, ...Base::FOO_RESPONSES, diff --git a/tests/Android5Java17Test.php b/tests/Android5Java17Test.php index e40c6c1732..6310cd9881 100644 --- a/tests/Android5Java17Test.php +++ b/tests/Android5Java17Test.php @@ -1,24 +1,38 @@ &2 && cat library/result.txt"'; + #[Override] protected array $expectedOutput = [ ...Base::PING_RESPONSE, ...Base::FOO_RESPONSES, diff --git a/tests/AppleSwift60Test.php b/tests/AppleSwift60Test.php index 16841c3d14..9211632561 100644 --- a/tests/AppleSwift60Test.php +++ b/tests/AppleSwift60Test.php @@ -1,23 +1,37 @@ getLanguage(), new Swagger2($spec)); @@ -433,7 +440,7 @@ public function testHTTPSuccess(): void do { $removed = \array_shift($output); - } while ($removed != 'Test Started' && sizeof($output) != 0); + } while ($removed != 'Test Started' && count($output) !== 0); echo \implode("\n", $output); @@ -443,23 +450,23 @@ public function testHTTPSuccess(): void } // HACK: Swift does not guarantee the order of the JSON parameters - if (\str_starts_with($expected, '{')) { + if (\str_starts_with((string) $expected, '{')) { $this->assertEquals( - \json_decode($expected, true), + \json_decode((string) $expected, true), \json_decode($output[$index], true) ); } elseif ($expected == 'unique()') { $this->assertNotEmpty($output[$index]); $this->assertIsString($output[$index]); - $this->assertEquals(20, strlen($output[$index])); - $this->assertNotEquals($output[$index], 'unique()'); + $this->assertSame(20, strlen($output[$index])); + $this->assertNotSame('unique()', $output[$index]); } else { $this->assertEquals($expected, $output[$index]); } } } - private function rmdirRecursive($dir): void + private function rmdirRecursive(string $dir): void { if (!\is_dir($dir)) { return; @@ -479,12 +486,12 @@ private function rmdirRecursive($dir): void private function assertExcludedFixtureWasRemoved(string $dir): void { - $iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS) + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS) ); foreach ($iterator as $file) { - $path = \strtolower($file->getPathname()); + $path = \strtolower((string) $file->getPathname()); foreach (self::EXCLUDED_FIXTURE_TOKENS as $token) { $this->assertStringNotContainsString($token, $path, "Excluded fixture leaked into generated path: {$path}"); diff --git a/tests/CLIBun10Test.php b/tests/CLIBun10Test.php index 0449fa430b..2005117d50 100644 --- a/tests/CLIBun10Test.php +++ b/tests/CLIBun10Test.php @@ -1,19 +1,29 @@ setNPMPackage('appwrite-cli'); diff --git a/tests/CLIBun11Test.php b/tests/CLIBun11Test.php index d934388ea1..c35a55709b 100644 --- a/tests/CLIBun11Test.php +++ b/tests/CLIBun11Test.php @@ -1,19 +1,29 @@ setNPMPackage('appwrite-cli'); diff --git a/tests/CLIBun13Test.php b/tests/CLIBun13Test.php index fc4495ea10..af35c223e2 100644 --- a/tests/CLIBun13Test.php +++ b/tests/CLIBun13Test.php @@ -1,19 +1,29 @@ setNPMPackage('appwrite-cli'); diff --git a/tests/DartBetaTest.php b/tests/DartBetaTest.php index 2c160b68cf..cfcf2c9e6a 100644 --- a/tests/DartBetaTest.php +++ b/tests/DartBetaTest.php @@ -1,23 +1,37 @@ tests/sdks/python/__init__.py', 'docker run --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor python:3.10-alpine pip install -r tests/sdks/python/requirements.txt --upgrade', ]; + #[Override] protected string $command = 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor --env PYTHONPATH=tests/sdks/python/vendor python:3.10-alpine python tests/sdks/python/test.py'; + #[Override] protected array $expectedOutput = [ ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, diff --git a/tests/Python311Test.php b/tests/Python311Test.php index 71fe5e642b..60d84b4659 100644 --- a/tests/Python311Test.php +++ b/tests/Python311Test.php @@ -1,24 +1,38 @@ tests/sdks/python/__init__.py', 'docker run --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor python:3.11-alpine pip install -r tests/sdks/python/requirements.txt --upgrade', ]; + #[Override] protected string $command = 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor --env PYTHONPATH=tests/sdks/python/vendor python:3.11-alpine python tests/sdks/python/test.py'; + #[Override] protected array $expectedOutput = [ ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, diff --git a/tests/Python312Test.php b/tests/Python312Test.php index 186b9ed432..db0da3894c 100644 --- a/tests/Python312Test.php +++ b/tests/Python312Test.php @@ -1,24 +1,38 @@ tests/sdks/python/__init__.py', 'docker run --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor python:3.12-alpine pip install -r tests/sdks/python/requirements.txt --upgrade', ]; + #[Override] protected string $command = 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor --env PYTHONPATH=tests/sdks/python/vendor python:3.12-alpine python tests/sdks/python/test.py'; + #[Override] protected array $expectedOutput = [ ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, diff --git a/tests/Python313Test.php b/tests/Python313Test.php index a3c0e9316e..fa6ca14c8f 100644 --- a/tests/Python313Test.php +++ b/tests/Python313Test.php @@ -1,24 +1,38 @@ tests/sdks/python/__init__.py', 'docker run --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor python:3.13-alpine pip install -r tests/sdks/python/requirements.txt --upgrade', ]; + #[Override] protected string $command = 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor --env PYTHONPATH=tests/sdks/python/vendor python:3.13-alpine python tests/sdks/python/test.py'; + #[Override] protected array $expectedOutput = [ ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, diff --git a/tests/Python39Test.php b/tests/Python39Test.php index 879e545e21..a9de2b75ab 100644 --- a/tests/Python39Test.php +++ b/tests/Python39Test.php @@ -1,24 +1,38 @@ tests/sdks/python/__init__.py', 'docker run --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor python:3.9-alpine pip install -r tests/sdks/python/requirements.txt --upgrade', ]; + #[Override] protected string $command = 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app --env PIP_TARGET=tests/sdks/python/vendor --env PYTHONPATH=tests/sdks/python/vendor python:3.9-alpine python tests/sdks/python/test.py'; + #[Override] protected array $expectedOutput = [ ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, diff --git a/tests/ReactNativeTest.php b/tests/ReactNativeTest.php index bb7987622b..1703d7801e 100644 --- a/tests/ReactNativeTest.php +++ b/tests/ReactNativeTest.php @@ -1,17 +1,29 @@ data); + return self::serializeAdditionalProperties($this->data); } } readonly class AdditionalPropsDataFieldMapped { - use \Appwrite\Models\ArraySerializable; + use ArraySerializable; public function __construct( public array $payload, @@ -70,28 +72,28 @@ public function __construct( public static function from(array $data): static { if (!array_key_exists('data', $data)) { - throw new \InvalidArgumentException('Missing required field "data" for ' . static::class . '.'); + throw new InvalidArgumentException('Missing required field "data" for ' . static::class . '.'); } if (!array_key_exists('status', $data)) { - throw new \InvalidArgumentException('Missing required field "status" for ' . static::class . '.'); + throw new InvalidArgumentException('Missing required field "status" for ' . static::class . '.'); } return new static( payload: $data['data'], status: $data['status'], - data: static::extractAdditionalPropertiesFromFields($data, ['data', 'status']), + data: self::extractAdditionalPropertiesFromFields($data, ['data', 'status']), ); } public function toArray(): array { $result = [ - 'data' => static::serializeValue($this->payload), - 'status' => static::serializeValue($this->status), + 'data' => self::serializeValue($this->payload), + 'status' => self::serializeValue($this->status), ]; - foreach (static::serializeAdditionalProperties($this->data) as $field => $value) { + foreach (self::serializeAdditionalProperties($this->data) as $field => $value) { $result[$field] = $value; } @@ -101,7 +103,7 @@ public function toArray(): array readonly class AdditionalPropsMapped { - use \Appwrite\Models\ArraySerializable; + use ArraySerializable; public function __construct( public string $id, @@ -112,22 +114,22 @@ public function __construct( public static function from(array $data): static { if (!array_key_exists('$id', $data)) { - throw new \InvalidArgumentException('Missing required field "$id" for ' . static::class . '.'); + throw new InvalidArgumentException('Missing required field "$id" for ' . static::class . '.'); } return new static( id: $data['$id'], - data: static::extractAdditionalPropertiesFromFields($data, ['$id']), + data: self::extractAdditionalPropertiesFromFields($data, ['$id']), ); } public function toArray(): array { $result = [ - '$id' => static::serializeValue($this->id), + '$id' => self::serializeValue($this->id), ]; - foreach (static::serializeAdditionalProperties($this->data) as $field => $value) { + foreach (self::serializeAdditionalProperties($this->data) as $field => $value) { $result[$field] = $value; }