├── .editorconfig ├── .env ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ └── config.yml ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── php-cs-fixer.yml │ ├── phpstan.yml │ └── run-tests.yml ├── .gitignore ├── .php_cs.dist.php ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── phpstan.neon ├── phpunit.xml.dist ├── src ├── Api.php ├── Api │ ├── Batch.php │ ├── DataObject.php │ ├── Endpoint.php │ ├── GraphQL.php │ ├── Meta.php │ └── Schema.php ├── Collections │ ├── ClassCollection.php │ ├── ModuleCollection.php │ ├── ObjectCollection.php │ └── PropertyCollection.php ├── Exceptions │ ├── AuthenticationException.php │ └── NotFoundException.php ├── Model │ ├── ClassModel.php │ ├── MetaModel.php │ ├── Model.php │ ├── ModuleModel.php │ ├── ObjectModel.php │ ├── PropertyModel.php │ └── SchemaModel.php └── Weaviate.php └── tests ├── Api ├── BatchTest.php ├── EndpointTest.php ├── MetaTest.php ├── ObjectTest.php └── SchemaTest.php ├── ApiTest.php ├── Fixtures ├── dataClass.json ├── meta.json ├── module.json ├── object.json ├── objects.json ├── property.json └── schema.json ├── Model ├── ClassTest.php ├── MetaTest.php ├── ModuleTest.php ├── ObjectTest.php └── SchemaTest.php ├── Pest.php └── WeaviateTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 4 6 | indent_style = space 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timkley/weaviate-php/d513d14817b931864c4aeaf1cdc9becf913186f1/.env -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code held within. They make the code freely 10 | available in the hope that it will be of use to other developers. It would be extremely unfair for them to suffer abuse or anger for their hard work. 11 | 12 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the world that developers are civilized and selfless 13 | people. 14 | 15 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient quality to benefit the project. Many developers have different 16 | skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 17 | 18 | ## Viability 19 | 20 | When requesting or submitting new features, first consider whether it might be useful to others. Open source projects are used by many developers, who may have 21 | entirely different needs to your own. Think about whether or not your feature is likely to be used by other users of the project. 22 | 23 | ## Procedure 24 | 25 | Before filing an issue: 26 | 27 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 28 | - Check to make sure your feature suggestion isn't already present within the project. 29 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 30 | - Check the pull requests tab to ensure that the feature isn't already in progress. 31 | 32 | Before submitting a pull request: 33 | 34 | - Check the codebase to ensure that your feature doesn't already exist. 35 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 36 | 37 | ## Requirements 38 | 39 | If the project maintainer has any additional requirements, you will find them listed here. 40 | 41 | - **[PSR-12 Coding Standard](https://www.php-fig.org/psr/psr-12/)** - The easiest way to apply the conventions is to 42 | install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 43 | 44 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 45 | 46 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 47 | 48 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 49 | 50 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 51 | 52 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while 53 | developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/timkley/weaviate-php/discussions/new?category=q-a 5 | about: Ask the community for help 6 | - name: Request a feature 7 | url: https://github.com/timkley/weaviate-php/discussions/new?category=ideas 8 | about: Share ideas for new features 9 | - name: Report a bug 10 | url: https://github.com/timkley/weaviate-php/issues/new 11 | about: Report a reproducable bug 12 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you discover any security related issues, please email timkley@gmail.com instead of using the issue tracker. 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | ignore: 8 | - dependency-name: "*" 9 | update-types: [ "version-update:semver-major" ] 10 | groups: 11 | minor-and-patch: 12 | patterns: 13 | - '*' 14 | update-types: 15 | - minor 16 | - patch 17 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: Check & fix styling 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | permissions: 10 | contents: write 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | with: 16 | ref: ${{ github.head_ref }} 17 | 18 | - name: Run PHP CS Fixer 19 | uses: docker://oskarstark/php-cs-fixer-ga 20 | with: 21 | args: --config=.php_cs.dist.php --allow-risky=yes 22 | 23 | - name: Commit changes 24 | uses: stefanzweifel/git-auto-commit-action@v4 25 | with: 26 | commit_message: Fix styling 27 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: Run PHPStan 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | phpstan: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Setup php 16 | uses: shivammathur/setup-php@v2 17 | 18 | - name: Install composer dependencies 19 | run: composer install --no-interaction --no-ansi --no-progress --no-suggest 20 | 21 | - name: PHPStan 22 | run: composer phpstan 23 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: [ ubuntu-latest, windows-latest ] 16 | php: [ 8.2, 8.3 ] 17 | stability: [ prefer-lowest, prefer-stable ] 18 | include: 19 | - testbench: ^9.0 20 | 21 | name: ${{ matrix.stability }} - ${{ matrix.os }} 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup PHP 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: ${{ matrix.php }} 31 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 32 | coverage: none 33 | 34 | - name: Setup problem matchers 35 | run: | 36 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 37 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 38 | 39 | - name: Install dependencies 40 | run: | 41 | composer require "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 42 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 43 | 44 | - name: Execute tests 45 | run: composer test 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs 3 | .php_cs.cache 4 | .phpunit.result.cache 5 | .phpunit.cache 6 | build 7 | coverage 8 | docs 9 | phpunit.xml 10 | psalm.xml 11 | testbench.yaml 12 | vendor 13 | node_modules 14 | .php-cs-fixer.cache 15 | -------------------------------------------------------------------------------- /.php_cs.dist.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/src', 6 | __DIR__ . '/tests', 7 | ]) 8 | ->name('*.php') 9 | ->ignoreDotFiles(true) 10 | ->ignoreVCS(true); 11 | 12 | return (new PhpCsFixer\Config()) 13 | ->setRules([ 14 | '@PSR12' => true, 15 | 'array_syntax' => ['syntax' => 'short'], 16 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 17 | 'no_unused_imports' => true, 18 | 'not_operator_with_successor_space' => true, 19 | 'trailing_comma_in_multiline' => true, 20 | 'phpdoc_scalar' => true, 21 | 'unary_operator_spaces' => true, 22 | 'binary_operator_spaces' => true, 23 | 'blank_line_before_statement' => [ 24 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 25 | ], 26 | 'phpdoc_single_line_var_spacing' => true, 27 | 'phpdoc_var_without_name' => true, 28 | 'class_attributes_separation' => [ 29 | 'elements' => [ 30 | 'method' => 'one', 31 | ], 32 | ], 33 | 'method_argument_space' => [ 34 | 'on_multiline' => 'ensure_fully_multiline', 35 | 'keep_multiple_spaces_after_comma' => true, 36 | ], 37 | 'single_trait_insert_per_statement' => true, 38 | ]) 39 | ->setFinder($finder); 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Tim Kleyersburg 4 | 5 | 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 6 | the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | 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 12 | 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 13 | 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. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Weaviate PHP SDK 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/timkley/weaviate-php.svg?style=flat-square)](https://packagist.org/packages/timkley/weaviate-php) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/timkley/weaviate-php/run-tests.yml?label=tests)](https://github.com/timkley/weaviate-php/actions?query=workflow%3Arun-tests+branch%3Amain) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/timkley/weaviate-php.svg?style=flat-square)](https://packagist.org/packages/timkley/weaviate-php) 6 | 7 | This is a wrapper around the [Weaviate REST API](https://weaviate.io/developers/weaviate/api/rest). 8 | 9 | ## Installation 10 | 11 | ### Requirements 12 | 13 | > PHP 8.2 or PHP 8.3 14 | 15 | ### With composer 16 | 17 | ```bash 18 | composer require timkley/weaviate-php 19 | ``` 20 | 21 | ## Usage 22 | 23 | This package supports API key authentication (available in Weaviate 1.18 and higher) or anonymous access. Please refer to the [official documentation](https://weaviate.io/developers/weaviate/configuration/authentication#api-key) for more information. 24 | 25 | ```php 26 | graphql()->get('{ 34 | Get { 35 | Things { 36 | Article { 37 | title 38 | } 39 | } 40 | } 41 | }'); 42 | 43 | // using the `batch` REST API 44 | $weaviate->batch()->create($objects); 45 | 46 | // adding query parameters 47 | $weaviate->dataObject()->withQueryParameters(['limit' => 10])->get(); 48 | ``` 49 | 50 | ## Testing 51 | 52 | ```bash 53 | composer test 54 | ``` 55 | 56 | ## Credits 57 | 58 | I took a lot of inspiration from existing packages like [mailgun/mailgun-php](https://github.com/mailgun/mailgun-php) 59 | or [lepikhinb/fathom-api](https://github.com/lepikhinb/fathom-api). 60 | 61 | And of course [Weaviate](https://weaviate.io/) for providing this great project. 62 | 63 | Thanks for contributing to open source! 64 | 65 | ## License 66 | 67 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timkley/weaviate-php", 3 | "version": "0.10.0", 4 | "description": "A PHP HTTP Client for the Weaviate REST API", 5 | "keywords": [ 6 | "php", 7 | "client", 8 | "weaviate", 9 | "api" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Tim Kleyersburg", 15 | "email": "timkley@gmail.com" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.2|^8.3", 20 | "guzzlehttp/guzzle": "^7.0", 21 | "illuminate/http": "^11.0|^12.0" 22 | }, 23 | "require-dev": { 24 | "pestphp/pest": "^v3.0.8", 25 | "phpstan/phpstan": "^1.8" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Weaviate\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Tests\\": "tests/" 35 | } 36 | }, 37 | "config": { 38 | "sort-packages": true, 39 | "allow-plugins": { 40 | "pestphp/pest-plugin": true 41 | } 42 | }, 43 | "minimum-stability": "dev", 44 | "prefer-stable": true, 45 | "scripts": { 46 | "phpstan": [ 47 | "./vendor/bin/phpstan --memory-limit=2048M --configuration=phpstan.neon" 48 | ], 49 | "test": [ 50 | "./vendor/bin/pest" 51 | ], 52 | "prepush": [ 53 | "@phpstan", 54 | "@test" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 8 3 | paths: 4 | - src 5 | ignoreErrors: 6 | - identifier: missingType.iterableValue 7 | - identifier: missingType.generics 8 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | 10 | ./src 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Api.php: -------------------------------------------------------------------------------- 1 | httpClient = new HttpClient(); 29 | } 30 | 31 | public function setQueryParameters(array $queryParameters): void 32 | { 33 | $this->queryParameters = $queryParameters; 34 | } 35 | 36 | public function getQueryParameters(): array 37 | { 38 | return $this->queryParameters; 39 | } 40 | 41 | /** 42 | * @throws AuthenticationException 43 | * @throws NotFoundException 44 | */ 45 | public function get(string $endpoint): Response 46 | { 47 | $this->latestResponse = $this->request()->get($endpoint); 48 | 49 | return $this->response(); 50 | } 51 | 52 | /** 53 | * @throws AuthenticationException 54 | * @throws NotFoundException 55 | */ 56 | public function post(string $endpoint, array $data = []): Response 57 | { 58 | $this->latestResponse = $this->request()->post($endpoint, $data); 59 | 60 | return $this->response(); 61 | } 62 | 63 | /** 64 | * @throws AuthenticationException 65 | * @throws NotFoundException 66 | */ 67 | public function put(string $endpoint, array $data = []): Response 68 | { 69 | $this->latestResponse = $this->request()->put($endpoint, $data); 70 | 71 | return $this->response(); 72 | } 73 | 74 | /** 75 | * @throws AuthenticationException 76 | * @throws NotFoundException 77 | */ 78 | public function patch(string $endpoint, array $data = []): Response 79 | { 80 | $this->latestResponse = $this->request()->patch($endpoint, $data); 81 | 82 | return $this->response(); 83 | } 84 | 85 | /** 86 | * @throws AuthenticationException 87 | * @throws NotFoundException 88 | */ 89 | public function delete(string $endpoint, array $data = []): Response 90 | { 91 | $this->latestResponse = $this->request()->delete($endpoint, $data); 92 | 93 | return $this->response(); 94 | } 95 | 96 | /** 97 | * @throws AuthenticationException 98 | * @throws NotFoundException 99 | */ 100 | public function head(string $endpoint): Response 101 | { 102 | $this->latestResponse = $this->request()->head($endpoint); 103 | 104 | return $this->response(); 105 | } 106 | 107 | protected function request(): PendingRequest 108 | { 109 | $httpClient = $this->httpClient 110 | ->withHeaders($this->additionalHeaders) 111 | ->withOptions(['query' => $this->queryParameters]) 112 | ->timeout($this->timeout) 113 | ->baseUrl(rtrim($this->apiUrl, '/') . '/' . $this->weaviateApiVersion); 114 | 115 | if ($this->apiToken) { 116 | $httpClient = $httpClient->withToken($this->apiToken); 117 | } 118 | 119 | return $httpClient; 120 | } 121 | 122 | /** 123 | * @throws AuthenticationException 124 | * @throws NotFoundException 125 | * @throws Exception 126 | */ 127 | protected function response(): Response 128 | { 129 | if ($this->latestResponse->status() === 401) { 130 | throw new AuthenticationException($this->latestResponse->json('message.description')); 131 | } 132 | 133 | if ($this->latestResponse->status() === 404) { 134 | throw new NotFoundException( 135 | sprintf('The requested ressource %s could not be found.', $this->latestResponse->effectiveUri()) 136 | ); 137 | } 138 | 139 | if (! $this->latestResponse->successful()) { 140 | throw new Exception($this->latestResponse->body()); 141 | } 142 | 143 | return $this->latestResponse; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Api/Batch.php: -------------------------------------------------------------------------------- 1 | api->post(self::ENDPOINT, [ 23 | 'objects' => $objects, 24 | ])->json() 25 | ); 26 | } 27 | 28 | public function delete(string $className, array $where = [], ?string $output = self::DEFAULT_OUTPUT, bool $dryRun = false): Response 29 | { 30 | if (is_null($output)) { 31 | $output = self::DEFAULT_OUTPUT; 32 | } 33 | 34 | if (! in_array($output, self::OUTPUT_METHODS)) { 35 | throw new \InvalidArgumentException('Invalid output method'); 36 | } 37 | 38 | return $this->api->delete(self::ENDPOINT, [ 39 | 'match' => [ 40 | 'class' => $className, 41 | 'where' => $where, 42 | ], 43 | 'output' => $output, 44 | 'dryRun' => $dryRun, 45 | ]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Api/DataObject.php: -------------------------------------------------------------------------------- 1 | api->get(self::ENDPOINT)->json(); 16 | 17 | return isset($response['objects']) ? ObjectCollection::fromArray($response['objects']) : new ObjectCollection([]); 18 | } 19 | 20 | public function getById(string $className, string $id): ObjectModel 21 | { 22 | return new ObjectModel( 23 | $this->api->get(self::ENDPOINT . '/' . $className . '/' . $id)->json() 24 | ); 25 | } 26 | 27 | public function create(array $data): ObjectModel 28 | { 29 | return new ObjectModel( 30 | $this->api->post(self::ENDPOINT, $data)->json() 31 | ); 32 | } 33 | 34 | public function update(string $className, string $id, array $data, bool $replace = false): bool 35 | { 36 | return (bool) $this->api->{$replace ? 'put' : 'patch'}(self::ENDPOINT . '/' . $className . '/' . $id, $data); 37 | } 38 | 39 | public function replace(string $className, string $id, array $data): bool 40 | { 41 | return $this->update($className, $id, $data, true); 42 | } 43 | 44 | public function delete(string $className, string $id): Response 45 | { 46 | return $this->api->delete(self::ENDPOINT . '/' . $className . '/' . $id); 47 | } 48 | 49 | public function exists(string $className, string $id): Response 50 | { 51 | return $this->api->head(self::ENDPOINT . '/' . $className . '/' . $id); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Api/Endpoint.php: -------------------------------------------------------------------------------- 1 | api->setQueryParameters($queryParameters); 17 | 18 | return $this; 19 | } 20 | 21 | public function request(string $url, string $method = 'get', array $data = []): Response 22 | { 23 | return $this->api->{$method}($url, $data); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Api/GraphQL.php: -------------------------------------------------------------------------------- 1 | api->post(self::ENDPOINT, [ 12 | 'query' => $query, 13 | ])->json(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Api/Meta.php: -------------------------------------------------------------------------------- 1 | api->get(self::ENDPOINT)->json() 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Api/Schema.php: -------------------------------------------------------------------------------- 1 | api->get($url)->json() 19 | ); 20 | } 21 | 22 | public function createClass(array $data): SchemaModel 23 | { 24 | return new SchemaModel( 25 | $this->api->post(self::ENDPOINT, $data)->json() 26 | ); 27 | } 28 | 29 | public function update(string $className, array $data): SchemaModel 30 | { 31 | return new SchemaModel( 32 | $this->api->put(self::ENDPOINT . '/' . $className, $data)->json() 33 | ); 34 | } 35 | 36 | public function addProperty(string $className, array $data): PropertyModel 37 | { 38 | return new PropertyModel( 39 | $this->api->post(self::ENDPOINT . '/' . $className . '/properties', $data)->json() 40 | ); 41 | } 42 | 43 | public function deleteClass(string $className): Response 44 | { 45 | return $this->api->delete(self::ENDPOINT . '/' . $className); 46 | } 47 | 48 | public function deleteAll(): Response 49 | { 50 | return $this->api->delete(self::ENDPOINT); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Collections/ClassCollection.php: -------------------------------------------------------------------------------- 1 | new ClassModel($item), $items)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Collections/ModuleCollection.php: -------------------------------------------------------------------------------- 1 | new ModuleModel([$key => $item]), array_keys($items), $items)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Collections/ObjectCollection.php: -------------------------------------------------------------------------------- 1 | new ObjectModel($item), $items)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Collections/PropertyCollection.php: -------------------------------------------------------------------------------- 1 | class = $data['class']; 18 | $this->description = $data['description'] ?? ''; 19 | $this->vectorizer = $data['vectorizer']; 20 | $this->properties = isset($data['properties']) ? PropertyCollection::fromArray($data['properties']) : null; 21 | $this->moduleConfig = $data['moduleConfig'] ?? null; 22 | } 23 | 24 | public function getClass(): string 25 | { 26 | return $this->class; 27 | } 28 | 29 | public function getDescription(): ?string 30 | { 31 | return $this->description; 32 | } 33 | 34 | public function getVectorizer(): string 35 | { 36 | return $this->vectorizer; 37 | } 38 | 39 | public function getProperties(): ?PropertyCollection 40 | { 41 | return $this->properties; 42 | } 43 | 44 | public function getModuleConfig(): ?array 45 | { 46 | return $this->moduleConfig; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Model/MetaModel.php: -------------------------------------------------------------------------------- 1 | hostname = $data['hostname']; 16 | $this->version = $data['version']; 17 | $this->modules = isset($data['modules']) ? ModuleCollection::fromArray($data['modules']) : null; 18 | } 19 | 20 | public function getHostname(): string 21 | { 22 | return $this->hostname; 23 | } 24 | 25 | public function getVersion(): string 26 | { 27 | return $this->version; 28 | } 29 | 30 | public function getModules(): ?ModuleCollection 31 | { 32 | return $this->modules; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Model/Model.php: -------------------------------------------------------------------------------- 1 | getProperties(); 14 | $array = []; 15 | foreach ($properties as $property) { 16 | $property->setAccessible(true); 17 | $value = $property->getValue($this); 18 | $array[$property->getName()] = ($value instanceof Arrayable) ? $property->getValue($this)->toArray() : $value; 19 | } 20 | 21 | return $array; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Model/ModuleModel.php: -------------------------------------------------------------------------------- 1 | key = (string) array_key_first($data); 14 | $data = $data[$this->key] ?? []; 15 | $this->name = $data['name'] ?? ''; 16 | $this->documentationHref = $data['documentationHref'] ?? ''; 17 | } 18 | 19 | public function getKey(): string 20 | { 21 | return $this->key; 22 | } 23 | 24 | public function getName(): string 25 | { 26 | return $this->name; 27 | } 28 | 29 | public function getDocumentationHref(): string 30 | { 31 | return $this->documentationHref; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Model/ObjectModel.php: -------------------------------------------------------------------------------- 1 | id = $data['id']; 19 | $this->class = $data['class']; 20 | $this->properties = isset($data['properties']) ? PropertyCollection::fromArray($data['properties']) : null; 21 | $this->vector = $data['vector'] ?? null; 22 | $this->creationTimeUnix = $data['creationTimeUnix']; 23 | $this->lastUpdateTimeUnix = $data['lastUpdateTimeUnix']; 24 | } 25 | 26 | public function getId(): string 27 | { 28 | return $this->id; 29 | } 30 | 31 | public function getClass(): string 32 | { 33 | return $this->class; 34 | } 35 | 36 | public function getProperties(): ?PropertyCollection 37 | { 38 | return $this->properties; 39 | } 40 | 41 | public function getVector(): ?array 42 | { 43 | return $this->vector; 44 | } 45 | 46 | public function getCreationTimeUnix(): int 47 | { 48 | return $this->creationTimeUnix; 49 | } 50 | 51 | public function getLastUpdateTimeUnix(): int 52 | { 53 | return $this->lastUpdateTimeUnix; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Model/PropertyModel.php: -------------------------------------------------------------------------------- 1 | name = $data['name']; 15 | $this->description = $data['description'] ?? ''; 16 | $this->dataType = $data['dataType']; 17 | $this->moduleConfig = $data['moduleConfig'] ?? null; 18 | } 19 | 20 | public function getName(): string 21 | { 22 | return $this->name; 23 | } 24 | 25 | public function getDescription(): ?string 26 | { 27 | return $this->description; 28 | } 29 | 30 | public function getDataType(): array 31 | { 32 | return $this->dataType; 33 | } 34 | 35 | public function getModuleConfig(): ?array 36 | { 37 | return $this->moduleConfig; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Model/SchemaModel.php: -------------------------------------------------------------------------------- 1 | classes = isset($data['classes']) ? ClassCollection::fromArray($data['classes']) : null; 14 | } 15 | 16 | public function getClasses(): ?ClassCollection 17 | { 18 | return $this->classes; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Weaviate.php: -------------------------------------------------------------------------------- 1 | $additionalHeaders 23 | */ 24 | public function __construct( 25 | string $apiUrl, 26 | #[\SensitiveParameter] 27 | string $apiToken, 28 | array $additionalHeaders = [], 29 | int $timeout = 30 30 | ) { 31 | $this->api = new Api($apiUrl, $apiToken, $additionalHeaders, $timeout); 32 | } 33 | 34 | public function schema(): Schema 35 | { 36 | return $this->schema ??= new Schema($this->api); 37 | } 38 | 39 | public function dataObject(): DataObject 40 | { 41 | return $this->objects ??= new DataObject($this->api); 42 | } 43 | 44 | public function batch(): Batch 45 | { 46 | return $this->batch ??= new Batch($this->api); 47 | } 48 | 49 | public function graphql(): GraphQL 50 | { 51 | return $this->graphQL ??= new GraphQL($this->api); 52 | } 53 | 54 | public function meta(): Meta 55 | { 56 | return $this->meta ??= new Meta($this->api); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Api/BatchTest.php: -------------------------------------------------------------------------------- 1 | batch()->create([]); 11 | 12 | expect($objects)->toBeInstanceOf(ObjectCollection::class); 13 | }); 14 | 15 | it('can delete objects in batch', function (?string $outputMethod) { 16 | fakeJsonResponse('objects.json'); 17 | 18 | $response = weaviate()->batch()->delete('Category', ['path'], $outputMethod); 19 | 20 | expect($response->status())->toBe(200); 21 | })->with([ 22 | 'minimal', 23 | 'verbose', 24 | null, 25 | ]); 26 | 27 | it('throws an exception when using an unsupported output method', function () { 28 | fakeJsonResponse('objects.json'); 29 | 30 | $object = weaviate()->batch()->delete('Category', [], 'invalid'); 31 | })->expectException(\InvalidArgumentException::class); 32 | -------------------------------------------------------------------------------- /tests/Api/EndpointTest.php: -------------------------------------------------------------------------------- 1 | api); 11 | 12 | $response = $endpoint->withQueryParameters(['test' => 'value', 'filter' => 'another-value'])->request('test'); 13 | 14 | expect($response->transferStats->getRequest()->getUri()->getQuery())->toBe('test=value&filter=another-value'); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/Api/MetaTest.php: -------------------------------------------------------------------------------- 1 | meta()->get(); 11 | 12 | expect($schema)->toBeInstanceOf(MetaModel::class); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/Api/ObjectTest.php: -------------------------------------------------------------------------------- 1 | dataObject()->get(); 12 | 13 | expect($objects)->toBeInstanceOf(ObjectCollection::class); 14 | expect($objects->first())->toBeInstanceOf(ObjectModel::class); 15 | }); 16 | 17 | it('can get a single object', function () { 18 | fakeJsonResponse('object.json'); 19 | 20 | $object = weaviate()->dataObject()->getById('Category', '1234'); 21 | 22 | expect($object)->toBeInstanceOf(ObjectModel::class); 23 | }); 24 | 25 | it('can create an object', function () { 26 | fakeJsonResponse('object.json'); 27 | 28 | $object = weaviate()->dataObject()->create([ 29 | 'class' => 'Category', 30 | 'properties' => [ 31 | 'name' => 'Test', 32 | ], 33 | ]); 34 | 35 | expect($object)->toBeInstanceOf(ObjectModel::class); 36 | }); 37 | 38 | it('can update an object', function () { 39 | fakeJsonResponse('object.json'); 40 | 41 | $response = weaviate()->dataObject()->update('Category', 'id', [ 42 | 'class' => 'Category', 43 | 'properties' => [ 44 | 'name' => 'Test', 45 | ], 46 | ]); 47 | 48 | expect($response)->toBeBool(); 49 | }); 50 | 51 | it('can replace an object', function () { 52 | fakeJsonResponse('object.json'); 53 | 54 | $response = weaviate()->dataObject()->replace('Category', 'id', [ 55 | 'class' => 'Category', 56 | 'properties' => [ 57 | 'name' => 'Test', 58 | ], 59 | ]); 60 | 61 | expect($response)->toBeBool(); 62 | }); 63 | 64 | it('can delete an object', function () { 65 | fakeResponse(); 66 | 67 | $response = weaviate()->dataObject()->delete('Category', 'id'); 68 | 69 | expect($response)->status()->toBe(200); 70 | }); 71 | 72 | it('can check if an object exists', function () { 73 | fakeJsonResponse('object.json'); 74 | 75 | $response = weaviate()->dataObject()->exists('Category', 'id'); 76 | 77 | expect($response->status())->toBe(200); 78 | }); 79 | -------------------------------------------------------------------------------- /tests/Api/SchemaTest.php: -------------------------------------------------------------------------------- 1 | schema()->get(); 12 | 13 | expect($schema)->toBeInstanceOf(SchemaModel::class); 14 | }); 15 | 16 | it('can get a specific schema class', function () { 17 | fakeJsonResponse('schema.json'); 18 | 19 | $schema = weaviate()->schema()->get('Category'); 20 | 21 | expect($schema)->toBeInstanceOf(SchemaModel::class); 22 | }); 23 | 24 | it('can create a schema class', function () { 25 | fakeJsonResponse('dataClass.json'); 26 | 27 | $schemaClass = weaviate()->schema()->createClass([ 28 | 'class' => 'Category', 29 | 'description' => 'A category', 30 | 'properties' => [ 31 | [ 32 | 'name' => 'name', 33 | 'dataType' => [ 34 | 'dataType' => 'string', 35 | ], 36 | ], 37 | ], 38 | ]); 39 | 40 | expect($schemaClass)->toBeInstanceOf(SchemaModel::class); 41 | }); 42 | 43 | it('can update a schema class', function () { 44 | fakeJsonResponse('dataClass.json'); 45 | 46 | $schemaClass = weaviate()->schema()->update('Category', [ 47 | 'class' => 'Category', 48 | 'description' => 'A category', 49 | ]); 50 | 51 | expect($schemaClass)->toBeInstanceOf(SchemaModel::class); 52 | }); 53 | 54 | it('can add a property', function () { 55 | fakeJsonResponse('property.json'); 56 | 57 | $schemaClass = weaviate()->schema()->addProperty('Category', [ 58 | 'name' => 'name', 59 | 'dataType' => [ 60 | 'dataType' => 'string', 61 | ], 62 | ]); 63 | 64 | expect($schemaClass)->toBeInstanceOf(PropertyModel::class); 65 | }); 66 | 67 | it('can delete a schema class', function () { 68 | fakeResponse(); 69 | $response = weaviate()->schema()->deleteClass('Category'); 70 | 71 | expect($response)->status()->toBe(200); 72 | }); 73 | 74 | it('can delete all classes', function () { 75 | fakeResponse(); 76 | $response = weaviate()->schema()->deleteAll(); 77 | 78 | expect($response)->status()->toBe(200); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/ApiTest.php: -------------------------------------------------------------------------------- 1 | api->{$method}('test'); 11 | 12 | expect($response->transferStats->getRequest()->getMethod())->toBe(strtoupper($method)); 13 | })->with([ 14 | 'get', 15 | 'post', 16 | 'put', 17 | 'patch', 18 | 'delete', 19 | ]); 20 | 21 | it('accepts query parameters', function () { 22 | weaviate()->api->setQueryParameters(['test' => 'value', 'filter' => 'another-value']); 23 | 24 | expect(weaviate()->api->getQueryParameters())->toBe(['test' => 'value', 'filter' => 'another-value']); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/Fixtures/dataClass.json: -------------------------------------------------------------------------------- 1 | { 2 | "class": "Category", 3 | "description": "Category an article is a type off", 4 | "moduleConfig": { 5 | "text2vec-contextionary": { 6 | "vectorizeClassName": false 7 | } 8 | }, 9 | "properties": [ 10 | { 11 | "dataType": [ 12 | "string" 13 | ], 14 | "description": "category name", 15 | "indexInverted": true, 16 | "moduleConfig": { 17 | "text2vec-contextionary": { 18 | "vectorizePropertyName": false 19 | } 20 | }, 21 | "name": "name" 22 | } 23 | ], 24 | "vectorIndexType": "hnsw", 25 | "vectorizer": "none" 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "hostname": "http://[::]:8080", 3 | "modules": { 4 | "generative-openai": { 5 | "documentationHref": "https://beta.openai.com/docs/api-reference/completions", 6 | "name": "Generative Search - OpenAI" 7 | }, 8 | "text2vec-openai": { 9 | "documentationHref": "https://beta.openai.com/docs/guides/embeddings/what-are-embeddings", 10 | "name": "OpenAI Module" 11 | } 12 | }, 13 | "version": "1.17.4" 14 | } 15 | -------------------------------------------------------------------------------- /tests/Fixtures/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "generative-openai": { 3 | "documentationHref": "https://beta.openai.com/docs/api-reference/completions", 4 | "name": "Generative Search - OpenAI" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/Fixtures/object.json: -------------------------------------------------------------------------------- 1 | { 2 | "class": "Category", 3 | "creationTimeUnix": 1677235243361, 4 | "id": "02c0906d-fd93-4159-a711-47ceb585925f", 5 | "lastUpdateTimeUnix": 1677235243361, 6 | "properties": { 7 | "content": "This is some content", 8 | "link": "This is a link" 9 | }, 10 | "vectorWeights": null 11 | } 12 | -------------------------------------------------------------------------------- /tests/Fixtures/objects.json: -------------------------------------------------------------------------------- 1 | { 2 | "deprecations": null, 3 | "objects": [ 4 | { 5 | "class": "Category", 6 | "creationTimeUnix": 1677235243361, 7 | "id": "02c0906d-fd93-4159-a711-47ceb585925f", 8 | "lastUpdateTimeUnix": 1677235243361, 9 | "properties": { 10 | "content": "This is some content", 11 | "link": "This is a link" 12 | }, 13 | "vectorWeights": null 14 | }, 15 | { 16 | "class": "Category", 17 | "creationTimeUnix": 1677235243361, 18 | "id": "02c0906d-fd93-4159-a711-47ceb585925f", 19 | "lastUpdateTimeUnix": 1677235243361, 20 | "properties": { 21 | "content": "This is some content", 22 | "link": "This is a link" 23 | }, 24 | "vectorWeights": null 25 | } 26 | ], 27 | "totalResults": 2 28 | } 29 | -------------------------------------------------------------------------------- /tests/Fixtures/property.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataType": [ 3 | "string" 4 | ], 5 | "description": "category name", 6 | "indexInverted": true, 7 | "moduleConfig": { 8 | "text2vec-contextionary": { 9 | "vectorizePropertyName": false 10 | } 11 | }, 12 | "name": "name" 13 | } 14 | -------------------------------------------------------------------------------- /tests/Fixtures/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes": [ 3 | { 4 | "class": "Category", 5 | "description": "Category an article is a type off", 6 | "moduleConfig": { 7 | "text2vec-contextionary": { 8 | "vectorizeClassName": false 9 | } 10 | }, 11 | "properties": [ 12 | { 13 | "dataType": [ 14 | "string" 15 | ], 16 | "description": "category name", 17 | "indexInverted": true, 18 | "moduleConfig": { 19 | "text2vec-contextionary": { 20 | "vectorizePropertyName": false 21 | } 22 | }, 23 | "name": "name" 24 | } 25 | ], 26 | "vectorIndexType": "hnsw", 27 | "vectorizer": "none" 28 | }, 29 | { 30 | "class": "Publication", 31 | "description": "A publication with an online source", 32 | "moduleConfig": { 33 | "text2vec-contextionary": { 34 | "vectorizeClassName": false 35 | } 36 | }, 37 | "properties": [ 38 | { 39 | "dataType": [ 40 | "string" 41 | ], 42 | "description": "Name of the publication", 43 | "name": "name" 44 | }, 45 | { 46 | "dataType": [ 47 | "geoCoordinates" 48 | ], 49 | "description": "Geo location of the HQ", 50 | "name": "headquartersGeoLocation" 51 | }, 52 | { 53 | "dataType": [ 54 | "Article" 55 | ], 56 | "description": "The articles this publication has", 57 | "name": "hasArticles" 58 | }, 59 | { 60 | "dataType": [ 61 | "Article" 62 | ], 63 | "description": "Articles this author wrote", 64 | "name": "wroteArticles" 65 | } 66 | ], 67 | "vectorIndexType": "hnsw", 68 | "vectorizer": "none" 69 | }, 70 | { 71 | "class": "Author", 72 | "description": "Normalised types", 73 | "moduleConfig": { 74 | "text2vec-contextionary": { 75 | "vectorizeClassName": true 76 | } 77 | }, 78 | "properties": [ 79 | { 80 | "dataType": [ 81 | "string" 82 | ], 83 | "description": "Name of the author", 84 | "name": "name" 85 | }, 86 | { 87 | "dataType": [ 88 | "Publication" 89 | ], 90 | "description": "The publication this author writes for", 91 | "name": "writesFor" 92 | } 93 | ], 94 | "vectorIndexType": "hnsw", 95 | "vectorizer": "none" 96 | }, 97 | { 98 | "class": "Article", 99 | "description": "Normalised types", 100 | "moduleConfig": { 101 | "text2vec-contextionary": { 102 | "vectorizeClassName": false 103 | } 104 | }, 105 | "properties": [ 106 | { 107 | "dataType": [ 108 | "string" 109 | ], 110 | "description": "title of the article", 111 | "indexInverted": true, 112 | "moduleConfig": { 113 | "text2vec-contextionary": { 114 | "vectorizePropertyName": false 115 | } 116 | }, 117 | "name": "title" 118 | }, 119 | { 120 | "dataType": [ 121 | "string" 122 | ], 123 | "description": "url of the article", 124 | "indexInverted": false, 125 | "moduleConfig": { 126 | "text2vec-contextionary": { 127 | "vectorizePropertyName": false 128 | } 129 | }, 130 | "name": "url" 131 | }, 132 | { 133 | "dataType": [ 134 | "text" 135 | ], 136 | "description": "summary of the article", 137 | "indexInverted": true, 138 | "moduleConfig": { 139 | "text2vec-contextionary": { 140 | "vectorizePropertyName": false 141 | } 142 | }, 143 | "name": "summary" 144 | }, 145 | { 146 | "dataType": [ 147 | "date" 148 | ], 149 | "description": "date of publication of the article", 150 | "name": "publicationDate" 151 | }, 152 | { 153 | "dataType": [ 154 | "int" 155 | ], 156 | "description": "Words in this article", 157 | "name": "wordCount" 158 | }, 159 | { 160 | "dataType": [ 161 | "Author", 162 | "Publication" 163 | ], 164 | "description": "authors this article has", 165 | "name": "hasAuthors" 166 | }, 167 | { 168 | "dataType": [ 169 | "Publication" 170 | ], 171 | "description": "publication this article is in", 172 | "name": "inPublication" 173 | }, 174 | { 175 | "dataType": [ 176 | "Category" 177 | ], 178 | "description": "category this article is of", 179 | "name": "ofCategory" 180 | }, 181 | { 182 | "dataType": [ 183 | "boolean" 184 | ], 185 | "description": "whether the article is currently accessible through the url", 186 | "name": "isAccessible" 187 | } 188 | ], 189 | "vectorIndexType": "hnsw", 190 | "vectorizer": "none" 191 | } 192 | ] 193 | } 194 | -------------------------------------------------------------------------------- /tests/Model/ClassTest.php: -------------------------------------------------------------------------------- 1 | toBeInstanceOf(ClassModel::class) 14 | ->and($dataClass->getClass())->toBe('Category') 15 | ->and($dataClass->getDescription())->toBe('Category an article is a type off') 16 | ->and($dataClass->getVectorizer())->toBe('none') 17 | ->and($dataClass->getProperties())->toBeInstanceOf(PropertyCollection::class) 18 | ->and($dataClass->getModuleConfig())->toBeArray() 19 | ; 20 | }); 21 | -------------------------------------------------------------------------------- /tests/Model/MetaTest.php: -------------------------------------------------------------------------------- 1 | getHostname())->toBe('http://[::]:8080'); 14 | expect($metaClass->getVersion())->toBe('1.17.4'); 15 | expect($metaClass->getModules())->toBeInstanceOf(ModuleCollection::class); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/Model/ModuleTest.php: -------------------------------------------------------------------------------- 1 | getKey())->toBe('generative-openai'); 13 | expect($moduleClass->getName())->toBe('Generative Search - OpenAI'); 14 | expect($moduleClass->getDocumentationHref())->toBe('https://beta.openai.com/docs/api-reference/completions'); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/Model/ObjectTest.php: -------------------------------------------------------------------------------- 1 | getId())->toBe('02c0906d-fd93-4159-a711-47ceb585925f'); 14 | expect($objectModel->getClass())->toBe('Category'); 15 | expect($objectModel->getProperties())->toBeInstanceOf(PropertyCollection::class); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/Model/SchemaTest.php: -------------------------------------------------------------------------------- 1 | getClasses())->toBeInstanceOf(ClassCollection::class); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | test; 12 | } 13 | 14 | function weaviate(): Weaviate 15 | { 16 | return this()->weaviate ??= new Weaviate('', 'test-token'); 17 | } 18 | 19 | function httpClient(): Factory 20 | { 21 | return weaviate()->api->httpClient; 22 | } 23 | 24 | function getJsonFixture(string $filename): array 25 | { 26 | return json_decode(file_get_contents(__DIR__ . sprintf('/Fixtures/%s', $filename)), true); 27 | } 28 | 29 | function fakeResponse(array $body = []): array 30 | { 31 | httpClient()->fake([ 32 | '*' => httpClient()->response($body), 33 | ]); 34 | 35 | return $body; 36 | } 37 | 38 | function fakeJsonResponse(string $filename): array 39 | { 40 | $fixture = getJsonFixture($filename); 41 | 42 | return fakeResponse($fixture); 43 | } 44 | 45 | function assertBodySent(array $fixture): void 46 | { 47 | httpClient()->assertSent(function (Request $request) use ($fixture) { 48 | return $request->body() === json_encode($fixture); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /tests/WeaviateTest.php: -------------------------------------------------------------------------------- 1 | schema())->toBeInstanceOf(Schema::class); 12 | }); 13 | 14 | it('returns the objects api', function () { 15 | expect(weaviate()->dataObject())->toBeInstanceOf(DataObject::class); 16 | }); 17 | 18 | it('returns the batch api', function () { 19 | expect(weaviate()->batch())->toBeInstanceOf(Batch::class); 20 | }); 21 | 22 | it('returns the graphql api', function () { 23 | expect(weaviate()->graphql())->toBeInstanceOf(GraphQL::class); 24 | }); 25 | --------------------------------------------------------------------------------