From c46dce2942231278b95aa3c098a1170c77d0c5c7 Mon Sep 17 00:00:00 2001 From: Nik <6206742+nik-418@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:44:07 +0100 Subject: [PATCH] Initial 0.1.0 release (#2) --- .github/FUNDING.yml | 1 - .github/ISSUE_TEMPLATE/config.yml | 6 +- .github/dependabot.yml | 12 - .github/workflows/dependabot-auto-merge.yml | 32 -- .../fix-php-code-style-issues-cs-fixer.yml | 26 -- ...pint.yml => fix-php-code-style-issues.yml} | 0 .github/workflows/run-tests-pest.yml | 37 --- .../{run-tests-phpunit.yml => run-tests.yml} | 0 .gitignore | 5 +- .php-cs-fixer.dist.php | 39 --- CHANGELOG.md | 3 +- LICENSE.md | 21 -- README.md | 91 +++--- composer.json | 40 +-- configure.php | 288 ------------------ phpunit.xml.dist | 6 +- src/API.php | 83 +++++ src/Client.php | 157 ++++++++++ src/ClientOptions.php | 15 + src/ClientResponse.php | 16 + src/Contracts/APIContract.php | 15 + src/Contracts/ClientContract.php | 15 + src/Exceptions/ClientException.php | 12 + src/Responses/APIProjectResponse.php | 25 ++ src/Responses/APIProjectsResponse.php | 25 ++ src/SkeletonClass.php | 7 - tests/APITest.php | 16 + tests/ArchTest.php | 5 - tests/ClientTest.php | 16 + tests/ExampleTestPest.php | 5 - tests/ExampleTestPhpunit.php | 9 - tests/Pest.php | 1 - 32 files changed, 475 insertions(+), 554 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/dependabot-auto-merge.yml delete mode 100644 .github/workflows/fix-php-code-style-issues-cs-fixer.yml rename .github/workflows/{fix-php-code-style-issues-pint.yml => fix-php-code-style-issues.yml} (100%) delete mode 100644 .github/workflows/run-tests-pest.yml rename .github/workflows/{run-tests-phpunit.yml => run-tests.yml} (100%) delete mode 100644 .php-cs-fixer.dist.php delete mode 100644 LICENSE.md delete mode 100644 configure.php create mode 100644 src/API.php create mode 100755 src/Client.php create mode 100644 src/ClientOptions.php create mode 100644 src/ClientResponse.php create mode 100644 src/Contracts/APIContract.php create mode 100644 src/Contracts/ClientContract.php create mode 100644 src/Exceptions/ClientException.php create mode 100644 src/Responses/APIProjectResponse.php create mode 100644 src/Responses/APIProjectsResponse.php delete mode 100755 src/SkeletonClass.php create mode 100644 tests/APITest.php delete mode 100644 tests/ArchTest.php create mode 100644 tests/ClientTest.php delete mode 100644 tests/ExampleTestPest.php delete mode 100644 tests/ExampleTestPhpunit.php delete mode 100644 tests/Pest.php diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index c68765b..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: :vendor_name diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 96701be..4febc45 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,11 @@ blank_issues_enabled: false contact_links: - name: Ask a question - url: https://github.com/:vendor_name/:package_name/discussions/new?category=q-a + url: https://github.com/banana-dev/banana-dev/discussions/new?category=q-a about: Ask the community for help - name: Request a feature - url: https://github.com/:vendor_name/:package_name/discussions/new?category=ideas + url: https://github.com/banana-dev/banana-dev/discussions/new?category=ideas about: Share ideas for new features - name: Report a security issue - url: https://github.com/:vendor_name/:package_name/security/policy + url: https://github.com/banana-dev/banana-dev/security/policy about: Learn how to notify us for sensitive bugs diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 30c8a49..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - labels: - - "dependencies" \ No newline at end of file diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml deleted file mode 100644 index ca2197d..0000000 --- a/.github/workflows/dependabot-auto-merge.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: dependabot-auto-merge -on: pull_request_target - -permissions: - pull-requests: write - contents: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v1.6.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Auto-merge Dependabot PRs for semver-minor updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - - - name: Auto-merge Dependabot PRs for semver-patch updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/fix-php-code-style-issues-cs-fixer.yml b/.github/workflows/fix-php-code-style-issues-cs-fixer.yml deleted file mode 100644 index 3e044ea..0000000 --- a/.github/workflows/fix-php-code-style-issues-cs-fixer.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Check & fix styling - -on: [push] - -permissions: - contents: write - -jobs: - php-cs-fixer: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - - name: Run PHP CS Fixer - uses: docker://oskarstark/php-cs-fixer-ga - with: - args: --config=.php-cs-fixer.dist.php --allow-risky=yes - - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Fix styling diff --git a/.github/workflows/fix-php-code-style-issues-pint.yml b/.github/workflows/fix-php-code-style-issues.yml similarity index 100% rename from .github/workflows/fix-php-code-style-issues-pint.yml rename to .github/workflows/fix-php-code-style-issues.yml diff --git a/.github/workflows/run-tests-pest.yml b/.github/workflows/run-tests-pest.yml deleted file mode 100644 index bac4eb8..0000000 --- a/.github/workflows/run-tests-pest.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Tests - -on: [push, pull_request] - -jobs: - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: true - matrix: - os: [ubuntu-latest, windows-latest] - php: [8.2, 8.1] - stability: [prefer-lowest, prefer-stable] - - name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo - coverage: none - - - name: Setup problem matchers - run: | - echo "::add-matcher::${{ runner.tool_cache }}/php.json" - echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - - name: Execute tests - run: vendor/bin/pest diff --git a/.github/workflows/run-tests-phpunit.yml b/.github/workflows/run-tests.yml similarity index 100% rename from .github/workflows/run-tests-phpunit.yml rename to .github/workflows/run-tests.yml diff --git a/.gitignore b/.gitignore index 841e6e5..076020f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ .idea .php_cs .php_cs.cache +.php-cs-fixer.cache .phpunit.result.cache +.phpunit.cache build composer.lock coverage @@ -9,5 +11,4 @@ docs phpunit.xml psalm.xml vendor -.php-cs-fixer.cache - +example/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php deleted file mode 100644 index 7ee4ffa..0000000 --- a/.php-cs-fixer.dist.php +++ /dev/null @@ -1,39 +0,0 @@ -in([ - __DIR__ . '/src', - __DIR__ . '/tests', - ]) - ->name('*.php') - ->ignoreDotFiles(true) - ->ignoreVCS(true); - -return (new PhpCsFixer\Config()) - ->setRules([ - '@PSR12' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => ['sort_algorithm' => 'alpha'], - 'no_unused_imports' => true, - 'not_operator_with_successor_space' => true, - 'trailing_comma_in_multiline' => true, - 'phpdoc_scalar' => true, - 'unary_operator_spaces' => true, - 'binary_operator_spaces' => true, - 'blank_line_before_statement' => [ - 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], - ], - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_var_without_name' => true, - 'class_attributes_separation' => [ - 'elements' => [ - 'method' => 'one', - ], - ], - 'method_argument_space' => [ - 'on_multiline' => 'ensure_fully_multiline', - 'keep_multiple_spaces_after_comma' => true, - ], - 'single_trait_insert_per_statement' => true, - ]) - ->setFinder($finder); diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d01146..1676f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ # Changelog -All notable changes to `:package_name` will be documented in this file. - +All notable changes to `banana-dev` will be documented in this file. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 58c9ad4..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) :vendor_name - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/README.md b/README.md index da3e831..8de67c7 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,69 @@ -# :package_description +# title -[![Latest Version on Packagist](https://img.shields.io/packagist/v/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug) -[![Tests](https://img.shields.io/github/actions/workflow/status/:vendor_slug/:package_slug/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/:vendor_slug/:package_slug/actions/workflows/run-tests.yml) -[![Total Downloads](https://img.shields.io/packagist/dt/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug) - ---- -This package can be used as to scaffold a framework agnostic package. Follow these steps to get started: +## Using it -1. Press the "Use template" button at the top of this repo to create a new repo with the contents of this skeleton -2. Run "php ./configure.php" to run a script that will replace all placeholders throughout all the files -3. Have fun creating your package. -4. If you need help creating a package, consider picking up our Laravel Package Training video course. ---- - -This is where your description should go. Try and limit it to a paragraph or two. Consider adding a small example. +## Testing -## Support us +```sh +mkdir example +cd example +composer init --name=banana-dev/example --description="" --author="" --autoload=src/ --repository='{"type":"path","url":"../"}' --license="" --require="banana-dev/banana-dev @dev" --require="guzzlehttp/guzzle:^7.8" --stability=dev --no-interaction +``` -[](https://spatie.be/github-ad-click/:package_name) +Say yes to the discovery prompt. +```sh +composer install +``` -We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). +Create a `test.php` within `example/src` with your own API key. +```php +listProjects(); +var_dump($projects); -## Usage +$project = $api->getProject($projects->json['results'][0]['id']); +var_dump($project); -```php -$skeleton = new VendorName\Skeleton(); -echo $skeleton->echoPhrase('Hello, VendorName!'); +$updated = $api->updateProject($project->json['id'], [ + 'maxReplicas' => 3, +]); +var_dump($updated); ``` -## Testing +## Development -```bash -composer test -``` +Install Homebrew -## Changelog - -Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. - -## Contributing +```sh +curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh +``` -Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. +Install PHP and Composer -## Security Vulnerabilities +```sh +brew install php composer +``` -Please review [our security policy](../../security/policy) on how to report security vulnerabilities. +Install dependencies -## Credits +```sh +composer install +``` -- [:author_name](https://github.com/:author_username) -- [All Contributors](../../contributors) +Run tests -## License +```sh +composer test +``` -The MIT License (MIT). Please see [License File](LICENSE.md) for more information. +Format code +```sh +composer format +``` diff --git a/composer.json b/composer.json index 98297a1..1ada02d 100644 --- a/composer.json +++ b/composer.json @@ -1,46 +1,52 @@ { - "name": ":vendor_slug/:package_slug", - "description": ":package_description", + "name": "banana-dev/banana-dev", + "description": "A PHP client to interact with Banana's machine learning inference APIs", "keywords": [ - ":vendor_name", - ":package_slug" + "Banana", "Banana.dev", "Banana client", "API wrapper", "SDK" ], - "homepage": "https://github.com/:vendor_slug/:package_slug", + "homepage": "https://github.com/bananaml/banana-php-sdk", "license": "MIT", "authors": [ { - "name": ":author_name", - "email": "author@domain.com", + "name": "Banana.dev", + "email": "support@banana.dev", "role": "Developer" } ], "require": { - "php": "^8.1" + "php": "^8.1", + "php-http/discovery": "^1.17", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "ramsey/uuid": "^4.7" }, "require-dev": { - :require_dev_testing, - :require_dev_codestyle, - "spatie/ray": "^1.28" + "laravel/pint": "^1.0", + "nyholm/psr7": "^1.8", + "phpunit/phpunit": "^10.3.2", + "spatie/ray": "^1.28", + "symfony/http-client": "^6.3" }, "autoload": { "psr-4": { - "VendorName\\Skeleton\\": "src" + "BananaDev\\": "src" } }, "autoload-dev": { "psr-4": { - "VendorName\\Skeleton\\Tests\\": "tests" + "BananaDev\\Tests\\": "tests" } }, "scripts": { - :scripts_testing, - :scripts_codestyle + "test": "vendor/bin/phpunit", + "test-coverage": "vendor/bin/phpunit --coverage", + "format": "vendor/bin/pint" }, "config": { "sort-packages": true, "allow-plugins": { - :plugins_testing, - "phpstan/extension-installer": true + "phpstan/extension-installer": true, + "php-http/discovery": false } }, "minimum-stability": "dev", diff --git a/configure.php b/configure.php deleted file mode 100644 index dcff87e..0000000 --- a/configure.php +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env php - $option === $default ? strtoupper($option) : $option, - $options, - )); - - $answer = ask("{$question} ({$suggestions})"); - - $validOptions = implode(', ', $options); - - while (! in_array($answer, $options)) { - if ($default && $answer === '') { - $answer = $default; - - break; - } - - writeln(PHP_EOL."Please pick one of the following options: {$validOptions}"); - - $answer = ask("{$question} ({$suggestions})"); - } - - if (! $answer) { - $answer = $default; - } - - return $answer; -} - -function confirm(string $question, bool $default = false): bool -{ - $answer = ask($question.' ('.($default ? 'Y/n' : 'y/N').')'); - - if (! $answer) { - return $default; - } - - return strtolower($answer) === 'y'; -} - -function writeln(string $line): void -{ - echo $line.PHP_EOL; -} - -function run(string $command): string -{ - return trim(shell_exec($command)); -} - -function str_after(string $subject, string $search): string -{ - $pos = strrpos($subject, $search); - - if ($pos === false) { - return $subject; - } - - return substr($subject, $pos + strlen($search)); -} - -function slugify(string $subject): string -{ - return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $subject), '-')); -} - -function title_case(string $subject): string -{ - return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $subject))); -} - -function replace_in_file(string $file, array $replacements): void -{ - $contents = file_get_contents($file); - - file_put_contents( - $file, - str_replace( - array_keys($replacements), - array_values($replacements), - $contents - ) - ); -} - -function removeReadmeParagraphs(string $file): void -{ - $contents = file_get_contents($file); - - file_put_contents( - $file, - preg_replace('/.*/s', '', $contents) ?: $contents - ); -} - -function determineSeparator(string $path): string -{ - return str_replace('/', DIRECTORY_SEPARATOR, $path); -} - -function replaceForWindows(): array -{ - return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i vendor | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton vendor_name vendor_slug author@domain.com"')); -} - -function replaceForAllOtherOSes(): array -{ - return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v '.basename(__FILE__))); -} - -function setupTestingLibrary(string $testingLibrary): void -{ - if ($testingLibrary === 'pest') { - unlink(__DIR__.'/tests/ExampleTestPhpunit.php'); - unlink(__DIR__.'/.github/workflows/run-tests-phpunit.yml'); - - rename( - from: __DIR__.'/tests/ExampleTestPest.php', - to: __DIR__.'/tests/ExampleTest.php' - ); - - rename( - from: __DIR__.'/.github/workflows/run-tests-pest.yml', - to: __DIR__.'/.github/workflows/run-tests.yml' - ); - - replace_in_file(__DIR__.'/composer.json', [ - ':require_dev_testing' => '"pestphp/pest": "^2.15"', - ':scripts_testing' => '"test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage"', - ':plugins_testing' => '"pestphp/pest-plugin": true', - ]); - } elseif ($testingLibrary === 'phpunit') { - unlink(__DIR__.'/tests/ExampleTestPest.php'); - unlink(__DIR__.'/tests/ArchTest.php'); - unlink(__DIR__.'/tests/Pest.php'); - unlink(__DIR__.'/.github/workflows/run-tests-pest.yml'); - - rename( - from: __DIR__.'/tests/ExampleTestPhpunit.php', - to: __DIR__.'/tests/ExampleTest.php' - ); - - rename( - from: __DIR__.'/.github/workflows/run-tests-phpunit.yml', - to: __DIR__.'/.github/workflows/run-tests.yml' - ); - - replace_in_file(__DIR__.'/composer.json', [ - ':require_dev_testing' => '"phpunit/phpunit": "^10.3.2"', - ':scripts_testing' => '"test": "vendor/bin/phpunit", - "test-coverage": "vendor/bin/phpunit --coverage"', - ':plugins_testing,' => '', // We need to remove the comma here as well, since there's nothing to add - ]); - } -} - -function setupCodeStyleLibrary(string $codeStyleLibrary): void -{ - if ($codeStyleLibrary === 'pint') { - unlink(__DIR__.'/.github/workflows/fix-php-code-style-issues-cs-fixer.yml'); - - rename( - from: __DIR__.'/.github/workflows/fix-php-code-style-issues-pint.yml', - to: __DIR__.'/.github/workflows/fix-php-code-style-issues.yml' - ); - - replace_in_file(__DIR__.'/composer.json', [ - ':require_dev_codestyle' => '"laravel/pint": "^1.0"', - ':scripts_codestyle' => '"format": "vendor/bin/pint"', - ':plugins_testing' => '', - ]); - - unlink(__DIR__.'/.php-cs-fixer.dist.php'); - } elseif ($codeStyleLibrary === 'cs fixer') { - unlink(__DIR__.'/.github/workflows/fix-php-code-style-issues-pint.yml'); - - rename( - from: __DIR__.'/.github/workflows/fix-php-code-style-issues-cs-fixer.yml', - to: __DIR__.'/.github/workflows/fix-php-code-style-issues.yml' - ); - - replace_in_file(__DIR__.'/composer.json', [ - ':require_dev_codestyle' => '"friendsofphp/php-cs-fixer": "^3.21.1"', - ':scripts_codestyle' => '"format": "vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --allow-risky=yes"', - ':plugins_testing' => '', - ]); - } -} - -$gitName = run('git config user.name'); -$authorName = ask('Author name', $gitName); - -$gitEmail = run('git config user.email'); -$authorEmail = ask('Author email', $gitEmail); - -$usernameGuess = explode(':', run('git config remote.origin.url'))[1]; -$usernameGuess = dirname($usernameGuess); -$usernameGuess = basename($usernameGuess); -$authorUsername = ask('Author username', $usernameGuess); - -$vendorName = ask('Vendor name', $authorUsername); -$vendorSlug = slugify($vendorName); -$vendorNamespace = ucwords($vendorName); -$vendorNamespace = ask('Vendor namespace', $vendorNamespace); - -$currentDirectory = getcwd(); -$folderName = basename($currentDirectory); - -$packageName = ask('Package name', $folderName); -$packageSlug = slugify($packageName); - -$className = title_case($packageName); -$className = ask('Class name', $className); -$description = ask('Package description', "This is my package {$packageSlug}"); - -$testingLibrary = askWithOptions( - 'Which testing library do you want to use?', - ['pest', 'phpunit'], - 'pest', -); - -$codeStyleLibrary = askWithOptions( - 'Which code style library do you want to use?', - ['pint', 'cs fixer'], - 'pint', -); - -writeln('------'); -writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})"); -writeln("Vendor : {$vendorName} ({$vendorSlug})"); -writeln("Package : {$packageSlug} <{$description}>"); -writeln("Namespace : {$vendorNamespace}\\{$className}"); -writeln("Class name : {$className}"); -writeln("Testing library : {$testingLibrary}"); -writeln("Code style library : {$codeStyleLibrary}"); -writeln('------'); - -writeln('This script will replace the above values in all relevant files in the project directory.'); - -if (! confirm('Modify files?', true)) { - exit(1); -} - -$files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); - -foreach ($files as $file) { - replace_in_file($file, [ - ':author_name' => $authorName, - ':author_username' => $authorUsername, - 'author@domain.com' => $authorEmail, - ':vendor_name' => $vendorName, - ':vendor_slug' => $vendorSlug, - 'VendorName' => $vendorNamespace, - ':package_name' => $packageName, - ':package_slug' => $packageSlug, - 'Skeleton' => $className, - ':package_description' => $description, - ]); - - match (true) { - str_contains($file, determineSeparator('src/SkeletonClass.php')) => rename($file, determineSeparator('./src/'.$className.'Class.php')), - str_contains($file, 'README.md') => removeReadmeParagraphs($file), - default => [], - }; -} - -setupTestingLibrary($testingLibrary); -setupCodeStyleLibrary($codeStyleLibrary); - -confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); - -confirm('Let this script delete itself?', true) && unlink(__FILE__); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d15fdb4..45b5bc6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,11 +1,11 @@ - + tests - + ./src diff --git a/src/API.php b/src/API.php new file mode 100644 index 0000000..b84461e --- /dev/null +++ b/src/API.php @@ -0,0 +1,83 @@ +call('GET', 'projects', $query); + + return new APIProjectsResponse( + json: json_decode((string) $response->getBody(), true), + statusCode: $response->getStatusCode(), + ); + } + + public function getProject(string $projectId, array $query = []): APIProjectResponse + { + $response = $this->call('GET', 'projects/'.$projectId, $query); + + return new APIProjectResponse( + json: json_decode((string) $response->getBody(), true), + statusCode: $response->getStatusCode(), + ); + } + + public function updateProject(string $projectId, array $data): APIProjectResponse + { + $response = $this->call('PUT', 'projects/'.$projectId, $data); + + return new APIProjectResponse( + json: json_decode((string) $response->getBody(), true), + statusCode: $response->getStatusCode(), + ); + } + + private function call(string $method, string $route, array $data = [], array $headers = []): ResponseInterface + { + $client = new Psr18Client(); + + $url = self::API_BASE_URL.'/'.trim($route, '/'); + + if ($method == 'GET') { + $url .= '?'.http_build_query($data); + } + + $request = $client->createRequest($method, $url); + + $headers = array_merge([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-BANANA-API-KEY' => $this->apiKey, + 'X-BANANA-REQUEST-ID' => Uuid::uuid4()->toString(), + ], $headers); + + foreach ($headers as $headerName => $headerValue) { + $request = $request->withHeader($headerName, $headerValue); + } + + if ($method == 'POST' || $method == 'PUT') { + $request = $request->withBody($client->createStream(empty($data) ? '{}' : json_encode($data))); + } + + return $client->sendRequest($request); + } +} diff --git a/src/Client.php b/src/Client.php new file mode 100755 index 0000000..bddb8d4 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,157 @@ +call('/_k/warmup', [], [], new ClientOptions(retry: false)); + } + + public function call(string $route, array $json = [], array $headers = [], ClientOptions $options = null): ClientResponse + { + if (! $options) { + $options = new ClientOptions(); + } + + $endpoint = $this->url.'/'.ltrim($route, '/'); + + $headers = array_merge([ + 'Content-Type' => 'application/json', + 'X-BANANA-API-KEY' => $this->apiKey, + 'X-BANANA-REQUEST-ID' => Uuid::uuid4()->toString(), + ], $headers); + + $backoffIntervalMs = 100; + $start = time(); + $firstCall = true; + + while (true) { + if (time() - $start > $options->retryTimeoutMs) { + throw new ClientException('Retry timeout exceeded'); + } + + if (! $firstCall) { + if ($this->verbosity === 'DEBUG') { + error_log('Retrying...'); + } + } + + $backoffIntervalMs = min($backoffIntervalMs * 2, self::MAX_BACKOFF_INTERVAL); // at most wait MAX_BACKOFF_INTERVAL ms + + $response = $this->makeRequest($endpoint, $json, $headers); + + $status = $response->getStatusCode(); + $body = (string) $response->getBody(); + + if ($this->verbosity === 'DEBUG' && $status !== 200) { + error_log('Status code: '.$status.PHP_EOL); + error_log($body.PHP_EOL); + } + + // success case -> return json and metadata + if ($status === 200) { + try { + $json = json_decode($body, true); + + return new ClientResponse($status, $json, $response->getHeaders()); + } catch (\Exception $e) { + throw new ClientException($body); + } + } + + // user at their quota -> retry + elseif ($status === 400) { // user at their quota, retry + if (! $options->retry) { + throw new ClientException($body); + } + usleep($backoffIntervalMs * 1000); + + continue; + } + + // bad auth || endpoint doesn't exist || payload too large -> throw + elseif ($status === 401 || $status === 404 || $status === 413) { + throw new ClientException($body); + } + + // banana is a teapot -> throw + elseif ($status === 418) { + throw new ClientException('banana is a teapot'); + } + + // potassium threw locked error -> retry + elseif ($status === 423) { + if (! $options->retry) { + $message = $body.PHP_EOL; + $message .= '423 errors are returned by Potassium when your server(s) are all busy handling GPU endpoints.'.PHP_EOL; + $message .= 'In most cases, you just want to retry later. Running $client->call() with the retry=true argument handles this for you.'; + throw new ClientException($message); + } + usleep($backoffIntervalMs * 1000); + + continue; + } + + // user's server had an unrecoverable error -> throw + elseif ($status === 500) { + throw new ClientException($body); + } + + // banana had a temporary error -> retry + elseif ($status === 503) { // banana had a temporary error, retry + if (! $options->retry) { + throw new ClientException($body); + } + usleep($backoffIntervalMs * 1000); + + continue; + } + + // gateway timeout -> throw + elseif ($status === 504) { + $message = 'Reached request timeout limit. To avoid this we recommend using an app.background() handler in your Potassium app.'; + throw new ClientException($message); + } + + // unexpected status code -> throw + else { + throw new ClientException('Unexpected HTTP response code: '.$status); + } + } + } + + private function makeRequest(string $url, array $data, array $headers): ResponseInterface + { + $client = new Psr18Client(); + + $request = $client->createRequest('POST', $url); + + foreach ($headers as $headerName => $headerValue) { + $request = $request->withHeader($headerName, $headerValue); + } + + $request = $request->withBody($client->createStream(empty($data) ? '{}' : json_encode($data))); + + return $client->sendRequest($request); + } +} diff --git a/src/ClientOptions.php b/src/ClientOptions.php new file mode 100644 index 0000000..2b54abd --- /dev/null +++ b/src/ClientOptions.php @@ -0,0 +1,15 @@ +statusCode; + } + + public function getResult(): array + { + return $this->json; + } +} diff --git a/src/Responses/APIProjectsResponse.php b/src/Responses/APIProjectsResponse.php new file mode 100644 index 0000000..cb602d3 --- /dev/null +++ b/src/Responses/APIProjectsResponse.php @@ -0,0 +1,25 @@ +statusCode; + } + + public function getResults(): array + { + return $this->json['results']; + } +} diff --git a/src/SkeletonClass.php b/src/SkeletonClass.php deleted file mode 100755 index dcf362c..0000000 --- a/src/SkeletonClass.php +++ /dev/null @@ -1,7 +0,0 @@ -assertNotNull($api); + } +} diff --git a/tests/ArchTest.php b/tests/ArchTest.php deleted file mode 100644 index fd6a1e4..0000000 --- a/tests/ArchTest.php +++ /dev/null @@ -1,5 +0,0 @@ -expect(['dd', 'dump', 'ray']) - ->not->toBeUsed(); diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..acae699 --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,16 @@ +assertNotNull($client); + } +} diff --git a/tests/ExampleTestPest.php b/tests/ExampleTestPest.php deleted file mode 100644 index 5d36321..0000000 --- a/tests/ExampleTestPest.php +++ /dev/null @@ -1,5 +0,0 @@ -toBeTrue(); -}); diff --git a/tests/ExampleTestPhpunit.php b/tests/ExampleTestPhpunit.php deleted file mode 100644 index fa301e0..0000000 --- a/tests/ExampleTestPhpunit.php +++ /dev/null @@ -1,9 +0,0 @@ -